summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaolo Angelelli <paolo.angelelli@theqtcompany.com>2016-07-11 14:27:34 +0200
committerAlex Blasche <alexander.blasche@theqtcompany.com>2016-07-27 05:36:25 +0000
commit04762a9eecafc80ebeb90c06258de551d451497f (patch)
tree1ac1ff2d6ea9f9fb448fee41aca0f2a30a50eea1
parent5adda3e6651f3d45838ece962a4ae656eac5af04 (diff)
downloadqtlocation-04762a9eecafc80ebeb90c06258de551d451497f.tar.gz
Add indirection for osm providers
To prevent furter breakage of qtlocation osm provider in existing qt versions, this patch introduces one level of indirection in resolving OSM providers, fetching the tile server address from files hosted at http://maps-redirect.qt.io/osm/5.6/ The content of the files requested for the server address resolution must be in JSON format, containing (currently) the following fields: { "Enabled" : bool, (optional) "UrlTemplate" : "<url template>", (mandatory) "ImageFormat" : "<image format>", (mandatory) "MapCopyRight" : "<copyright>", (mandatory) "DataCopyRight" : "<copyright>", (mandatory) "MinimumZoomLevel" : <minimumZoomLevel>, (optional) "MaximumZoomLevel" : <maximumZoomLevel>, (optional) } Enabled is optional, and allows us to temporarily disable tile providers if they go offline without firing requests to them. Default is true. MinimumZoomLevel and MaximumZoomLevel are also optional, and allow us to prevent tile requests to the providers, if they do not support the specific ZL. Default is 0 and 19, respectively. <server address template> is required, and is the tile url template, with %x, %y and %z as placeholders for the actual parameters. Example: http://localhost:8080/maps/%z/%x/%y.png <image format> is required, and is the format of the tile. Example: "png" or "jpg" <MapCopyRight> is required and is the string that will be displayed in the "Map (c)" part of the on-screen copyright notice. example: "<a href='http://www.mapquest.com/'>MapQuest</a>" <DataCopyRight> is required and is the string that will be displayed in the "Data (c)" part of the on-screen copyright notice. example: "a href= 'http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors" The patch also adds four additional OSM plugin parameters, modifies an existing ones, and removes another existing one. Removed: - osm.mapping.copyright, now removed and replaced by two other parameters (see below). New: - osm.mapping.providersrepository.address, allowing to change the hardcoded http://maps-redirect.qt.io/osm/5.6/ The implication of this parameter is that it becomes possible to use file:// urls or even qrc:, allowing to ship custom providers with the applicarions - osm.mapping.providersrepository.disabled, allowing to disable the indirection and go with hardcoded URLs by default. - osm.mapping.custom.mapcopyright replaces the old osm.mapping.copyright, and contains the copyright notice to be displayed next to the "Map (c)" part of the copyright, to be consistent with the way the copyright notice coming from the provider data is handled - osm.mapping.custom.datacopyright replaces the old osm.mapping.copyright, and contains the copyright notice to be displayed next to the "Data (c)" part of the copyright, to be consistent with the way the copyright notice coming from the provider data is handled Modified: - osm.mapping.host now became osm.mapping.custom.host, improving the naming consistency. Task-number: QTBUG-54599 Change-Id: Iee88883572a198c00bcf54cf2bc33fbcc0498a68 Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
-rw-r--r--examples/location/mapviewer/mapviewer.qml2
-rw-r--r--src/location/doc/src/plugins/osm.qdoc25
-rw-r--r--src/location/maps/qgeotilefetcher.cpp9
-rw-r--r--src/location/maps/qgeotilefetcher_p.h1
-rw-r--r--src/plugins/geoservices/osm/osm.pro6
-rw-r--r--src/plugins/geoservices/osm/qgeomapreplyosm.cpp17
-rw-r--r--src/plugins/geoservices/osm/qgeomapreplyosm.h5
-rw-r--r--src/plugins/geoservices/osm/qgeotiledmaposm.cpp65
-rw-r--r--src/plugins/geoservices/osm/qgeotiledmaposm.h7
-rw-r--r--src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp150
-rw-r--r--src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.h8
-rw-r--r--src/plugins/geoservices/osm/qgeotilefetcherosm.cpp110
-rw-r--r--src/plugins/geoservices/osm/qgeotilefetcherosm.h27
-rw-r--r--src/plugins/geoservices/osm/qgeotileproviderosm.cpp318
-rw-r--r--src/plugins/geoservices/osm/qgeotileproviderosm.h256
15 files changed, 897 insertions, 109 deletions
diff --git a/examples/location/mapviewer/mapviewer.qml b/examples/location/mapviewer/mapviewer.qml
index eca51485..1b49472c 100644
--- a/examples/location/mapviewer/mapviewer.qml
+++ b/examples/location/mapviewer/mapviewer.qml
@@ -74,7 +74,7 @@ ApplicationWindow {
map = mapComponent.createObject(page);
map.plugin = plugin;
- map.zoomLevel = (map.maximumZoomLevel - map.minimumZoomLevel)/2
+ map.zoomLevel = 8
}
function getPlugins()
diff --git a/src/location/doc/src/plugins/osm.qdoc b/src/location/doc/src/plugins/osm.qdoc
index 1c924fd3..2fd9e7c4 100644
--- a/src/location/doc/src/plugins/osm.qdoc
+++ b/src/location/doc/src/plugins/osm.qdoc
@@ -60,15 +60,30 @@ a prefix.
\li User agent string set when making network requests. This parameter should be set to a
value that uniquely identifies the application.
\row
- \li osm.mapping.host
+ \li osm.mapping.custom.host
\li Url string set when making network requests to the tile server. This parameter should be set to a
valid server url with the correct osm api and the \l{Map::activeMapType} to the corresponding \l{MapType}.CustomMap.
The CustomMap will only be available if this parameter is set.
- \note Setting the mapping.host parameter to a new server renders the map tile cache useless for the old custommap style.
+ \note Setting the mapping.custom.host parameter to a new server renders the map tile cache useless for the old custommap style.
\row
- \li osm.mapping.copyright
- \li Custom copryright string is used when setting the \l{Map::activeMapType} to \l{MapType}.CustomMap via urlprefix parameter.
- This copyright will only be used when using the CustomMap from above. If empty no copyright will be displayed for the custom map.
+ \li osm.mapping.custom.mapcopyright
+ \li Custom map copryright string is used when setting the \l{Map::activeMapType} to \l{MapType}.CustomMap via urlprefix parameter.
+ This copyright will only be used when using the CustomMap from above. If empty no map copyright will be displayed for the custom map.
+\row
+ \li osm.mapping.custom.datacopyright
+ \li Custom data copryright string is used when setting the \l{Map::activeMapType} to \l{MapType}.CustomMap via urlprefix parameter.
+ This copyright will only be used when using the CustomMap from above. If empty no data copyright will be displayed for the custom map.
+\row
+ \li osm.mapping.providersrepository.address
+ \li The OpenStreetMap plugin retrieves the provider's information from a remote repository. This is done to prevent using hardcoded
+ servers by default, which may become unavailable. By default this information is fetched from \l {http://maps-redirect.qt.io} {maps-redirect.qt.io}.
+ Setting this parameter changes the provider repository address to a user-specified one, which must contain the files
+ \tt{street}, \tt{satellite}, \tt{cycle}, \tt{transit}, \tt{night-transit}, \tt{terrain} and \tt{hiking}.
+\row
+ \li osm.mapping.providersrepository.disabled
+ \li By default, the OpenStreetMap plugin retrieves the provider's information from a remote repository to avoid a loss of service due to unavailability of hardcoded services.
+ The plugin, however, still contains fallback hardcoded provider data, in case the provider repository becomes unreachable.
+ Setting this parameter to \b true makes the plugin use the hardcoded urls only and therefore prevents the plugin from fetching provider data from the remote repository.
\row
\li osm.routing.host
\li Url string set when making network requests to the routing server. This parameter should be set to a
diff --git a/src/location/maps/qgeotilefetcher.cpp b/src/location/maps/qgeotilefetcher.cpp
index 40a1a721..0e0e81ca 100644
--- a/src/location/maps/qgeotilefetcher.cpp
+++ b/src/location/maps/qgeotilefetcher.cpp
@@ -73,7 +73,7 @@ void QGeoTileFetcher::updateTileRequests(const QSet<QGeoTileSpec> &tilesAdded,
d->queue_ += tilesAdded.toList();
- if (d->enabled_ && !d->queue_.isEmpty() && !d->timer_.isActive())
+ if (d->enabled_ && initialized() && !d->queue_.isEmpty() && !d->timer_.isActive())
d->timer_.start(0, this);
}
@@ -158,7 +158,7 @@ void QGeoTileFetcher::timerEvent(QTimerEvent *event)
return;
}
- if (d->queue_.isEmpty()) {
+ if (d->queue_.isEmpty() || !initialized()) {
d->timer_.stop();
return;
}
@@ -166,6 +166,11 @@ void QGeoTileFetcher::timerEvent(QTimerEvent *event)
requestNextTile();
}
+bool QGeoTileFetcher::initialized() const
+{
+ return true;
+}
+
void QGeoTileFetcher::handleReply(QGeoTiledMapReply *reply, const QGeoTileSpec &spec)
{
Q_D(QGeoTileFetcher);
diff --git a/src/location/maps/qgeotilefetcher_p.h b/src/location/maps/qgeotilefetcher_p.h
index cabab050..cbd8b995 100644
--- a/src/location/maps/qgeotilefetcher_p.h
+++ b/src/location/maps/qgeotilefetcher_p.h
@@ -85,6 +85,7 @@ Q_SIGNALS:
protected:
void timerEvent(QTimerEvent *event);
QGeoTiledMappingManagerEngine::CacheAreas cacheHint() const;
+ virtual bool initialized() const;
private:
QGeoTileFetcherPrivate *d_ptr;
diff --git a/src/plugins/geoservices/osm/osm.pro b/src/plugins/geoservices/osm/osm.pro
index e73c16d7..56f4cb33 100644
--- a/src/plugins/geoservices/osm/osm.pro
+++ b/src/plugins/geoservices/osm/osm.pro
@@ -14,7 +14,8 @@ HEADERS += \
qplacemanagerengineosm.h \
qplacesearchreplyosm.h \
qplacecategoriesreplyosm.h \
- qgeotiledmaposm.h
+ qgeotiledmaposm.h \
+ qgeotileproviderosm.h
SOURCES += \
qgeoserviceproviderpluginosm.cpp \
@@ -28,7 +29,8 @@ SOURCES += \
qplacemanagerengineosm.cpp \
qplacesearchreplyosm.cpp \
qplacecategoriesreplyosm.cpp \
- qgeotiledmaposm.cpp
+ qgeotiledmaposm.cpp \
+ qgeotileproviderosm.cpp
OTHER_FILES += \
diff --git a/src/plugins/geoservices/osm/qgeomapreplyosm.cpp b/src/plugins/geoservices/osm/qgeomapreplyosm.cpp
index d020716e..91816950 100644
--- a/src/plugins/geoservices/osm/qgeomapreplyosm.cpp
+++ b/src/plugins/geoservices/osm/qgeomapreplyosm.cpp
@@ -35,12 +35,16 @@
#include <QtLocation/private/qgeotilespec_p.h>
-QGeoMapReplyOsm::QGeoMapReplyOsm(QNetworkReply *reply, const QGeoTileSpec &spec, QObject *parent)
+QGeoMapReplyOsm::QGeoMapReplyOsm(QNetworkReply *reply,
+ const QGeoTileSpec &spec,
+ const QString &imageFormat,
+ QObject *parent)
: QGeoTiledMapReply(spec, parent), m_reply(reply)
{
connect(m_reply, SIGNAL(finished()), this, SLOT(networkReplyFinished()));
connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(networkReplyError(QNetworkReply::NetworkError)));
+ setMapImageFormat(imageFormat);
}
QGeoMapReplyOsm::~QGeoMapReplyOsm()
@@ -69,18 +73,15 @@ void QGeoMapReplyOsm::networkReplyFinished()
if (!m_reply)
return;
- if (m_reply->error() != QNetworkReply::NoError)
+ if (m_reply->error() != QNetworkReply::NoError) {
+ m_reply->deleteLater();
+ m_reply = 0;
return;
+ }
QByteArray a = m_reply->readAll();
setMapImageData(a);
- int mapId = tileSpec().mapId();
- if (mapId == 1 || mapId == 2)
- setMapImageFormat(QStringLiteral("jpg"));
- else
- setMapImageFormat(QStringLiteral("png"));
-
setFinished(true);
m_reply->deleteLater();
diff --git a/src/plugins/geoservices/osm/qgeomapreplyosm.h b/src/plugins/geoservices/osm/qgeomapreplyosm.h
index afcae435..2b07e8b0 100644
--- a/src/plugins/geoservices/osm/qgeomapreplyosm.h
+++ b/src/plugins/geoservices/osm/qgeomapreplyosm.h
@@ -34,6 +34,9 @@
#ifndef QGEOMAPREPLYOSM_H
#define QGEOMAPREPLYOSM_H
+#include "qgeotilefetcherosm.h"
+#include "qgeotileproviderosm.h"
+
#include <QtNetwork/QNetworkReply>
#include <QtLocation/private/qgeotiledmapreply_p.h>
#include <QtCore/qpointer.h>
@@ -45,7 +48,7 @@ class QGeoMapReplyOsm : public QGeoTiledMapReply
Q_OBJECT
public:
- explicit QGeoMapReplyOsm(QNetworkReply *reply, const QGeoTileSpec &spec, QObject *parent = 0);
+ QGeoMapReplyOsm(QNetworkReply *reply, const QGeoTileSpec &spec, const QString &imageFormat, QObject *parent = 0);
~QGeoMapReplyOsm();
void abort();
diff --git a/src/plugins/geoservices/osm/qgeotiledmaposm.cpp b/src/plugins/geoservices/osm/qgeotiledmaposm.cpp
index 16f99cdd..ea6e26a8 100644
--- a/src/plugins/geoservices/osm/qgeotiledmaposm.cpp
+++ b/src/plugins/geoservices/osm/qgeotiledmaposm.cpp
@@ -33,14 +33,22 @@
#include "qgeotiledmaposm.h"
#include "qgeotiledmappingmanagerengineosm.h"
+#include "qgeotilefetcherosm.h"
#include <QtLocation/private/qgeotilespec_p.h>
QT_BEGIN_NAMESPACE
QGeoTiledMapOsm::QGeoTiledMapOsm(QGeoTiledMappingManagerEngineOsm *engine, QObject *parent)
-: QGeoTiledMap(engine, parent), m_mapId(-1), m_customCopyright(engine->customCopyright())
+: QGeoTiledMap(engine, parent), m_mapId(-1), m_engine(engine)
{
+ // Needed because evaluateCopyrights() is only triggered if visible tiles change in the map.
+ // It fails the first time it gets called if providers aren't resolved, and subsequent calls
+ // to it will be skipped until visible tiles change.
+ // This connection makes sure the copyrights are evaluated when copyright data are ready regardless
+ // of what tiles are visible.
+ connect(qobject_cast<QGeoTileFetcherOsm *>(engine->tileFetcher()), &QGeoTileFetcherOsm::providerDataUpdated,
+ this, &QGeoTiledMapOsm::onProviderDataUpdated);
}
QGeoTiledMapOsm::~QGeoTiledMapOsm()
@@ -56,32 +64,43 @@ void QGeoTiledMapOsm::evaluateCopyrights(const QSet<QGeoTileSpec> &visibleTiles)
if (tile.mapId() == m_mapId)
return;
+ int providerId = tile.mapId() - 1;
+ if (providerId < 0 || providerId >= m_engine->providers().size() || !m_engine->providers().at(providerId)->isValid())
+ return;
+
m_mapId = tile.mapId();
+ onProviderDataUpdated(m_engine->providers().at(providerId));
+}
- QString copyrights;
- switch (m_mapId) {
- case 1:
- case 2:
- // set attribution to Map Quest
- copyrights = tr("Tiles Courtesy of <a href='http://www.mapquest.com/'>MapQuest</a><br/>Data &copy; <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors");
- break;
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- // set attribution to Thunder Forest
- copyrights = tr("Maps &copy; <a href='http://www.thunderforest.com/'>Thunderforest</a><br/>Data &copy; <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors");
- break;
- case 8:
- copyrights = m_customCopyright;
- break;
- default:
- // set attribution to OSM
- copyrights = tr("&copy; <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors");
+void QGeoTiledMapOsm::onProviderDataUpdated(const QGeoTileProviderOsm *provider)
+{
+ if (!provider->isResolved() || provider->mapType().mapId() != m_mapId)
+ return;
+ QString copyRights;
+ const QString mapCopy = provider->mapCopyRight();
+ const QString dataCopy = provider->dataCopyRight();
+ const QString styleCopy = provider->styleCopyRight();
+ if (!mapCopy.isEmpty()) {
+ copyRights += QStringLiteral("Map &copy; ");
+ copyRights += mapCopy;
+ }
+ if (!dataCopy.isEmpty()) {
+ if (!copyRights.isEmpty())
+ copyRights += QStringLiteral("<br/>");
+ copyRights += QStringLiteral("Data &copy; ");
+ copyRights += dataCopy;
}
+ if (!styleCopy.isEmpty()) {
+ if (!copyRights.isEmpty())
+ copyRights += QStringLiteral("<br/>");
+ copyRights += QStringLiteral("Style &copy; ");
+ copyRights += styleCopy;
+ }
+
+ if (copyRights.isEmpty() && provider->mapType().style() == QGeoMapType::CustomMap)
+ copyRights = m_engine->customCopyright();
- emit copyrightsChanged(copyrights);
+ emit copyrightsChanged(copyRights);
}
QT_END_NAMESPACE
diff --git a/src/plugins/geoservices/osm/qgeotiledmaposm.h b/src/plugins/geoservices/osm/qgeotiledmaposm.h
index 86a52229..c77da457 100644
--- a/src/plugins/geoservices/osm/qgeotiledmaposm.h
+++ b/src/plugins/geoservices/osm/qgeotiledmaposm.h
@@ -34,6 +34,8 @@
#ifndef QGEOTILEDMAPOSM_H
#define QGEOTILEDMAPOSM_H
+#include "qgeotileproviderosm.h"
+
#include <QtLocation/private/qgeotiledmap_p.h>
QT_BEGIN_NAMESPACE
@@ -50,9 +52,12 @@ public:
protected:
void evaluateCopyrights(const QSet<QGeoTileSpec> &visibleTiles) Q_DECL_OVERRIDE;
+protected Q_SLOTS:
+ void onProviderDataUpdated(const QGeoTileProviderOsm *provider);
+
private:
int m_mapId;
- const QString m_customCopyright;
+ QGeoTiledMappingManagerEngineOsm *m_engine;
};
QT_END_NAMESPACE
diff --git a/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp
index 3671051d..92982a1f 100644
--- a/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp
+++ b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp
@@ -34,10 +34,14 @@
#include "qgeotiledmappingmanagerengineosm.h"
#include "qgeotilefetcherosm.h"
#include "qgeotiledmaposm.h"
+#include "qgeotileproviderosm.h"
#include <QtLocation/private/qgeocameracapabilities_p.h>
#include <QtLocation/private/qgeomaptype_p.h>
#include <QtLocation/private/qgeotiledmap_p.h>
+#include <QtLocation/private/qgeofiletilecache_p.h>
+
+#include <QtNetwork/QNetworkAccessManager>
QT_BEGIN_NAMESPACE
@@ -51,33 +55,142 @@ QGeoTiledMappingManagerEngineOsm::QGeoTiledMappingManagerEngineOsm(const QVarian
setTileSize(QSize(256, 256));
+ QNetworkAccessManager *nm = new QNetworkAccessManager();
+ QString domain = QStringLiteral("http://maps-redirect.qt.io/osm/5.6/");
+ if (parameters.contains(QStringLiteral("osm.mapping.providersrepository.address"))) {
+ QString customAddress = parameters.value(QStringLiteral("osm.mapping.providersrepository.address")).toString();
+ // Allowing some malformed addresses ( containing the suffix "/osm/5.6/"
+ if (customAddress.indexOf(QStringLiteral(":")) < 0) // defaulting to http:// if no prefix is found
+ customAddress = QStringLiteral("http://") + customAddress;
+ if (customAddress[customAddress.length()-1] != QLatin1Char('/'))
+ customAddress += QLatin1Char('/');
+ domain = customAddress;
+ }
+
+ m_providers.push_back(
+ new QGeoTileProviderOsm(domain + "street",
+ nm,
+ QGeoMapType(QGeoMapType::StreetMap, tr("Street Map"), tr("Street map view in daylight mode"), false, false, 1),
+ QGeoTileProviderOsm::TileProvider(QStringLiteral("http://c.tile.openstreetmap.org/%z/%x/%y.png"),
+ QStringLiteral("png"),
+ QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap.org</a>"),
+ QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors")
+ )));
+ m_providers.push_back(
+ new QGeoTileProviderOsm(domain + "satellite",
+ nm,
+ QGeoMapType(QGeoMapType::SatelliteMapDay, tr("Satellite Map"), tr("Satellite map view in daylight mode"), false, false, 2),
+ QGeoTileProviderOsm::TileProvider(QStringLiteral("http://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/%z/%y/%x"),
+ QStringLiteral("jpg"),
+ QStringLiteral("<a href='http://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer'>USGS The National Map: Orthoimagery</a>"),
+ QStringLiteral("<a href='http://landsat.gsfc.nasa.gov/?page_id=2339'>USGS/NASA Landsat</a>")
+ )));
+ m_providers.push_back(
+ new QGeoTileProviderOsm(domain + "cycle",
+ nm,
+ QGeoMapType(QGeoMapType::CycleMap, tr("Cycle Map"), tr("Cycle map view in daylight mode"), false, false, 3),
+ QGeoTileProviderOsm::TileProvider(QStringLiteral("http://c.tile.opencyclemap.org/cycle/%z/%x/%y.png"),
+ QStringLiteral("png"),
+ QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"),
+ QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors")
+ )));
+ m_providers.push_back(
+ new QGeoTileProviderOsm(domain + "transit",
+ nm,
+ QGeoMapType(QGeoMapType::TransitMap, tr("Transit Map"), tr("Public transit map view in daylight mode"), false, false, 4),
+ QGeoTileProviderOsm::TileProvider(QStringLiteral("http://c.tile2.opencyclemap.org/transport/%z/%x/%y.png"),
+ QStringLiteral("png"),
+ QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"),
+ QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors")
+ )));
+ m_providers.push_back(
+ new QGeoTileProviderOsm(domain + "night-transit",
+ nm,
+ QGeoMapType(QGeoMapType::TransitMap, tr("Night Transit Map"), tr("Public transit map view in night mode"), false, true, 5),
+ QGeoTileProviderOsm::TileProvider(QStringLiteral("http://a.tile.thunderforest.com/transport-dark/%z/%x/%y.png"),
+ QStringLiteral("png"),
+ QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"),
+ QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors")
+ )));
+ m_providers.push_back(
+ new QGeoTileProviderOsm(domain + "terrain",
+ nm,
+ QGeoMapType(QGeoMapType::TerrainMap, tr("Terrain Map"), tr("Terrain map view"), false, false, 6),
+ QGeoTileProviderOsm::TileProvider(QStringLiteral("http://a.tile.thunderforest.com/landscape/%z/%x/%y.png"),
+ QStringLiteral("png"),
+ QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"),
+ QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors")
+ )));
+ m_providers.push_back(
+ new QGeoTileProviderOsm(domain + "hiking",
+ nm,
+ QGeoMapType(QGeoMapType::PedestrianMap, tr("Hiking Map"), tr("Hiking map view"), false, false, 7),
+ QGeoTileProviderOsm::TileProvider(QStringLiteral("http://a.tile.thunderforest.com/outdoors/%z/%x/%y.png"),
+ QStringLiteral("png"),
+ QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"),
+ QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors")
+ )));
+
+ if (parameters.contains(QStringLiteral("osm.mapping.custom.host"))
+ || parameters.contains(QStringLiteral("osm.mapping.host"))) {
+ // Adding a custom provider
+ QString tmsServer;
+ if (parameters.contains(QStringLiteral("osm.mapping.host")))
+ tmsServer = parameters.value(QStringLiteral("osm.mapping.host")).toString();
+ if (parameters.contains(QStringLiteral("osm.mapping.custom.host"))) // priority to the new one
+ tmsServer = parameters.value(QStringLiteral("osm.mapping.custom.host")).toString();
+
+ QString mapCopyright;
+ QString dataCopyright;
+ if (parameters.contains(QStringLiteral("osm.mapping.custom.mapcopyright")))
+ mapCopyright = parameters.value(QStringLiteral("osm.mapping.custom.mapcopyright")).toString();
+ if (parameters.contains(QStringLiteral("osm.mapping.custom.datacopyright")))
+ dataCopyright = parameters.value(QStringLiteral("osm.mapping.custom.datacopyright")).toString();
+
+ if (parameters.contains(QStringLiteral("osm.mapping.copyright")))
+ m_customCopyright = parameters.value(QStringLiteral("osm.mapping.copyright")).toString();
+
+ m_providers.push_back(
+ new QGeoTileProviderOsm("",
+ nm,
+ QGeoMapType(QGeoMapType::CustomMap, tr("Custom URL Map"), tr("Custom url map view set via urlprefix parameter"), false, false, 8),
+ QGeoTileProviderOsm::TileProvider(tmsServer + QStringLiteral("%z/%x/%y.png"),
+ QStringLiteral("png"),
+ mapCopyright,
+ dataCopyright
+ )));
+
+ m_providers.last()->disableRedirection();
+ }
+
+ bool disableRedirection = false;
+ if (parameters.contains(QStringLiteral("osm.mapping.providersrepository.disabled")))
+ disableRedirection = parameters.value(QStringLiteral("osm.mapping.providersrepository.disabled")).toBool();
+
QList<QGeoMapType> mapTypes;
+ foreach (QGeoTileProviderOsm * provider, m_providers) {
+ provider->setParent(this);
+ if (disableRedirection)
+ provider->disableRedirection();
+ mapTypes << provider->mapType();
+ }
// See map type implementations in QGeoTiledMapOsm and QGeoTileFetcherOsm.
- mapTypes << QGeoMapType(QGeoMapType::StreetMap, tr("Street Map"), tr("Street map view in daylight mode"), false, false, 1);
- mapTypes << QGeoMapType(QGeoMapType::SatelliteMapDay, tr("Satellite Map"), tr("Satellite map view in daylight mode"), false, false, 2);
- mapTypes << QGeoMapType(QGeoMapType::CycleMap, tr("Cycle Map"), tr("Cycle map view in daylight mode"), false, false, 3);
- mapTypes << QGeoMapType(QGeoMapType::TransitMap, tr("Transit Map"), tr("Public transit map view in daylight mode"), false, false, 4);
- mapTypes << QGeoMapType(QGeoMapType::TransitMap, tr("Night Transit Map"), tr("Public transit map view in night mode"), false, true, 5);
- mapTypes << QGeoMapType(QGeoMapType::TerrainMap, tr("Terrain Map"), tr("Terrain map view"), false, false, 6);
- mapTypes << QGeoMapType(QGeoMapType::PedestrianMap, tr("Hiking Map"), tr("Hiking map view"), false, false, 7);
- if (parameters.contains(QStringLiteral("osm.mapping.host")))
- mapTypes << QGeoMapType(QGeoMapType::CustomMap, tr("Custom URL Map"), tr("Custom url map view set via urlprefix parameter"), false, false, 8);
setSupportedMapTypes(mapTypes);
- QGeoTileFetcherOsm *tileFetcher = new QGeoTileFetcherOsm(this);
+ QGeoTileFetcherOsm *tileFetcher = new QGeoTileFetcherOsm(m_providers, nm, this);
if (parameters.contains(QStringLiteral("osm.useragent"))) {
const QByteArray ua = parameters.value(QStringLiteral("osm.useragent")).toString().toLatin1();
tileFetcher->setUserAgent(ua);
}
- if (parameters.contains(QStringLiteral("osm.mapping.host"))) {
- const QString up = parameters.value(QStringLiteral("osm.mapping.host")).toString().toLatin1();
- tileFetcher->setUrlPrefix(up);
- }
- if (parameters.contains(QStringLiteral("osm.mapping.copyright")))
- m_customCopyright = parameters.value(QStringLiteral("osm.mapping.copyright")).toString().toLatin1();
+
setTileFetcher(tileFetcher);
+ QAbstractGeoTileCache *tileCache = new QGeoFileTileCache(QAbstractGeoTileCache::baseCacheDirectory() + QStringLiteral("osm"));
+ // 50mb of disk cache by default to minimize n. of accesses to public OSM servers
+ tileCache->setMaxDiskUsage(50 * 1024 * 1024);
+ setTileCache(tileCache);
+
*error = QGeoServiceProvider::NoError;
errorString->clear();
}
@@ -91,6 +204,11 @@ QGeoMap *QGeoTiledMappingManagerEngineOsm::createMap()
return new QGeoTiledMapOsm(this);
}
+const QVector<QGeoTileProviderOsm *> &QGeoTiledMappingManagerEngineOsm::providers()
+{
+ return m_providers;
+}
+
QString QGeoTiledMappingManagerEngineOsm::customCopyright() const
{
return m_customCopyright;
diff --git a/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.h b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.h
index c32dbea8..9755b0c2 100644
--- a/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.h
+++ b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.h
@@ -34,25 +34,31 @@
#ifndef QGEOTILEDMAPPINGMANAGERENGINEOSM_H
#define QGEOTILEDMAPPINGMANAGERENGINEOSM_H
-#include <QtLocation/QGeoServiceProvider>
+#include "qgeotileproviderosm.h"
+#include <QtLocation/QGeoServiceProvider>
#include <QtLocation/private/qgeotiledmappingmanagerengine_p.h>
+#include <QVector>
+
QT_BEGIN_NAMESPACE
class QGeoTiledMappingManagerEngineOsm : public QGeoTiledMappingManagerEngine
{
Q_OBJECT
+ friend class QGeoTiledMapOsm;
public:
QGeoTiledMappingManagerEngineOsm(const QVariantMap &parameters,
QGeoServiceProvider::Error *error, QString *errorString);
~QGeoTiledMappingManagerEngineOsm();
QGeoMap *createMap();
+ const QVector<QGeoTileProviderOsm *> &providers();
QString customCopyright() const;
private:
+ QVector<QGeoTileProviderOsm *> m_providers;
QString m_customCopyright;
};
diff --git a/src/plugins/geoservices/osm/qgeotilefetcherosm.cpp b/src/plugins/geoservices/osm/qgeotilefetcherosm.cpp
index 9d2a83fa..45da7d42 100644
--- a/src/plugins/geoservices/osm/qgeotilefetcherosm.cpp
+++ b/src/plugins/geoservices/osm/qgeotilefetcherosm.cpp
@@ -38,12 +38,36 @@
#include <QtNetwork/QNetworkRequest>
#include <QtLocation/private/qgeotilespec_p.h>
+
QT_BEGIN_NAMESPACE
-QGeoTileFetcherOsm::QGeoTileFetcherOsm(QObject *parent)
-: QGeoTileFetcher(parent), m_networkManager(new QNetworkAccessManager(this)),
- m_userAgent("Qt Location based application")
+static bool providersResolved(const QVector<QGeoTileProviderOsm *> &providers)
{
+ foreach (const QGeoTileProviderOsm *provider, providers)
+ if (!provider->isResolved())
+ return false;
+ return true;
+}
+
+QGeoTileFetcherOsm::QGeoTileFetcherOsm(const QVector<QGeoTileProviderOsm *> &providers,
+ QNetworkAccessManager *nm,
+ QObject *parent)
+: QGeoTileFetcher(parent), m_userAgent("Qt Location based application"),
+ m_providers(providers), m_nm(nm), m_ready(true)
+{
+ m_nm->setParent(this);
+ foreach (QGeoTileProviderOsm *provider, m_providers) {
+ if (!provider->isResolved()) {
+ m_ready = false;
+ connect(provider, &QGeoTileProviderOsm::resolutionFinished,
+ this, &QGeoTileFetcherOsm::onProviderResolutionFinished);
+ connect(provider, &QGeoTileProviderOsm::resolutionError,
+ this, &QGeoTileFetcherOsm::onProviderResolutionError);
+ provider->resolveProvider();
+ }
+ }
+ if (m_ready)
+ readyUpdated();
}
void QGeoTileFetcherOsm::setUserAgent(const QByteArray &userAgent)
@@ -51,57 +75,55 @@ void QGeoTileFetcherOsm::setUserAgent(const QByteArray &userAgent)
m_userAgent = userAgent;
}
-void QGeoTileFetcherOsm::setUrlPrefix(const QString &urlPrefix)
+bool QGeoTileFetcherOsm::initialized() const
{
- m_urlPrefix = urlPrefix;
+ if (!m_ready) {
+ foreach (QGeoTileProviderOsm *provider, m_providers)
+ if (!provider->isResolved())
+ provider->resolveProvider();
+ }
+ return m_ready;
}
-QGeoTiledMapReply *QGeoTileFetcherOsm::getTileImage(const QGeoTileSpec &spec)
+void QGeoTileFetcherOsm::onProviderResolutionFinished(const QGeoTileProviderOsm *provider)
{
- QNetworkRequest request;
- request.setRawHeader("User-Agent", m_userAgent);
+ if ((m_ready = providersResolved(m_providers))) {
+ qWarning("QGeoTileFetcherOsm: all providers resolved");
+ readyUpdated();
+ }
+ emit providerDataUpdated(provider);
+}
- QString urlPrefix;
- QString suffix = QStringLiteral(".png");
+void QGeoTileFetcherOsm::onProviderResolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error)
+{
+ Q_UNUSED(error)
+ if ((m_ready = providersResolved(m_providers))) {
+ qWarning("QGeoTileFetcherOsm: all providers resolved");
+ readyUpdated();
+ }
+ emit providerDataUpdated(provider);
+}
- switch (spec.mapId()) {
- case 1:
- urlPrefix = QStringLiteral("http://otile1.mqcdn.com/tiles/1.0.0/map/");
- suffix = QStringLiteral(".jpg");
- break;
- case 2:
- urlPrefix = QStringLiteral("http://otile1.mqcdn.com/tiles/1.0.0/sat/");
- suffix = QStringLiteral(".jpg");
- break;
- case 3:
- urlPrefix = QStringLiteral("http://a.tile.thunderforest.com/cycle/");
- break;
- case 4:
- urlPrefix = QStringLiteral("http://a.tile.thunderforest.com/transport/");
- break;
- case 5:
- urlPrefix = QStringLiteral("http://a.tile.thunderforest.com/transport-dark/");
- break;
- case 6:
- urlPrefix = QStringLiteral("http://a.tile.thunderforest.com/landscape/");
- break;
- case 7:
- urlPrefix = QStringLiteral("http://a.tile.thunderforest.com/outdoors/");
- break;
- case 8:
- urlPrefix = m_urlPrefix;
- break;
- default:
+QGeoTiledMapReply *QGeoTileFetcherOsm::getTileImage(const QGeoTileSpec &spec)
+{
+ int id = spec.mapId();
+ if (id < 1 || id > m_providers.size()) {
qWarning("Unknown map id %d\n", spec.mapId());
+ id = 0;
}
+ id -= 1; // TODO: make OSM map ids start from 0.
- request.setUrl(QUrl(urlPrefix + QString::number(spec.zoom()) + QLatin1Char('/') +
- QString::number(spec.x()) + QLatin1Char('/') +
- QString::number(spec.y()) + suffix));
-
- QNetworkReply *reply = m_networkManager->get(request);
+ const QUrl url = m_providers[id]->tileAddress(spec.x(), spec.y(), spec.zoom());
+ QNetworkRequest request;
+ request.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent);
+ request.setUrl(url);
+ QNetworkReply *reply = m_nm->get(request);
+ return new QGeoMapReplyOsm(reply, spec, m_providers[id]->format());
+}
- return new QGeoMapReplyOsm(reply, spec);
+void QGeoTileFetcherOsm::readyUpdated()
+{
+ updateTileRequests(QSet<QGeoTileSpec>(), QSet<QGeoTileSpec>());
}
QT_END_NAMESPACE
diff --git a/src/plugins/geoservices/osm/qgeotilefetcherosm.h b/src/plugins/geoservices/osm/qgeotilefetcherosm.h
index 2f61f8f7..bee5b8e3 100644
--- a/src/plugins/geoservices/osm/qgeotilefetcherosm.h
+++ b/src/plugins/geoservices/osm/qgeotilefetcherosm.h
@@ -34,31 +34,48 @@
#ifndef QGEOTILEFETCHEROSM_H
#define QGEOTILEFETCHEROSM_H
+#include "qgeotileproviderosm.h"
#include <QtLocation/private/qgeotilefetcher_p.h>
+#include <QVector>
QT_BEGIN_NAMESPACE
-class QGeoTiledMappingManagerEngine;
class QNetworkAccessManager;
class QGeoTileFetcherOsm : public QGeoTileFetcher
{
Q_OBJECT
+ friend class QGeoMapReplyOsm;
+ friend class QGeoTiledMappingManagerEngineOsm;
public:
- QGeoTileFetcherOsm(QObject *parent = 0);
+ QGeoTileFetcherOsm(const QVector<QGeoTileProviderOsm *> &providers,
+ QNetworkAccessManager *nm,
+ QObject *parent = 0);
void setUserAgent(const QByteArray &userAgent);
- void setUrlPrefix(const QString &urlPrefix);
+
+Q_SIGNALS:
+ void providerDataUpdated(const QGeoTileProviderOsm *provider);
+
+protected:
+ bool initialized() const Q_DECL_OVERRIDE;
+
+protected Q_SLOTS:
+ void onProviderResolutionFinished(const QGeoTileProviderOsm *provider);
+ void onProviderResolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error);
private:
QGeoTiledMapReply *getTileImage(const QGeoTileSpec &spec);
+ void readyUpdated();
- QNetworkAccessManager *m_networkManager;
QByteArray m_userAgent;
- QString m_urlPrefix;
+ QVector<QGeoTileProviderOsm *> m_providers;
+ QNetworkAccessManager *m_nm;
+ bool m_ready;
};
QT_END_NAMESPACE
#endif // QGEOTILEFETCHEROSM_H
+
diff --git a/src/plugins/geoservices/osm/qgeotileproviderosm.cpp b/src/plugins/geoservices/osm/qgeotileproviderosm.cpp
new file mode 100644
index 00000000..3d46a425
--- /dev/null
+++ b/src/plugins/geoservices/osm/qgeotileproviderosm.cpp
@@ -0,0 +1,318 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtLocation module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgeotileproviderosm.h"
+
+#include <QtCore/QJsonDocument>
+#include <QtCore/QJsonObject>
+#include <QDebug>
+
+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)
+{
+ if (!m_urlRedirector.isValid())
+ disableRedirection();
+}
+
+QGeoTileProviderOsm::~QGeoTileProviderOsm()
+{
+
+}
+
+void QGeoTileProviderOsm::resolveProvider()
+{
+ 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)));
+}
+
+void QGeoTileProviderOsm::disableRedirection()
+{
+ m_status = Invalid;
+ m_provider.m_valid = false;
+}
+
+void QGeoTileProviderOsm::handleError(QNetworkReply::NetworkError error)
+{
+ 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;
+ }
+}
+
+QUrl QGeoTileProviderOsm::tileAddress(int x, int y, int z) const
+{
+ if (m_provider.isValid())
+ return m_provider.tileAddress(x,y,z);
+ if (m_providerFallback.isValid())
+ return m_providerFallback.tileAddress(x,y,z);
+ return QUrl();
+}
+
+QString QGeoTileProviderOsm::mapCopyRight() const
+{
+ if (m_provider.isValid())
+ return m_provider.mapCopyRight();
+ if (m_providerFallback.isValid())
+ return m_providerFallback.mapCopyRight();
+ return QString();
+}
+
+QString QGeoTileProviderOsm::dataCopyRight() const
+{
+ if (m_provider.isValid())
+ return m_provider.dataCopyRight();
+ if (m_providerFallback.isValid())
+ return m_providerFallback.dataCopyRight();
+ return QString();
+}
+
+QString QGeoTileProviderOsm::styleCopyRight() const
+{
+ if (m_provider.isValid())
+ return m_provider.styleCopyRight();
+ if (m_providerFallback.isValid())
+ return m_providerFallback.styleCopyRight();
+ return QString();
+}
+
+QString QGeoTileProviderOsm::format() const
+{
+ if (m_provider.isValid())
+ return m_provider.format();
+ if (m_providerFallback.isValid())
+ return m_providerFallback.format();
+ return QString();
+}
+
+const QGeoMapType &QGeoTileProviderOsm::mapType() const
+{
+ return m_mapType;
+}
+
+bool QGeoTileProviderOsm::isValid() const
+{
+ return (m_provider.isValid() || m_providerFallback.isValid());
+}
+
+bool QGeoTileProviderOsm::isResolved() const
+{
+ return (m_status == Valid || m_status == Invalid);
+}
+
+void QGeoTileProviderOsm::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;
+ }
+
+ if (reply->error() != QNetworkReply::NoError) {
+ handleError(reply->error());
+ if (m_status == Invalid)
+ emit resolutionError(this, reply->error());
+ return;
+ }
+ m_status = Invalid;
+
+ /*
+ * The content of a provider information file must be in JSON format, containing
+ * (as of Qt 5.6.2) the following fields:
+ *
+ * {
+ * "Enabled" : bool, (optional)
+ * "UrlTemplate" : "<url template>", (mandatory)
+ * "ImageFormat" : "<image format>", (mandatory)
+ * "MapCopyRight" : "<copyright>", (mandatory)
+ * "DataCopyRight" : "<copyright>", (mandatory)
+ * "StyleCopyRight" : "<copyright>", (optional)
+ * "MinimumZoomLevel" : <minimumZoomLevel>, (optional)
+ * "MaximumZoomLevel" : <maximumZoomLevel>, (optional)
+ * }
+ *
+ * Enabled is optional, and allows to temporarily disable a tile provider if it becomes
+ * unavailable, without making the osm plugin fire requests to it. Default is true.
+ *
+ * MinimumZoomLevel and MaximumZoomLevel are also optional, and allow to prevent invalid tile
+ * requests to the providers, if they do not support the specific ZL. Default is 0 and 19,
+ * respectively.
+ *
+ * <server address template> is required, and is the tile url template, with %x, %y and %z as
+ * placeholders for the actual parameters.
+ * Example:
+ * http://localhost:8080/maps/%z/%x/%y.png
+ *
+ * <image format> is required, and is the format of the tile.
+ * Examples:
+ * "png", "jpg"
+ *
+ * <MapCopyRight> is required and is the string that will be displayed in the "Map (c)" part
+ * of the on-screen copyright notice. Can be an empty string.
+ * Example:
+ * "<a href='http://www.mapquest.com/'>MapQuest</a>"
+ *
+ * <DataCopyRight> is required and is the string that will be displayed in the "Data (c)" part
+ * of the on-screen copyright notice. Can be an empty string.
+ * Example:
+ * "<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors"
+ *
+ * <StyleCopyRight> is optional and is the string that will be displayed in the optional "Style (c)" part
+ * of the on-screen copyright notice.
+ */
+
+ QJsonParseError error;
+ 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();
+ const QJsonValue urlTemplate = json.value(QLatin1String("UrlTemplate"));
+ const QJsonValue imageFormat = json.value(QLatin1String("ImageFormat"));
+ const QJsonValue copyRightMap = json.value(QLatin1String("MapCopyRight"));
+ const QJsonValue copyRightData = json.value(QLatin1String("DataCopyRight"));
+ if ( urlTemplate == QJsonValue::Undefined
+ || imageFormat == QJsonValue::Undefined
+ || copyRightMap == QJsonValue::Undefined
+ || copyRightData == QJsonValue::Undefined
+ || !urlTemplate.isString()
+ || !imageFormat.isString()
+ || !copyRightMap.isString()
+ || !copyRightData.isString()) {
+ qWarning() << "QGeoTileProviderOsm: Incomplete redirection data" << "at "<<m_urlRedirector;
+ emit resolutionFinished(this);
+ return;
+ }
+
+ 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();
+
+ int minZL = 0;
+ int maxZL = 19;
+ const QJsonValue minZoom = json.value(QLatin1String("MinimumZoomLevel"));
+ if (minZoom.isDouble())
+ minZL = 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;
+
+ emit resolutionFinished(this);
+}
+
+void QGeoTileProviderOsm::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);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/geoservices/osm/qgeotileproviderosm.h b/src/plugins/geoservices/osm/qgeotileproviderosm.h
new file mode 100644
index 00000000..cdff7997
--- /dev/null
+++ b/src/plugins/geoservices/osm/qgeotileproviderosm.h
@@ -0,0 +1,256 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtLocation module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTILEPROVIDEROSM_H
+#define QTILEPROVIDEROSM_H
+
+#include <QtLocation/private/qgeomaptype_p.h>
+
+#include <QtCore/QUrl>
+#include <QtNetwork/QNetworkAccessManager>
+#include <QtNetwork/QNetworkReply>
+#include <QtCore/QPointer>
+#include <QTimer>
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+class QGeoTileProviderOsm: public QObject
+{
+ Q_OBJECT
+
+ friend class QGeoTileFetcherOsm;
+ friend class QGeoMapReplyOsm;
+ friend class QGeoTiledMappingManagerEngineOsm;
+public:
+ struct TileProvider {
+
+ static inline void sort2(int &a, int &b)
+ {
+ if (a > b) {
+ int temp=a;
+ a=b;
+ b=temp;
+ }
+ }
+
+ TileProvider() : m_valid(false)
+ {
+
+ }
+
+ TileProvider(const QString &urlTemplate,
+ const QString &format,
+ const QString &copyRightMap,
+ const QString &copyRightData,
+ int minimumZoomLevel = 0,
+ int maximumZoomLevel = 19) : m_valid(false)
+ {
+ if (urlTemplate.isEmpty())
+ return;
+ m_urlTemplate = urlTemplate;
+
+ if (format.isEmpty())
+ return;
+ m_format = format;
+
+ m_copyRightMap = copyRightMap;
+ m_copyRightData = copyRightData;
+
+ if (minimumZoomLevel < 0 || minimumZoomLevel > 30)
+ return;
+ m_minimumZoomLevel = minimumZoomLevel;
+
+ if (maximumZoomLevel < 0 || maximumZoomLevel > 30 || maximumZoomLevel < minimumZoomLevel)
+ return;
+ m_maximumZoomLevel = maximumZoomLevel;
+
+ // 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 + 4, 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_valid = true;
+ }
+
+ ~TileProvider()
+ {
+ }
+
+ inline bool isValid() const
+ {
+ return m_valid;
+ }
+
+ inline QString mapCopyRight() const
+ {
+ return m_copyRightMap;
+ }
+
+ inline QString dataCopyRight() const
+ {
+ return m_copyRightData;
+ }
+
+ inline QString styleCopyRight() const
+ {
+ return m_copyRightStyle;
+ }
+
+ inline QString format() const
+ {
+ return m_format;
+ }
+
+ // Optional properties, not needed to construct a provider
+ void setStyleCopyRight(const QString &copyright)
+ {
+ m_copyRightStyle = copyright;
+ }
+
+ QUrl 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);
+ }
+
+ bool m_valid;
+ QString m_urlTemplate;
+ QString m_format;
+ QString m_copyRightMap;
+ QString m_copyRightData;
+ QString m_copyRightStyle;
+ QString m_urlPrefix;
+ QString m_urlSuffix;
+ int m_minimumZoomLevel;
+ int m_maximumZoomLevel;
+
+ int paramsLUT[3]; //Lookup table to handle possibly shuffled x,y,z
+ QString paramsSep[2]; // what goes in between %x, %y and %z
+ };
+
+ enum Status {Idle,
+ Resolving,
+ Valid,
+ Invalid };
+
+ QGeoTileProviderOsm(const QString &urlRedir,
+ QNetworkAccessManager *nm,
+ const QGeoMapType &mapType,
+ const TileProvider &providerFallback);
+
+ ~QGeoTileProviderOsm();
+
+
+
+ QUrl tileAddress(int x, int y, int z) const;
+ QString mapCopyRight() const;
+ QString dataCopyRight() const;
+ QString styleCopyRight() const;
+ QString format() const;
+ const QGeoMapType &mapType() const;
+ bool isValid() const;
+ bool isResolved() const;
+
+Q_SIGNALS:
+ void resolutionFinished(const QGeoTileProviderOsm *provider);
+ void resolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error);
+
+public Q_SLOTS:
+ void onNetworkReplyFinished();
+ void onNetworkReplyError(QNetworkReply::NetworkError error);
+ void resolveProvider();
+
+protected:
+ void disableRedirection();
+ void handleError(QNetworkReply::NetworkError error);
+
+ QNetworkAccessManager *m_nm;
+ QUrl m_urlRedirector; // The URL from where to fetch the URL template
+ TileProvider m_provider;
+ TileProvider m_providerFallback;
+ QGeoMapType m_mapType;
+ Status m_status;
+ QTimer m_retryTimer;
+};
+
+QT_END_NAMESPACE
+
+#endif // QTILEPROVIDEROSM_H