diff options
Diffstat (limited to 'src/plugins/geoservices/osm/qgeotileproviderosm.cpp')
-rw-r--r-- | src/plugins/geoservices/osm/qgeotileproviderosm.cpp | 472 |
1 files changed, 355 insertions, 117 deletions
diff --git a/src/plugins/geoservices/osm/qgeotileproviderosm.cpp b/src/plugins/geoservices/osm/qgeotileproviderosm.cpp index 3d46a425..afa8e45f 100644 --- a/src/plugins/geoservices/osm/qgeotileproviderosm.cpp +++ b/src/plugins/geoservices/osm/qgeotileproviderosm.cpp @@ -44,150 +44,271 @@ QT_BEGIN_NAMESPACE static const int maxValidZoom = 30; -QGeoTileProviderOsm::QGeoTileProviderOsm(const QString &urlRedir, - QNetworkAccessManager *nm, - const QGeoMapType &mapType, - const QGeoTileProviderOsm::TileProvider &providerFallback) - : m_nm(nm), m_urlRedirector(urlRedir), - m_providerFallback(providerFallback), - m_mapType(mapType), m_status(Idle) +QGeoTileProviderOsm::QGeoTileProviderOsm(QNetworkAccessManager *nm, + const QGeoMapType &mapType, + const QVector<TileProvider *> &providers) +: m_nm(nm), m_provider(nullptr), m_mapType(mapType), m_status(Idle) { - if (!m_urlRedirector.isValid()) - disableRedirection(); + for (int i = 0; i < providers.size(); ++i) { + TileProvider *p = providers[i]; + if (!m_provider) + m_providerId = i; + addProvider(p); + } + + if (!m_provider || m_provider->isValid()) + m_status = Resolved; } QGeoTileProviderOsm::~QGeoTileProviderOsm() { +} + +QUrl QGeoTileProviderOsm::tileAddress(int x, int y, int z) const +{ + if (m_status != Resolved || !m_provider) + return QUrl(); + return m_provider->tileAddress(x, y, z); +} + +QString QGeoTileProviderOsm::mapCopyRight() const +{ + if (m_status != Resolved || !m_provider) + return QString(); + return m_provider->mapCopyRight(); +} + +QString QGeoTileProviderOsm::dataCopyRight() const +{ + if (m_status != Resolved || !m_provider) + return QString(); + return m_provider->dataCopyRight(); +} + +QString QGeoTileProviderOsm::styleCopyRight() const +{ + if (m_status != Resolved || !m_provider) + return QString(); + return m_provider->styleCopyRight(); +} + +QString QGeoTileProviderOsm::format() const +{ + if (m_status != Resolved || !m_provider) + return QString(); + return m_provider->format(); +} + +const QGeoMapType &QGeoTileProviderOsm::mapType() const +{ + return m_mapType; +} + +bool QGeoTileProviderOsm::isValid() const +{ + if (m_status != Resolved || !m_provider) + return false; + return m_provider->isValid(); +} +bool QGeoTileProviderOsm::isResolved() const +{ + return (m_status == Resolved); } void QGeoTileProviderOsm::resolveProvider() { - switch (m_status) { - case Resolving: - case Invalid: - case Valid: - return; - case Idle: - m_status = Resolving; - break; - } + if (m_status == Resolved || m_status == Resolving) + return; - QNetworkRequest request; - request.setHeader(QNetworkRequest::UserAgentHeader, QByteArrayLiteral("QGeoTileFetcherOsm")); - request.setUrl(m_urlRedirector); - QNetworkReply *reply = m_nm->get(request); - connect(reply, SIGNAL(finished()), this, SLOT(onNetworkReplyFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), - this, SLOT(onNetworkReplyError(QNetworkReply::NetworkError))); + m_status = Resolving; + // Provider can't be null while on Idle status. + connect(m_provider, &TileProvider::resolutionFinished, this, &QGeoTileProviderOsm::onResolutionFinished); + connect(m_provider, &TileProvider::resolutionError, this, &QGeoTileProviderOsm::onResolutionError); + m_provider->resolveProvider(); } void QGeoTileProviderOsm::disableRedirection() { - m_status = Invalid; - m_provider.m_valid = false; + if (m_provider && m_provider->isValid()) + return; + bool found = false; + for (TileProvider *p: m_providerList) { + if (p->isValid() && !found) { + m_provider = p; + found = true; + } + p->disconnect(this); + } } -void QGeoTileProviderOsm::handleError(QNetworkReply::NetworkError error) +void QGeoTileProviderOsm::onResolutionFinished(TileProvider *provider) { - switch (error) { - case QNetworkReply::ConnectionRefusedError: - case QNetworkReply::TooManyRedirectsError: - case QNetworkReply::InsecureRedirectError: - case QNetworkReply::ContentAccessDenied: - case QNetworkReply::ContentOperationNotPermittedError: - case QNetworkReply::ContentNotFoundError: - case QNetworkReply::AuthenticationRequiredError: - case QNetworkReply::ContentGoneError: - case QNetworkReply::OperationNotImplementedError: - case QNetworkReply::ServiceUnavailableError: - // Errors we don't expect to recover from in the near future, which - // prevent accessing the redirection info but not the actual providers. - m_status = Invalid; - default: - break; + Q_UNUSED(provider) + // provider and m_provider are the same, at this point. m_status is Resolving. + m_status = Resolved; + emit resolutionFinished(this); +} + +void QGeoTileProviderOsm::onResolutionError(TileProvider *provider) +{ + Q_UNUSED(provider) + // provider and m_provider are the same at this point. m_status is Resolving. + if (m_provider->isInvalid()) { + m_provider = nullptr; + m_status = Resolved; + if (m_providerId >= m_providerList.size() -1) { // no hope left + emit resolutionError(this); + return; + } + // Advance the pointer in the provider list, and possibly start resolution on the next in the list. + for (int i = m_providerId + 1; i < m_providerList.size(); ++i) { + m_providerId = i; + TileProvider *p = m_providerList[m_providerId]; + if (!p->isInvalid()) { + m_provider = p; + if (!p->isValid()) { + m_status = Idle; + //m_status = Resolving; + //p->resolveProvider(); + } + } + } + if (!m_provider) + emit resolutionError(this); + } else if (m_provider->isValid()) { + m_status = Resolved; + emit resolutionFinished(this); + } else { // still not resolved. But network error is recoverable. + m_status = Idle; + //m_provider->resolveProvider(); } } -QUrl QGeoTileProviderOsm::tileAddress(int x, int y, int z) const +void QGeoTileProviderOsm::addProvider(TileProvider *provider) { - if (m_provider.isValid()) - return m_provider.tileAddress(x,y,z); - if (m_providerFallback.isValid()) - return m_providerFallback.tileAddress(x,y,z); - return QUrl(); + if (!provider) + return; + QScopedPointer<TileProvider> p(provider); + if (provider->status() == TileProvider::Invalid) + return; // if the provider is already resolved and invalid, no point in adding it. + + provider = p.take(); + provider->setNetworkManager(m_nm); + provider->setParent(this); + m_providerList.append(provider); + if (!m_provider) + m_provider = provider; } -QString QGeoTileProviderOsm::mapCopyRight() const + +/* + QGeoTileProviderOsm::TileProvder +*/ + +static void sort2(int &a, int &b) { - if (m_provider.isValid()) - return m_provider.mapCopyRight(); - if (m_providerFallback.isValid()) - return m_providerFallback.mapCopyRight(); - return QString(); + if (a > b) { + int temp=a; + a=b; + b=temp; + } } -QString QGeoTileProviderOsm::dataCopyRight() const +TileProvider::TileProvider() : m_status(Invalid), m_nm(nullptr) { - if (m_provider.isValid()) - return m_provider.dataCopyRight(); - if (m_providerFallback.isValid()) - return m_providerFallback.dataCopyRight(); - return QString(); + } -QString QGeoTileProviderOsm::styleCopyRight() const +TileProvider::TileProvider(const QUrl &urlRedirector) : m_status(Idle), m_urlRedirector(urlRedirector), m_nm(nullptr) { - if (m_provider.isValid()) - return m_provider.styleCopyRight(); - if (m_providerFallback.isValid()) - return m_providerFallback.styleCopyRight(); - return QString(); + if (!m_urlRedirector.isValid()) + m_status = Invalid; } -QString QGeoTileProviderOsm::format() const +TileProvider::TileProvider(const QString &urlTemplate, + const QString &format, + const QString ©RightMap, + const QString ©RightData, + int minimumZoomLevel, + int maximumZoomLevel) +: m_status(Invalid), m_nm(nullptr), m_urlTemplate(urlTemplate), + m_format(format), m_copyRightMap(copyRightMap), m_copyRightData(copyRightData), + m_minimumZoomLevel(minimumZoomLevel), m_maximumZoomLevel(maximumZoomLevel) { - if (m_provider.isValid()) - return m_provider.format(); - if (m_providerFallback.isValid()) - return m_providerFallback.format(); - return QString(); + setupProvider(); } -const QGeoMapType &QGeoTileProviderOsm::mapType() const +TileProvider::~TileProvider() { - return m_mapType; } -bool QGeoTileProviderOsm::isValid() const +void TileProvider::resolveProvider() { - return (m_provider.isValid() || m_providerFallback.isValid()); + if (!m_nm) + return; + + switch (m_status) { + case Resolving: + case Invalid: + case Valid: + return; + case Idle: + m_status = Resolving; + break; + } + + QNetworkRequest request; + request.setHeader(QNetworkRequest::UserAgentHeader, QByteArrayLiteral("QGeoTileFetcherOsm")); + request.setUrl(m_urlRedirector); + QNetworkReply *reply = m_nm->get(request); + connect(reply, SIGNAL(finished()), this, SLOT(onNetworkReplyFinished()) ); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onNetworkReplyError(QNetworkReply::NetworkError))); } -bool QGeoTileProviderOsm::isResolved() const +void TileProvider::handleError(QNetworkReply::NetworkError error) { - return (m_status == Valid || m_status == Invalid); + switch (error) { + case QNetworkReply::ConnectionRefusedError: + case QNetworkReply::TooManyRedirectsError: + case QNetworkReply::InsecureRedirectError: + case QNetworkReply::ContentAccessDenied: + case QNetworkReply::ContentOperationNotPermittedError: + case QNetworkReply::ContentNotFoundError: + case QNetworkReply::AuthenticationRequiredError: + case QNetworkReply::ContentGoneError: + case QNetworkReply::OperationNotImplementedError: + case QNetworkReply::ServiceUnavailableError: + // Errors we don't expect to recover from in the near future, which + // prevent accessing the redirection info but not the actual providers. + qWarning() << "QGeoTileProviderOsm network error:" << error; + m_status = Invalid; + default: + break; + } } -void QGeoTileProviderOsm::onNetworkReplyFinished() +void TileProvider::onNetworkReplyFinished() { QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); switch (m_status) { - case Resolving: - m_status = Idle; - case Idle: // should not happen - case Invalid: // should not happen - break; - case Valid: // should not happen - return; + case Resolving: + m_status = Idle; + case Idle: // should not happen + case Invalid: // should not happen + break; + case Valid: // should not happen + emit resolutionFinished(this); + return; } + QObject errorEmitter; + QMetaObject::Connection errorEmitterConnection = connect(&errorEmitter, &QObject::destroyed, [this](){ this->resolutionError(this); }); + if (reply->error() != QNetworkReply::NoError) { handleError(reply->error()); - if (m_status == Invalid) - emit resolutionError(this, reply->error()); return; } m_status = Invalid; @@ -241,12 +362,10 @@ void QGeoTileProviderOsm::onNetworkReplyFinished() QJsonDocument d = QJsonDocument::fromJson(reply->readAll(), &error); if (error.error != QJsonParseError::NoError) { qWarning() << "QGeoTileProviderOsm: Error parsing redirection data: "<<error.errorString() << "at "<<m_urlRedirector; - emit resolutionFinished(this); return; } if (!d.isObject()) { qWarning() << "QGeoTileProviderOsm: Invalid redirection data" << "at "<<m_urlRedirector; - emit resolutionFinished(this); return; } const QJsonObject json = d.object(); @@ -263,56 +382,175 @@ void QGeoTileProviderOsm::onNetworkReplyFinished() || !copyRightMap.isString() || !copyRightData.isString()) { qWarning() << "QGeoTileProviderOsm: Incomplete redirection data" << "at "<<m_urlRedirector; - emit resolutionFinished(this); return; } + m_urlTemplate = urlTemplate.toString(); + m_format = imageFormat.toString(); + m_copyRightMap = copyRightMap.toString(); + m_copyRightData = copyRightData.toString(); + const QJsonValue enabled = json.value(QLatin1String("Enabled")); if (enabled.isBool() && ! enabled.toBool()) { qWarning() << "QGeoTileProviderOsm: Tileserver disabled" << "at "<<m_urlRedirector; - emit resolutionFinished(this); return; } - QString styleCopyRight; const QJsonValue copyRightStyle = json.value(QLatin1String("StyleCopyRight")); if (copyRightStyle != QJsonValue::Undefined && copyRightStyle.isString()) - styleCopyRight = copyRightStyle.toString(); + m_copyRightStyle = copyRightStyle.toString(); - int minZL = 0; - int maxZL = 19; + m_minimumZoomLevel = 0; + m_maximumZoomLevel = 19; const QJsonValue minZoom = json.value(QLatin1String("MinimumZoomLevel")); if (minZoom.isDouble()) - minZL = qBound(0, int(minZoom.toDouble()), maxValidZoom); + m_minimumZoomLevel = qBound(0, int(minZoom.toDouble()), maxValidZoom); const QJsonValue maxZoom = json.value(QLatin1String("MaximumZoomLevel")); if (maxZoom.isDouble()) - maxZL = qBound(0, int(maxZoom.toDouble()), maxValidZoom); - - m_provider = TileProvider(urlTemplate.toString(), - imageFormat.toString(), - copyRightMap.toString(), - copyRightData.toString(), - minZL, - maxZL); - m_provider.setStyleCopyRight(styleCopyRight); - - if (m_provider.isValid()) - m_status = Valid; + m_maximumZoomLevel = qBound(0, int(maxZoom.toDouble()), maxValidZoom); - emit resolutionFinished(this); + setupProvider(); + if (isValid()) { + QObject::disconnect(errorEmitterConnection); + emit resolutionFinished(this); + } } -void QGeoTileProviderOsm::onNetworkReplyError(QNetworkReply::NetworkError error) +void TileProvider::onNetworkReplyError(QNetworkReply::NetworkError error) { if (m_status == Resolving) m_status = Idle; - qWarning() << "QGeoTileProviderOsm::onNetworkReplyError " << error; handleError(error); - static_cast<QNetworkReply *>(sender())->deleteLater(); - if (m_status == Invalid) - emit resolutionError(this, error); + emit resolutionError(this); +} + +void TileProvider::setupProvider() +{ + if (m_urlTemplate.isEmpty()) + return; + + if (m_format.isEmpty()) + return; + + if (m_minimumZoomLevel < 0 || m_minimumZoomLevel > 30) + return; + + if (m_maximumZoomLevel < 0 || m_maximumZoomLevel > 30 || m_maximumZoomLevel < m_minimumZoomLevel) + return; + + // Currently supporting only %x, %y and &z + int offset[3]; + offset[0] = m_urlTemplate.indexOf(QLatin1String("%x")); + if (offset[0] < 0) + return; + + offset[1] = m_urlTemplate.indexOf(QLatin1String("%y")); + if (offset[1] < 0) + return; + + offset[2] = m_urlTemplate.indexOf(QLatin1String("%z")); + if (offset[2] < 0) + return; + + int sortedOffsets[3]; + std::copy(offset, offset + 3, sortedOffsets); + sort2(sortedOffsets[0] ,sortedOffsets[1]); + sort2(sortedOffsets[1] ,sortedOffsets[2]); + sort2(sortedOffsets[0] ,sortedOffsets[1]); + + int min = sortedOffsets[0]; + int max = sortedOffsets[2]; + int mid = sortedOffsets[1]; + + // Initing LUT + for (int i=0; i<3; i++) { + if (offset[0] == sortedOffsets[i]) + paramsLUT[i] = 0; + else if (offset[1] == sortedOffsets[i]) + paramsLUT[i] = 1; + else + paramsLUT[i] = 2; + } + + m_urlPrefix = m_urlTemplate.mid(0 , min); + m_urlSuffix = m_urlTemplate.mid(max + 2, m_urlTemplate.size() - max - 2); + + paramsSep[0] = m_urlTemplate.mid(min + 2, mid - min - 2); + paramsSep[1] = m_urlTemplate.mid(mid + 2, max - mid - 2); + m_status = Valid; } +bool TileProvider::isValid() const +{ + return m_status == Valid; +} + +bool TileProvider::isInvalid() const +{ + return m_status == Invalid; +} + +bool TileProvider::isResolved() const +{ + return (m_status == Valid || m_status == Invalid); +} + +QString TileProvider::mapCopyRight() const +{ + return m_copyRightMap; +} + +QString TileProvider::dataCopyRight() const +{ + return m_copyRightData; +} + +QString TileProvider::styleCopyRight() const +{ + return m_copyRightStyle; +} + +QString TileProvider::format() const +{ + return m_format; +} + +void TileProvider::setStyleCopyRight(const QString ©right) +{ + m_copyRightStyle = copyright; +} + +QUrl TileProvider::tileAddress(int x, int y, int z) const +{ + if (z < m_minimumZoomLevel || z > m_maximumZoomLevel) + return QUrl(); + int params[3] = { x, y, z}; + QString url; + url += m_urlPrefix; + url += QString::number(params[paramsLUT[0]]); + url += paramsSep[0]; + url += QString::number(params[paramsLUT[1]]); + url += paramsSep[1]; + url += QString::number(params[paramsLUT[2]]); + url += m_urlSuffix; + return QUrl(url); +} + +void TileProvider::setNetworkManager(QNetworkAccessManager *nm) +{ + m_nm = nm; +} + +TileProvider::Status TileProvider::status() const +{ + return m_status; +} + + QT_END_NAMESPACE + + + + |