summaryrefslogtreecommitdiff
path: root/src/plugins/geoservices/osm
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/geoservices/osm')
-rw-r--r--src/plugins/geoservices/osm/osm.pro4
-rw-r--r--src/plugins/geoservices/osm/providers/5.8/cycle8
-rw-r--r--src/plugins/geoservices/osm/providers/5.8/hiking9
-rw-r--r--src/plugins/geoservices/osm/providers/5.8/night-transit9
-rw-r--r--src/plugins/geoservices/osm/providers/5.8/satellite10
-rw-r--r--src/plugins/geoservices/osm/providers/5.8/street10
-rw-r--r--src/plugins/geoservices/osm/providers/5.8/street-hires9
-rw-r--r--src/plugins/geoservices/osm/providers/5.8/terrain9
-rw-r--r--src/plugins/geoservices/osm/providers/5.8/transit9
-rw-r--r--src/plugins/geoservices/osm/qgeofiletilecacheosm.cpp360
-rw-r--r--src/plugins/geoservices/osm/qgeofiletilecacheosm.h91
-rw-r--r--src/plugins/geoservices/osm/qgeotiledmaposm.cpp5
-rw-r--r--src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp255
-rw-r--r--src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.h4
-rw-r--r--src/plugins/geoservices/osm/qgeotilefetcherosm.cpp39
-rw-r--r--src/plugins/geoservices/osm/qgeotilefetcherosm.h7
-rw-r--r--src/plugins/geoservices/osm/qgeotileproviderosm.cpp550
-rw-r--r--src/plugins/geoservices/osm/qgeotileproviderosm.h261
18 files changed, 1283 insertions, 366 deletions
diff --git a/src/plugins/geoservices/osm/osm.pro b/src/plugins/geoservices/osm/osm.pro
index 56f4cb33..86a62744 100644
--- a/src/plugins/geoservices/osm/osm.pro
+++ b/src/plugins/geoservices/osm/osm.pro
@@ -1,6 +1,6 @@
TARGET = qtgeoservices_osm
-QT += location-private positioning-private network
+QT += location-private positioning-private network concurrent
HEADERS += \
qgeoserviceproviderpluginosm.h \
@@ -15,6 +15,7 @@ HEADERS += \
qplacesearchreplyosm.h \
qplacecategoriesreplyosm.h \
qgeotiledmaposm.h \
+ qgeofiletilecacheosm.h \
qgeotileproviderosm.h
SOURCES += \
@@ -30,6 +31,7 @@ SOURCES += \
qplacesearchreplyosm.cpp \
qplacecategoriesreplyosm.cpp \
qgeotiledmaposm.cpp \
+ qgeofiletilecacheosm.cpp \
qgeotileproviderosm.cpp
diff --git a/src/plugins/geoservices/osm/providers/5.8/cycle b/src/plugins/geoservices/osm/providers/5.8/cycle
new file mode 100644
index 00000000..5e37aab2
--- /dev/null
+++ b/src/plugins/geoservices/osm/providers/5.8/cycle
@@ -0,0 +1,8 @@
+{
+ "UrlTemplate" : "http://a.tile.thunderforest.com/cycle/%z/%x/%y.png",
+ "ImageFormat" : "png",
+ "QImageFormat" : "Indexed8",
+ "ID" : "thf-cycle",
+ "MapCopyRight" : "<a href='http://www.thunderforest.com/'>Thunderforest</a>",
+ "DataCopyRight" : "<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors"
+}
diff --git a/src/plugins/geoservices/osm/providers/5.8/hiking b/src/plugins/geoservices/osm/providers/5.8/hiking
new file mode 100644
index 00000000..1bb182e4
--- /dev/null
+++ b/src/plugins/geoservices/osm/providers/5.8/hiking
@@ -0,0 +1,9 @@
+{
+ "UrlTemplate" : "http://b.tiles.wmflabs.org/hikebike/%z/%x/%y.png",
+ "ImageFormat" : "png",
+ "QImageFormat" : "Indexed8",
+ "ID" : "wmf-hike",
+ "MaximumZoomLevel" : 18,
+ "MapCopyRight" : "<a href='https://wikimediafoundation.org/wiki/Terms_of_Use'>WikiMedia Foundation</a>",
+ "DataCopyRight" : "<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors"
+}
diff --git a/src/plugins/geoservices/osm/providers/5.8/night-transit b/src/plugins/geoservices/osm/providers/5.8/night-transit
new file mode 100644
index 00000000..988a096a
--- /dev/null
+++ b/src/plugins/geoservices/osm/providers/5.8/night-transit
@@ -0,0 +1,9 @@
+{
+ "UrlTemplate" : "http://a.tile.thunderforest.com/transport-dark/%z/%x/%y.png",
+ "ImageFormat" : "png",
+ "QImageFormat" : "Indexed8",
+ "ID" : "thf-nighttransit",
+ "MaximumZoomLevel" : 19,
+ "MapCopyRight" : "<a href='http://www.thunderforest.com/'>Thunderforest</a>",
+ "DataCopyRight" : "<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors"
+}
diff --git a/src/plugins/geoservices/osm/providers/5.8/satellite b/src/plugins/geoservices/osm/providers/5.8/satellite
new file mode 100644
index 00000000..5c48a077
--- /dev/null
+++ b/src/plugins/geoservices/osm/providers/5.8/satellite
@@ -0,0 +1,10 @@
+{
+ "Enabled" : false,
+ "UrlTemplate" : "http://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/%z/%y/%x",
+ "ImageFormat" : "jpg",
+ "QImageFormat" : "RGB888",
+ "ID" : "usgs-l7",
+ "MaximumZoomLevel" : 8,
+ "MapCopyRight" : "<a href='http://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer'>USGS The National Map: Orthoimagery</a>",
+ "DataCopyRight" : "<a href='http://landsat.gsfc.nasa.gov/?page_id=2339'>USGS/NASA Landsat</a>"
+}
diff --git a/src/plugins/geoservices/osm/providers/5.8/street b/src/plugins/geoservices/osm/providers/5.8/street
new file mode 100644
index 00000000..b3bccf1d
--- /dev/null
+++ b/src/plugins/geoservices/osm/providers/5.8/street
@@ -0,0 +1,10 @@
+{
+ "UrlTemplate" : "http://korona.geog.uni-heidelberg.de/tiles/roads/x=%x&y=%y&z=%z",
+ "ImageFormat" : "jpg",
+ "QImageFormat" : "Indexed8",
+ "ID" : "oms-street",
+ "MaximumZoomLevel" : 20,
+ "MapCopyRight" : "<a href='http://giscience.uni-hd.de/'>GIScience Research Group @ University of Heidelberg</a>",
+ "StyleCopyRight" : "<a href='http://www.geog.uni-heidelberg.de/personen/gis_rylov.html'>Maxim Rylov</a>",
+ "DataCopyRight" : "<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors"
+}
diff --git a/src/plugins/geoservices/osm/providers/5.8/street-hires b/src/plugins/geoservices/osm/providers/5.8/street-hires
new file mode 100644
index 00000000..9819f619
--- /dev/null
+++ b/src/plugins/geoservices/osm/providers/5.8/street-hires
@@ -0,0 +1,9 @@
+{
+ "UrlTemplate" : "https://maps.wikimedia.org/osm-intl/%z/%x/%y@2x.png",
+ "ImageFormat" : "png",
+ "QImageFormat" : "Indexed8",
+ "MaximumZoomLevel" : 18,
+ "ID" : "wmf-intl-2x",
+ "MapCopyRight" : "<a href='https://wikimediafoundation.org/wiki/Terms_of_Use'>WikiMedia Foundation</a>",
+ "DataCopyRight" : "<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors"
+}
diff --git a/src/plugins/geoservices/osm/providers/5.8/terrain b/src/plugins/geoservices/osm/providers/5.8/terrain
new file mode 100644
index 00000000..7fc6636c
--- /dev/null
+++ b/src/plugins/geoservices/osm/providers/5.8/terrain
@@ -0,0 +1,9 @@
+{
+ "UrlTemplate" : "http://a.tile.thunderforest.com/landscape/%z/%x/%y.png",
+ "ImageFormat" : "png",
+ "QImageFormat" : "Indexed8",
+ "ID" : "thf-landsc",
+ "MaximumZoomLevel" : 19,
+ "MapCopyRight" : "<a href='http://www.thunderforest.com/'>Thunderforest</a>",
+ "DataCopyRight" : "<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors"
+}
diff --git a/src/plugins/geoservices/osm/providers/5.8/transit b/src/plugins/geoservices/osm/providers/5.8/transit
new file mode 100644
index 00000000..ebf87b05
--- /dev/null
+++ b/src/plugins/geoservices/osm/providers/5.8/transit
@@ -0,0 +1,9 @@
+{
+ "UrlTemplate" : "http://a.tile.thunderforest.com/transport/%z/%x/%y.png",
+ "ImageFormat" : "png",
+ "QImageFormat" : "Indexed8",
+ "ID" : "thf-transit",
+ "MaximumZoomLevel" : 19,
+ "MapCopyRight" : "<a href='http://www.thunderforest.com/'>Thunderforest</a>",
+ "DataCopyRight" : "<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors"
+}
diff --git a/src/plugins/geoservices/osm/qgeofiletilecacheosm.cpp b/src/plugins/geoservices/osm/qgeofiletilecacheosm.cpp
new file mode 100644
index 00000000..a563cced
--- /dev/null
+++ b/src/plugins/geoservices/osm/qgeofiletilecacheosm.cpp
@@ -0,0 +1,360 @@
+/****************************************************************************
+**
+** 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 "qgeofiletilecacheosm.h"
+#include <QtLocation/private/qgeotilespec_p.h>
+#include <QDir>
+#include <QDirIterator>
+#include <QPair>
+#include <QDateTime>
+#include <QtConcurrent>
+#include <QThread>
+
+QT_BEGIN_NAMESPACE
+
+QGeoFileTileCacheOsm::QGeoFileTileCacheOsm(const QVector<QGeoTileProviderOsm *> &providers,
+ const QString &offlineDirectory,
+ const QString &directory,
+ QObject *parent)
+: QGeoFileTileCache(directory, parent), m_offlineDirectory(offlineDirectory), m_requestCancel(0), m_providers(providers)
+{
+ m_highDpi.resize(providers.size());
+ for (int i = 0; i < providers.size(); i++) {
+ m_highDpi[i] = providers[i]->isHighDpi();
+ m_mapIdFutures[providers[i]->mapType().mapId()].isFinished(); // To construct a default future for this mapId
+ connect(providers[i], &QGeoTileProviderOsm::resolutionFinished, this, &QGeoFileTileCacheOsm::onProviderResolutionFinished);
+ connect(providers[i], &QGeoTileProviderOsm::resolutionError, this, &QGeoFileTileCacheOsm::onProviderResolutionFinished);
+ }
+}
+
+QGeoFileTileCacheOsm::~QGeoFileTileCacheOsm()
+{
+ m_requestCancel = 1;
+ m_future.waitForFinished();
+ for (const QGeoTileProviderOsm *p : m_providers)
+ m_mapIdFutures[p->mapType().mapId()].waitForFinished();
+}
+
+QSharedPointer<QGeoTileTexture> QGeoFileTileCacheOsm::get(const QGeoTileSpec &spec)
+{
+ QSharedPointer<QGeoTileTexture> tt = getFromMemory(spec);
+ if (tt)
+ return tt;
+ if ((tt = getFromOfflineStorage(spec)))
+ return tt;
+ return getFromDisk(spec);
+}
+
+void QGeoFileTileCacheOsm::onProviderResolutionFinished(const QGeoTileProviderOsm *provider)
+{
+ clearObsoleteTiles(provider);
+ Q_UNUSED(provider)
+ for (int i = 0; i < m_providers.size(); i++) {
+ if (m_providers[i]->isHighDpi() != m_highDpi[i]) { // e.g., HiDpi was requested but only LoDpi is available
+ int mapId = m_providers[i]->mapType().mapId();
+ m_highDpi[i] = m_providers[i]->isHighDpi();
+
+ // reload cache for mapId i
+ dropTiles(mapId);
+ loadTiles(mapId);
+
+ // reload offline registry for mapId i
+ m_mapIdFutures[mapId] = QtConcurrent::run(this, &QGeoFileTileCacheOsm::initOfflineRegistry, mapId);
+
+ // send signal to clear scene in all maps created through this provider that use the reloaded tiles
+ emit mapDataUpdated(mapId);
+ }
+ }
+}
+
+// On resolution error the provider is removed ONLY if there is no enabled hardcoded fallback.
+// Hardcoded fallbacks also have a timestamp, that can get updated with Qt releases.
+void QGeoFileTileCacheOsm::onProviderResolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error)
+{
+ Q_UNUSED(error)
+ clearObsoleteTiles(provider); // this still removes tiles who happen to be older than qgeotileproviderosm.cpp defaultTs
+}
+
+void QGeoFileTileCacheOsm::init()
+{
+ if (directory_.isEmpty())
+ directory_ = baseLocationCacheDirectory();
+ QDir::root().mkpath(directory_);
+
+ // find max mapId
+ int max = 0;
+ for (auto p: m_providers)
+ if (p->mapType().mapId() > max)
+ max = p->mapType().mapId();
+ // Create a mapId to maxTimestamp LUT..
+ m_maxMapIdTimestamps.resize(max+1); // initializes to invalid QDateTime
+
+ // .. by finding the newest file in each tileset (tileset = mapId).
+ QDir dir(directory_);
+ QStringList formats;
+ formats << QLatin1String("*.*");
+ QStringList files = dir.entryList(formats, QDir::Files);
+
+ for (const QString &tileFileName : files) {
+ QGeoTileSpec spec = filenameToTileSpec(tileFileName);
+ if (spec.zoom() == -1)
+ continue;
+ QFileInfo fi(dir.filePath(tileFileName));
+ if (fi.lastModified() > m_maxMapIdTimestamps[spec.mapId()])
+ m_maxMapIdTimestamps[spec.mapId()] = fi.lastModified();
+ }
+
+ // Base class ::init()
+ QGeoFileTileCache::init();
+
+ for (QGeoTileProviderOsm * p: m_providers)
+ clearObsoleteTiles(p);
+
+ if (!m_offlineDirectory.isEmpty())
+ m_future = QtConcurrent::run(this, &QGeoFileTileCacheOsm::initOfflineRegistry, -1);
+}
+
+QSharedPointer<QGeoTileTexture> QGeoFileTileCacheOsm::getFromOfflineStorage(const QGeoTileSpec &spec)
+{
+ QMutexLocker locker(&storageLock);
+ if (m_tilespecToOfflineFilepath.contains(spec)) {
+ QFile file(m_tilespecToOfflineFilepath[spec]);
+ file.open(QIODevice::ReadOnly);
+ QByteArray bytes = file.readAll();
+ file.close();
+ locker.unlock();
+
+ QImage image;
+ if (!image.loadFromData(bytes)) {
+ handleError(spec, QLatin1String("Problem with tile image"));
+ return QSharedPointer<QGeoTileTexture>(0);
+ }
+
+ addToMemoryCache(spec, bytes, QString());
+ QSharedPointer<QGeoTileTexture> tt = addToTextureCache(spec, image);
+ if (tt)
+ return tt;
+ }
+
+ return QSharedPointer<QGeoTileTexture>();
+}
+
+void QGeoFileTileCacheOsm::dropTiles(int mapId)
+{
+ QList<QGeoTileSpec> keys;
+ keys = textureCache_.keys();
+ for (const QGeoTileSpec &k : keys)
+ if (k.mapId() == mapId)
+ textureCache_.remove(k);
+
+ keys = memoryCache_.keys();
+ for (const QGeoTileSpec &k : keys)
+ if (k.mapId() == mapId)
+ memoryCache_.remove(k);
+
+ keys = diskCache_.keys();
+ for (const QGeoTileSpec &k : keys)
+ if (k.mapId() == mapId)
+ diskCache_.remove(k);
+}
+
+void QGeoFileTileCacheOsm::loadTiles(int mapId)
+{
+ QStringList formats;
+ formats << QLatin1String("*.*");
+
+ QDir dir(directory_);
+ QStringList files = dir.entryList(formats, QDir::Files);
+
+ for (int i = 0; i < files.size(); ++i) {
+ QGeoTileSpec spec = filenameToTileSpec(files.at(i));
+ if (spec.zoom() == -1 || spec.mapId() != mapId)
+ continue;
+ QString filename = dir.filePath(files.at(i));
+ addToDiskCache(spec, filename);
+ }
+}
+
+void QGeoFileTileCacheOsm::initOfflineRegistry(int mapId)
+{
+ // Dealing with duplicates: picking the newest
+ QMap<QString, QPair<QString, QDateTime> > fileDates; // key is filename, value is <filepath, lastmodified>
+ QDirIterator it(m_offlineDirectory, QStringList() << "*.*", QDir::Files, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks );
+ while (it.hasNext()) {
+ const QString &path = it.next();
+ QFileInfo f(path);
+ if (!fileDates.contains(f.fileName()) || fileDates[f.fileName()].second < f.lastModified())
+ fileDates[f.fileName()] = QPair<QString, QDateTime>(f.filePath(), f.lastModified());
+ if (m_requestCancel)
+ return;
+ }
+
+ // Clear the content of the index. Entirely (at startup), or selectively (when a provider resolution changes the highDpi status).
+ if (mapId < 0) {
+ storageLock.lock();
+ m_tilespecToOfflineFilepath.clear();
+ storageLock.unlock();
+ } else {
+ QList<QGeoTileSpec> toRemove;
+ for (auto i = m_tilespecToOfflineFilepath.constBegin(); i != m_tilespecToOfflineFilepath.constEnd(); ++i) {
+ if (i.key().mapId() == mapId)
+ toRemove.append(i.key());
+ }
+ storageLock.lock();
+ for (const auto &i : toRemove)
+ m_tilespecToOfflineFilepath.remove(i);
+ storageLock.unlock();
+ }
+ if (m_requestCancel)
+ return;
+
+ // Fill the index entirely or selectively
+ int count = 0;
+ for (auto i= fileDates.constBegin(); i != fileDates.constEnd(); ++i) {
+ QGeoTileSpec spec = filenameToTileSpec(i.key());
+ if (spec.zoom() == -1)
+ continue;
+ if (mapId >= 0 && spec.mapId() != mapId) // if mapId != -1, pick up only those files with that mapId.
+ continue;
+ count++;
+ storageLock.lock();
+ m_tilespecToOfflineFilepath[spec] = i.value().first;
+ storageLock.unlock();
+ if (m_requestCancel)
+ return;
+ }
+ //qInfo() << "OSM plugin has found and is using "<< count <<" offline tiles";
+}
+
+QString QGeoFileTileCacheOsm::tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const
+{
+ int providerId = spec.mapId() - 1;
+ if (providerId < 0 || providerId >= m_providers.size())
+ return QString();
+ QString filename = spec.plugin();
+ filename += QLatin1String("-");
+ filename += (m_providers[providerId]->isHighDpi()) ? QLatin1Char('h') : QLatin1Char('l');
+ filename += QLatin1String("-");
+ filename += QString::number(spec.mapId());
+ filename += QLatin1String("-");
+ filename += QString::number(spec.zoom());
+ filename += QLatin1String("-");
+ filename += QString::number(spec.x());
+ filename += QLatin1String("-");
+ filename += QString::number(spec.y());
+
+ //Append version if real version number to ensure backwards compatibility and eviction of old tiles
+ if (spec.version() != -1) {
+ filename += QLatin1String("-");
+ filename += QString::number(spec.version());
+ }
+
+ filename += QLatin1String(".");
+ filename += format;
+
+ QDir dir = QDir(directory);
+
+ return dir.filePath(filename);
+}
+
+QGeoTileSpec QGeoFileTileCacheOsm::filenameToTileSpec(const QString &filename) const
+{
+ QGeoTileSpec emptySpec;
+
+ QStringList parts = filename.split('.');
+
+ if (parts.length() != 2)
+ return emptySpec;
+
+ QString name = parts.at(0);
+ QStringList fields = name.split('-');
+
+ int length = fields.length();
+ if (length != 6 && length != 7)
+ return emptySpec;
+
+ QList<int> numbers;
+
+ bool ok = false;
+ for (int i = 2; i < length; ++i) {
+ ok = false;
+ int value = fields.at(i).toInt(&ok);
+ if (!ok)
+ return emptySpec;
+ numbers.append(value);
+ }
+
+ bool highDpi = m_providers[numbers.at(0) - 1]->isHighDpi();
+ if (highDpi && fields.at(1) != QLatin1Char('h'))
+ return emptySpec;
+ else if (!highDpi && fields.at(1) != QLatin1Char('l'))
+ return emptySpec;
+
+ //File name without version, append default
+ if (numbers.length() < 5)
+ numbers.append(-1);
+
+ return QGeoTileSpec(fields.at(0),
+ numbers.at(0),
+ numbers.at(1),
+ numbers.at(2),
+ numbers.at(3),
+ numbers.at(4));
+}
+
+void QGeoFileTileCacheOsm::clearObsoleteTiles(const QGeoTileProviderOsm *p)
+{
+ // process initialized providers, and connect the others
+
+ if (p->isResolved()) {
+ if (m_maxMapIdTimestamps[p->mapType().mapId()].isValid() && // there are tiles in the cache
+ p->timestamp() > m_maxMapIdTimestamps[p->mapType().mapId()]) { // and they are older than the provider
+ qInfo() << "provider for " << p->mapType().name() << " timestamp: " << p->timestamp()
+ << " -- data last modified: " << m_maxMapIdTimestamps[p->mapType().mapId()] << ". Clearing.";
+ clearMapId(p->mapType().mapId());
+ m_maxMapIdTimestamps[p->mapType().mapId()] = p->timestamp(); // don't do it again.
+ }
+ } else {
+ connect(p, &QGeoTileProviderOsm::resolutionFinished,
+ this, &QGeoFileTileCacheOsm::onProviderResolutionFinished);
+#if 0 // If resolution fails, better not try to remove anything. Beside, on error, resolutionFinished is also emitted.
+ connect(p, &QGeoTileProviderOsm::resolutionError,
+ this, &QGeoFileTileCacheOsm::onProviderResolutionError);
+#endif
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/geoservices/osm/qgeofiletilecacheosm.h b/src/plugins/geoservices/osm/qgeofiletilecacheosm.h
new file mode 100644
index 00000000..d26cad4a
--- /dev/null
+++ b/src/plugins/geoservices/osm/qgeofiletilecacheosm.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** 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 QGEOFILETILECACHEOSM_H
+#define QGEOFILETILECACHEOSM_H
+
+#include "qgeotileproviderosm.h"
+#include <QtLocation/private/qgeofiletilecache_p.h>
+#include <QHash>
+#include <QtConcurrent>
+#include <qatomic.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGeoFileTileCacheOsm : public QGeoFileTileCache
+{
+ Q_OBJECT
+public:
+ QGeoFileTileCacheOsm(const QVector<QGeoTileProviderOsm *> &providers,
+ const QString &offlineDirectory = QString(),
+ const QString &directory = QString(),
+ QObject *parent = 0);
+ ~QGeoFileTileCacheOsm();
+
+ QSharedPointer<QGeoTileTexture> get(const QGeoTileSpec &spec) Q_DECL_OVERRIDE;
+
+Q_SIGNALS:
+ void mapDataUpdated(int mapId);
+
+protected Q_SLOTS:
+ void onProviderResolutionFinished(const QGeoTileProviderOsm *provider);
+ void onProviderResolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error);
+
+protected:
+ void init() Q_DECL_OVERRIDE;
+ QString tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const Q_DECL_OVERRIDE;
+ QGeoTileSpec filenameToTileSpec(const QString &filename) const Q_DECL_OVERRIDE;
+ QSharedPointer<QGeoTileTexture> getFromOfflineStorage(const QGeoTileSpec &spec);
+ void dropTiles(int mapId);
+ void loadTiles(int mapId);
+
+ void initOfflineRegistry(int mapId = -1);
+ void clearObsoleteTiles(const QGeoTileProviderOsm *p);
+
+ QString m_offlineDirectory;
+ QHash<QGeoTileSpec, QString> m_tilespecToOfflineFilepath;
+ QAtomicInt m_requestCancel;
+ QFuture<void> m_future;
+ QMap<int, QFuture<void>> m_mapIdFutures;
+ QMutex storageLock;
+ QVector<QGeoTileProviderOsm *> m_providers;
+ QVector<bool> m_highDpi;
+ QVector<QDateTime> m_maxMapIdTimestamps;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGEOFILETILECACHEOSM_H
diff --git a/src/plugins/geoservices/osm/qgeotiledmaposm.cpp b/src/plugins/geoservices/osm/qgeotiledmaposm.cpp
index f16e602a..d94a40a6 100644
--- a/src/plugins/geoservices/osm/qgeotiledmaposm.cpp
+++ b/src/plugins/geoservices/osm/qgeotiledmaposm.cpp
@@ -71,10 +71,13 @@ void QGeoTiledMapOsm::evaluateCopyrights(const QSet<QGeoTileSpec> &visibleTiles)
return;
int providerId = tile.mapId() - 1;
- if (providerId < 0 || providerId >= m_engine->providers().size() || !m_engine->providers().at(providerId)->isValid())
+ if (providerId < 0 || providerId >= m_engine->providers().size())
return;
m_mapId = tile.mapId();
+ if (!m_engine->providers().at(providerId)->isValid())
+ return;
+
onProviderDataUpdated(m_engine->providers().at(providerId));
}
diff --git a/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp
index fba177f4..ff79c261 100644
--- a/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp
+++ b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp
@@ -40,7 +40,7 @@
#include "qgeotiledmappingmanagerengineosm.h"
#include "qgeotilefetcherosm.h"
#include "qgeotiledmaposm.h"
-#include "qgeotileproviderosm.h"
+#include "qgeofiletilecacheosm.h"
#include <QtLocation/private/qgeocameracapabilities_p.h>
#include <QtLocation/private/qgeomaptype_p.h>
@@ -62,77 +62,120 @@ QGeoTiledMappingManagerEngineOsm::QGeoTiledMappingManagerEngineOsm(const QVarian
setTileSize(QSize(256, 256));
QNetworkAccessManager *nm = new QNetworkAccessManager();
- QString domain = QStringLiteral("http://maps-redirect.qt.io/osm/5.6/");
+ QString domain = QStringLiteral("http://maps-redirect.qt.io/osm/5.8/");
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/"
+ // Allowing some malformed addresses
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;
+ if (QUrl(customAddress).isValid())
+ domain = customAddress;
+ else
+ qWarning() << "Invalid custom providers repository address: " << customAddress;
}
- m_providers.push_back(
- new QGeoTileProviderOsm(domain + "street",
- nm,
+ bool highdpi = false;
+ if (parameters.contains(QStringLiteral("osm.mapping.highdpi_tiles"))) {
+ const QString param = parameters.value(QStringLiteral("osm.mapping.highdpi_tiles")).toString().toLower();
+ if (param == "true")
+ highdpi = true;
+ }
+
+ /* TileProviders setup */
+ QVector<TileProvider *> providers_street;
+ QVector<TileProvider *> providers_satellite;
+ QVector<TileProvider *> providers_cycle;
+ QVector<TileProvider *> providers_transit;
+ QVector<TileProvider *> providers_nighttransit;
+ QVector<TileProvider *> providers_terrain;
+ QVector<TileProvider *> providers_hiking;
+ if (highdpi) {
+ providers_street.push_back(new TileProvider(domain + "street-hires", true));
+ providers_satellite.push_back(new TileProvider(domain + "satellite-hires", true));
+ providers_cycle.push_back(new TileProvider(domain + "cycle-hires", true));
+ providers_transit.push_back(new TileProvider(domain + "transit-hires", true));
+ providers_nighttransit.push_back(new TileProvider(domain + "night-transit-hires", true));
+ providers_terrain.push_back(new TileProvider(domain + "terrain-hires", true));
+ providers_hiking.push_back(new TileProvider(domain + "hiking-hires", true));
+ }
+ providers_street.push_back(new TileProvider(domain + "street"));
+ providers_satellite.push_back(new TileProvider(domain + "satellite"));
+ providers_cycle.push_back(new TileProvider(domain + "cycle"));
+ providers_transit.push_back(new TileProvider(domain + "transit"));
+ providers_nighttransit.push_back(new TileProvider(domain + "night-transit"));
+ providers_terrain.push_back(new TileProvider(domain + "terrain"));
+ providers_hiking.push_back(new TileProvider(domain + "hiking"));
+ // Backups
+ const QDateTime defaultTs = QDateTime::fromString(QStringLiteral("2016-06-01T00:00:00"), Qt::ISODate);
+ providers_street.push_back(
+ new 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")));
+ providers_street.back()->setTimestamp(defaultTs);
+
+ // No available open access satellite backup with satisfactory level of details at the present.
+
+ providers_cycle.push_back(
+ new 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")));
+ providers_cycle.back()->setTimestamp(defaultTs);
+
+ providers_transit.push_back(
+ new 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")));
+ providers_transit.back()->setTimestamp(defaultTs);
+
+ providers_nighttransit.push_back(
+ new 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")) );
+ providers_nighttransit.back()->setTimestamp(defaultTs);
+
+ providers_terrain.push_back(
+ new 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")));
+ providers_terrain.back()->setTimestamp(defaultTs);
+
+ providers_hiking.push_back(
+ new 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")));
+ providers_hiking.back()->setTimestamp(defaultTs);
+
+
+ /* QGeoTileProviderOsms setup */
+ m_providers.push_back( new QGeoTileProviderOsm( 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,
+ providers_street ));
+ m_providers.push_back( new QGeoTileProviderOsm( nm,
QGeoMapType(QGeoMapType::SatelliteMapDay, tr("Satellite Map"), tr("Satellite map view in daylight mode"), false, false, 2),
- QGeoTileProviderOsm::TileProvider()
- ));
- m_providers.push_back(
- new QGeoTileProviderOsm(domain + "cycle",
- nm,
+ providers_satellite ));
+ m_providers.push_back( new QGeoTileProviderOsm( 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,
+ providers_cycle ));
+ m_providers.push_back( new QGeoTileProviderOsm( 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,
+ providers_transit ));
+ m_providers.push_back( new QGeoTileProviderOsm( 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,
+ providers_nighttransit ));
+ m_providers.push_back( new QGeoTileProviderOsm( 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,
+ providers_terrain ));
+ m_providers.push_back( new QGeoTileProviderOsm( 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")
- )));
+ providers_hiking ));
if (parameters.contains(QStringLiteral("osm.mapping.custom.host"))
|| parameters.contains(QStringLiteral("osm.mapping.host"))) {
@@ -154,14 +197,13 @@ QGeoTiledMappingManagerEngineOsm::QGeoTiledMappingManagerEngineOsm(const QVarian
m_customCopyright = parameters.value(QStringLiteral("osm.mapping.copyright")).toString();
m_providers.push_back(
- new QGeoTileProviderOsm("",
- nm,
+ 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"),
+ { new TileProvider(tmsServer + QStringLiteral("%z/%x/%y.png"),
QStringLiteral("png"),
mapCopyright,
- dataCopyright
- )));
+ dataCopyright) }
+ ));
m_providers.last()->disableRedirection();
}
@@ -182,20 +224,87 @@ QGeoTiledMappingManagerEngineOsm::QGeoTiledMappingManagerEngineOsm(const QVarian
}
updateMapTypes();
+
+ /* TILE CACHE */
+ if (parameters.contains(QStringLiteral("osm.mapping.cache.directory"))) {
+ m_cacheDirectory = parameters.value(QStringLiteral("osm.mapping.cache.directory")).toString();
+ } else {
+ // managerName() is not yet set, we have to hardcode the plugin name below
+ m_cacheDirectory = QAbstractGeoTileCache::baseLocationCacheDirectory() + QLatin1String("osm");
+ }
+ if (parameters.contains(QStringLiteral("osm.mapping.offline.directory")))
+ m_offlineDirectory = parameters.value(QStringLiteral("osm.mapping.offline.directory")).toString();
+ QGeoFileTileCacheOsm *tileCache = new QGeoFileTileCacheOsm(m_providers, m_offlineDirectory, m_cacheDirectory);
+
+ /*
+ * Disk cache setup -- defaults to ByteSize (old behavior)
+ */
+ if (parameters.contains(QStringLiteral("osm.mapping.cache.disk.cost_strategy"))) {
+ QString cacheStrategy = parameters.value(QStringLiteral("osm.mapping.cache.disk.cost_strategy")).toString().toLower();
+ if (cacheStrategy == QLatin1String("bytesize"))
+ tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize);
+ else
+ tileCache->setCostStrategyDisk(QGeoFileTileCache::Unitary);
+ } else {
+ tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize);
+ }
+ if (parameters.contains(QStringLiteral("osm.mapping.cache.disk.size"))) {
+ bool ok = false;
+ int cacheSize = parameters.value(QStringLiteral("osm.mapping.cache.disk.size")).toString().toInt(&ok);
+ if (ok)
+ tileCache->setMaxDiskUsage(cacheSize);
+ }
+
+ /*
+ * Memory cache setup -- defaults to ByteSize (old behavior)
+ */
+ if (parameters.contains(QStringLiteral("osm.mapping.cache.memory.cost_strategy"))) {
+ QString cacheStrategy = parameters.value(QStringLiteral("osm.mapping.cache.memory.cost_strategy")).toString().toLower();
+ if (cacheStrategy == QLatin1String("bytesize"))
+ tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize);
+ else
+ tileCache->setCostStrategyMemory(QGeoFileTileCache::Unitary);
+ } else {
+ tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize);
+ }
+ if (parameters.contains(QStringLiteral("osm.mapping.cache.memory.size"))) {
+ bool ok = false;
+ int cacheSize = parameters.value(QStringLiteral("osm.mapping.cache.memory.size")).toString().toInt(&ok);
+ if (ok)
+ tileCache->setMaxMemoryUsage(cacheSize);
+ }
+
+ /*
+ * Texture cache setup -- defaults to ByteSize (old behavior)
+ */
+ if (parameters.contains(QStringLiteral("osm.mapping.cache.texture.cost_strategy"))) {
+ QString cacheStrategy = parameters.value(QStringLiteral("osm.mapping.cache.texture.cost_strategy")).toString().toLower();
+ if (cacheStrategy == QLatin1String("bytesize"))
+ tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize);
+ else
+ tileCache->setCostStrategyTexture(QGeoFileTileCache::Unitary);
+ } else {
+ tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize);
+ }
+ if (parameters.contains(QStringLiteral("osm.mapping.cache.texture.size"))) {
+ bool ok = false;
+ int cacheSize = parameters.value(QStringLiteral("osm.mapping.cache.texture.size")).toString().toInt(&ok);
+ if (ok)
+ tileCache->setExtraTextureUsage(cacheSize);
+ }
+
+
+ setTileCache(tileCache);
+
+
+ /* TILE FETCHER */
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);
}
-
-
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();
}
@@ -206,7 +315,10 @@ QGeoTiledMappingManagerEngineOsm::~QGeoTiledMappingManagerEngineOsm()
QGeoMap *QGeoTiledMappingManagerEngineOsm::createMap()
{
- return new QGeoTiledMapOsm(this);
+ QGeoTiledMap *map = new QGeoTiledMapOsm(this);
+ connect(qobject_cast<QGeoFileTileCacheOsm *>(tileCache()), &QGeoFileTileCacheOsm::mapDataUpdated
+ , map, &QGeoTiledMap::clearScene);
+ return map;
}
const QVector<QGeoTileProviderOsm *> &QGeoTiledMappingManagerEngineOsm::providers()
@@ -226,9 +338,8 @@ void QGeoTiledMappingManagerEngineOsm::onProviderResolutionFinished(const QGeoTi
updateMapTypes();
}
-void QGeoTiledMappingManagerEngineOsm::onProviderResolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error)
+void QGeoTiledMappingManagerEngineOsm::onProviderResolutionError(const QGeoTileProviderOsm *provider)
{
- Q_UNUSED(error)
if (!provider->isResolved())
return;
updateMapTypes();
diff --git a/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.h b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.h
index 247e4377..b1f0a13c 100644
--- a/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.h
+++ b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.h
@@ -65,7 +65,7 @@ public:
protected Q_SLOTS:
void onProviderResolutionFinished(const QGeoTileProviderOsm *provider);
- void onProviderResolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error);
+ void onProviderResolutionError(const QGeoTileProviderOsm *provider);
protected:
void updateMapTypes();
@@ -73,6 +73,8 @@ protected:
private:
QVector<QGeoTileProviderOsm *> m_providers;
QString m_customCopyright;
+ QString m_cacheDirectory;
+ QString m_offlineDirectory;
};
QT_END_NAMESPACE
diff --git a/src/plugins/geoservices/osm/qgeotilefetcherosm.cpp b/src/plugins/geoservices/osm/qgeotilefetcherosm.cpp
index 98621411..f7c25d61 100644
--- a/src/plugins/geoservices/osm/qgeotilefetcherosm.cpp
+++ b/src/plugins/geoservices/osm/qgeotilefetcherosm.cpp
@@ -43,6 +43,7 @@
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtLocation/private/qgeotilespec_p.h>
+#include <QtLocation/private/qgeotilefetcher_p_p.h>
QT_BEGIN_NAMESPACE
@@ -55,10 +56,30 @@ static bool providersResolved(const QVector<QGeoTileProviderOsm *> &providers)
return true;
}
+class QGeoTileFetcherOsmPrivate : public QGeoTileFetcherPrivate
+{
+ Q_DECLARE_PUBLIC(QGeoTileFetcherOsm)
+public:
+ QGeoTileFetcherOsmPrivate();
+ virtual ~QGeoTileFetcherOsmPrivate();
+
+private:
+ Q_DISABLE_COPY(QGeoTileFetcherOsmPrivate)
+};
+
+QGeoTileFetcherOsmPrivate::QGeoTileFetcherOsmPrivate() : QGeoTileFetcherPrivate()
+{
+}
+
+QGeoTileFetcherOsmPrivate::~QGeoTileFetcherOsmPrivate()
+{
+}
+
+
QGeoTileFetcherOsm::QGeoTileFetcherOsm(const QVector<QGeoTileProviderOsm *> &providers,
QNetworkAccessManager *nm,
QObject *parent)
-: QGeoTileFetcher(parent), m_userAgent("Qt Location based application"),
+: QGeoTileFetcher(*new QGeoTileFetcherOsmPrivate(), parent), m_userAgent("Qt Location based application"),
m_providers(providers), m_nm(nm), m_ready(true)
{
m_nm->setParent(this);
@@ -69,6 +90,8 @@ QGeoTileFetcherOsm::QGeoTileFetcherOsm(const QVector<QGeoTileProviderOsm *> &pro
this, &QGeoTileFetcherOsm::onProviderResolutionFinished);
connect(provider, &QGeoTileProviderOsm::resolutionError,
this, &QGeoTileFetcherOsm::onProviderResolutionError);
+ connect(provider, &QGeoTileProviderOsm::resolutionRequired,
+ this, &QGeoTileFetcherOsm::restartTimer, Qt::QueuedConnection);
provider->resolveProvider();
}
}
@@ -100,9 +123,8 @@ void QGeoTileFetcherOsm::onProviderResolutionFinished(const QGeoTileProviderOsm
emit providerDataUpdated(provider);
}
-void QGeoTileFetcherOsm::onProviderResolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error)
+void QGeoTileFetcherOsm::onProviderResolutionError(const QGeoTileProviderOsm *provider)
{
- Q_UNUSED(error)
if ((m_ready = providersResolved(m_providers))) {
qWarning("QGeoTileFetcherOsm: all providers resolved");
readyUpdated();
@@ -110,6 +132,14 @@ void QGeoTileFetcherOsm::onProviderResolutionError(const QGeoTileProviderOsm *pr
emit providerDataUpdated(provider);
}
+void QGeoTileFetcherOsm::restartTimer()
+{
+ Q_D(QGeoTileFetcherOsm);
+
+ if (!d->queue_.isEmpty())
+ d->timer_.start(0, this);
+}
+
QGeoTiledMapReply *QGeoTileFetcherOsm::getTileImage(const QGeoTileSpec &spec)
{
int id = spec.mapId();
@@ -122,6 +152,9 @@ QGeoTiledMapReply *QGeoTileFetcherOsm::getTileImage(const QGeoTileSpec &spec)
}
id -= 1; // TODO: make OSM map ids start from 0.
+ if (spec.zoom() > m_providers[id]->maximumZoomLevel() || spec.zoom() < m_providers[id]->minimumZoomLevel())
+ return Q_NULLPTR;
+
const QUrl url = m_providers[id]->tileAddress(spec.x(), spec.y(), spec.zoom());
QNetworkRequest request;
request.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent);
diff --git a/src/plugins/geoservices/osm/qgeotilefetcherosm.h b/src/plugins/geoservices/osm/qgeotilefetcherosm.h
index 8d69cc56..a7b89bad 100644
--- a/src/plugins/geoservices/osm/qgeotilefetcherosm.h
+++ b/src/plugins/geoservices/osm/qgeotilefetcherosm.h
@@ -47,10 +47,12 @@
QT_BEGIN_NAMESPACE
class QNetworkAccessManager;
+class QGeoTileFetcherOsmPrivate;
class QGeoTileFetcherOsm : public QGeoTileFetcher
{
Q_OBJECT
+ Q_DECLARE_PRIVATE(QGeoTileFetcherOsm)
friend class QGeoMapReplyOsm;
friend class QGeoTiledMappingManagerEngineOsm;
@@ -69,10 +71,11 @@ protected:
protected Q_SLOTS:
void onProviderResolutionFinished(const QGeoTileProviderOsm *provider);
- void onProviderResolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error);
+ void onProviderResolutionError(const QGeoTileProviderOsm *provider);
+ void restartTimer();
private:
- QGeoTiledMapReply *getTileImage(const QGeoTileSpec &spec);
+ QGeoTiledMapReply *getTileImage(const QGeoTileSpec &spec) Q_DECL_OVERRIDE;
void readyUpdated();
QByteArray m_userAgent;
diff --git a/src/plugins/geoservices/osm/qgeotileproviderosm.cpp b/src/plugins/geoservices/osm/qgeotileproviderosm.cpp
index 3d46a425..1989c44f 100644
--- a/src/plugins/geoservices/osm/qgeotileproviderosm.cpp
+++ b/src/plugins/geoservices/osm/qgeotileproviderosm.cpp
@@ -43,115 +43,89 @@
QT_BEGIN_NAMESPACE
static const int maxValidZoom = 30;
+static const QDateTime defaultTs = QDateTime::fromString(QStringLiteral("2016-06-01T00:00:00"), Qt::ISODate);
-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()
{
-
}
-void QGeoTileProviderOsm::resolveProvider()
+QUrl QGeoTileProviderOsm::tileAddress(int x, int y, int z) const
{
- switch (m_status) {
- case Resolving:
- case Invalid:
- case Valid:
- return;
- case Idle:
- m_status = Resolving;
- break;
- }
+ if (m_status != Resolved || !m_provider)
+ return QUrl();
+ return m_provider->tileAddress(x, y, z);
+}
- 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)));
+QString QGeoTileProviderOsm::mapCopyRight() const
+{
+ if (m_status != Resolved || !m_provider)
+ return QString();
+ return m_provider->mapCopyRight();
}
-void QGeoTileProviderOsm::disableRedirection()
+QString QGeoTileProviderOsm::dataCopyRight() const
{
- m_status = Invalid;
- m_provider.m_valid = false;
+ if (m_status != Resolved || !m_provider)
+ return QString();
+ return m_provider->dataCopyRight();
}
-void QGeoTileProviderOsm::handleError(QNetworkReply::NetworkError error)
+QString QGeoTileProviderOsm::styleCopyRight() const
{
- 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;
- }
+ if (m_status != Resolved || !m_provider)
+ return QString();
+ return m_provider->styleCopyRight();
}
-QUrl QGeoTileProviderOsm::tileAddress(int x, int y, int z) const
+QString QGeoTileProviderOsm::format() 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();
+ if (m_status != Resolved || !m_provider)
+ return QString();
+ return m_provider->format();
}
-QString QGeoTileProviderOsm::mapCopyRight() const
+int QGeoTileProviderOsm::minimumZoomLevel() const
{
- if (m_provider.isValid())
- return m_provider.mapCopyRight();
- if (m_providerFallback.isValid())
- return m_providerFallback.mapCopyRight();
- return QString();
+ if (m_status != Resolved || !m_provider)
+ return 0;
+ return m_provider->minimumZoomLevel();
}
-QString QGeoTileProviderOsm::dataCopyRight() const
+int QGeoTileProviderOsm::maximumZoomLevel() const
{
- if (m_provider.isValid())
- return m_provider.dataCopyRight();
- if (m_providerFallback.isValid())
- return m_providerFallback.dataCopyRight();
- return QString();
+ if (m_status != Resolved || !m_provider)
+ return 20;
+ return m_provider->maximumZoomLevel();
}
-QString QGeoTileProviderOsm::styleCopyRight() const
+bool QGeoTileProviderOsm::isHighDpi() const
{
- if (m_provider.isValid())
- return m_provider.styleCopyRight();
- if (m_providerFallback.isValid())
- return m_providerFallback.styleCopyRight();
- return QString();
+ if (!m_provider)
+ return false;
+ return m_provider->isHighDpi();
}
-QString QGeoTileProviderOsm::format() const
+const QDateTime QGeoTileProviderOsm::timestamp() const
{
- if (m_provider.isValid())
- return m_provider.format();
- if (m_providerFallback.isValid())
- return m_providerFallback.format();
- return QString();
+ if (!m_provider)
+ return QDateTime();
+ return m_provider->timestamp();
}
const QGeoMapType &QGeoTileProviderOsm::mapType() const
@@ -161,33 +135,218 @@ const QGeoMapType &QGeoTileProviderOsm::mapType() const
bool QGeoTileProviderOsm::isValid() const
{
- return (m_provider.isValid() || m_providerFallback.isValid());
+ if (m_status != Resolved || !m_provider)
+ return false;
+ return m_provider->isValid();
}
bool QGeoTileProviderOsm::isResolved() const
{
- return (m_status == Valid || m_status == Invalid);
+ return (m_status == Resolved);
+}
+
+void QGeoTileProviderOsm::resolveProvider()
+{
+ if (m_status == Resolved || m_status == Resolving)
+ return;
+
+ 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()
+{
+ 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::onResolutionFinished(TileProvider *provider)
+{
+ 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;
+#if 0 // leaving triggering the retry to the tile fetcher, instead of constantly spinning it in here.
+ m_status = Resolving;
+ p->resolveProvider();
+#endif
+ emit resolutionRequired();
+ }
+ break;
+ }
+ }
+ 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;
+#if 0 // leaving triggering the retry to the tile fetcher
+ m_provider->resolveProvider();
+#endif
+ }
+}
+
+void QGeoTileProviderOsm::addProvider(TileProvider *provider)
+{
+ 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;
+}
+
+
+/*
+ Class TileProvder
+*/
+
+static void sort2(int &a, int &b)
+{
+ if (a > b) {
+ int temp=a;
+ a=b;
+ b=temp;
+ }
+}
+
+TileProvider::TileProvider() : m_status(Invalid), m_nm(nullptr), m_timestamp(defaultTs), m_highDpi(false)
+{
+
+}
+
+TileProvider::TileProvider(const QUrl &urlRedirector, bool highDpi)
+: m_status(Idle), m_urlRedirector(urlRedirector), m_nm(nullptr), m_timestamp(defaultTs), m_highDpi(highDpi)
+{
+ if (!m_urlRedirector.isValid())
+ m_status = Invalid;
+}
+
+TileProvider::TileProvider(const QString &urlTemplate,
+ const QString &format,
+ const QString &copyRightMap,
+ const QString &copyRightData,
+ bool highDpi,
+ 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), m_timestamp(defaultTs), m_highDpi(highDpi)
+{
+ setupProvider();
+}
+
+TileProvider::~TileProvider()
+{
+}
+
+void TileProvider::resolveProvider()
+{
+ 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);
+ request.setAttribute(QNetworkRequest::BackgroundRequestAttribute, true);
+ 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 TileProvider::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:
+ //qWarning() << "QGeoTileProviderOsm network error:" << error;
+ 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;
@@ -205,48 +364,51 @@ void QGeoTileProviderOsm::onNetworkReplyFinished()
* "StyleCopyRight" : "<copyright>", (optional)
* "MinimumZoomLevel" : <minimumZoomLevel>, (optional)
* "MaximumZoomLevel" : <maximumZoomLevel>, (optional)
+ * "Timestamp" : <timestamp>, (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,
+ * requests to the providers, if they do not support the specific ZL. Default is 0 and 20,
* respectively.
*
- * <server address template> is required, and is the tile url template, with %x, %y and %z as
+ * UrlTemplate 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.
+ * ImageFormat 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
+ * 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
+ * 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
+ * StyleCopyRight is optional and is the string that will be displayed in the optional "Style (c)" part
* of the on-screen copyright notice.
+ *
+ * Timestamp is optional, and if set will cause QtLocation to clear the content of the cache older
+ * than this timestamp. The purpose is to prevent mixing tiles from different providers in the cache
+ * upon provider change. The value must be a string in ISO 8601 format (see Qt::ISODate)
*/
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();
@@ -263,56 +425,200 @@ 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 = 20;
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_maximumZoomLevel = qBound(0, int(maxZoom.toDouble()), maxValidZoom);
- m_provider = TileProvider(urlTemplate.toString(),
- imageFormat.toString(),
- copyRightMap.toString(),
- copyRightData.toString(),
- minZL,
- maxZL);
- m_provider.setStyleCopyRight(styleCopyRight);
+ const QJsonValue ts = json.value(QLatin1String("Timestamp"));
+ if (ts.isString())
+ m_timestamp = QDateTime::fromString(ts.toString(), Qt::ISODate);
- if (m_provider.isValid())
- m_status = Valid;
-
- 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;
+}
+
+int TileProvider::minimumZoomLevel() const
+{
+ return m_minimumZoomLevel;
+}
+
+int TileProvider::maximumZoomLevel() const
+{
+ return m_maximumZoomLevel;
}
+const QDateTime &TileProvider::timestamp() const
+{
+ return m_timestamp;
+}
+
+bool TileProvider::isHighDpi() const
+{
+ return m_highDpi;
+}
+
+void TileProvider::setStyleCopyRight(const QString &copyright)
+{
+ m_copyRightStyle = copyright;
+}
+
+void TileProvider::setTimestamp(const QDateTime &timestamp)
+{
+ m_timestamp = timestamp;
+}
+
+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
diff --git a/src/plugins/geoservices/osm/qgeotileproviderosm.h b/src/plugins/geoservices/osm/qgeotileproviderosm.h
index f396b3b5..b8647244 100644
--- a/src/plugins/geoservices/osm/qgeotileproviderosm.h
+++ b/src/plugins/geoservices/osm/qgeotileproviderosm.h
@@ -40,215 +40,148 @@
#include <QtLocation/private/qgeomaptype_p.h>
#include <QtCore/QUrl>
+#include <QtCore/QVector>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtCore/QPointer>
#include <QTimer>
#include <algorithm>
+#include <QtCore/QJsonDocument>
+#include <QtCore/QJsonObject>
+#include <QDateTime>
QT_BEGIN_NAMESPACE
-class QGeoTileProviderOsm: public QObject
+class TileProvider: 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 + 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_valid = true;
- }
-
- ~TileProvider()
- {
- }
-
- inline bool isValid() const
- {
- return m_valid;
- }
+ enum Status {Idle,
+ Resolving,
+ Valid,
+ Invalid };
- inline QString mapCopyRight() const
- {
- return m_copyRightMap;
- }
+ TileProvider();
+ // "Online" constructor. Needs resolution to fetch the parameters
+ TileProvider(const QUrl &urlRedirector, bool highDpi = false);
+ // Offline constructor. Doesn't need URLRedirector and networkmanager
+ TileProvider(const QString &urlTemplate,
+ const QString &format,
+ const QString &copyRightMap,
+ const QString &copyRightData,
+ bool highDpi = false,
+ int minimumZoomLevel = 0,
+ int maximumZoomLevel = 19);
+
+ ~TileProvider();
+ void setNetworkManager(QNetworkAccessManager *nm);
- inline QString dataCopyRight() const
- {
- return m_copyRightData;
- }
+ void resolveProvider();
+ void handleError(QNetworkReply::NetworkError error);
+ void setupProvider();
+
+ inline bool isValid() const;
+ inline bool isInvalid() const;
+ inline bool isResolved() const;
+ inline Status status() const;
+
+ inline QString mapCopyRight() const;
+ inline QString dataCopyRight() const;
+ inline QString styleCopyRight() const;
+ inline QString format() const;
+ inline int minimumZoomLevel() const;
+ inline int maximumZoomLevel() const;
+ inline const QDateTime &timestamp() const;
+ inline bool isHighDpi() const;
+ QUrl tileAddress(int x, int y, int z) const;
- inline QString styleCopyRight() const
- {
- return m_copyRightStyle;
- }
+ // Optional properties, not needed to construct a provider
+ void setStyleCopyRight(const QString &copyright);
+ void setTimestamp(const QDateTime &timestamp);
- inline QString format() const
- {
- return m_format;
- }
+ Status m_status;
+ QUrl m_urlRedirector; // The URL from where to fetch the URL template in case of a provider to resolve.
+ QNetworkAccessManager *m_nm;
+ 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;
+ QDateTime m_timestamp;
+ bool m_highDpi;
+
+ int paramsLUT[3]; //Lookup table to handle possibly shuffled x,y,z
+ QString paramsSep[2]; // what goes in between %x, %y and %z
- // Optional properties, not needed to construct a provider
- void setStyleCopyRight(const QString &copyright)
- {
- m_copyRightStyle = copyright;
- }
+Q_SIGNALS:
+ void resolutionFinished(TileProvider *provider);
+ void resolutionError(TileProvider *provider);
- 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);
- }
+public Q_SLOTS:
+ void onNetworkReplyFinished();
+ void onNetworkReplyError(QNetworkReply::NetworkError error);
- 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;
+friend class QGeoTileProviderOsm;
+};
- int paramsLUT[3]; //Lookup table to handle possibly shuffled x,y,z
- QString paramsSep[2]; // what goes in between %x, %y and %z
- };
+class QGeoTileProviderOsm: public QObject
+{
+ Q_OBJECT
+ friend class QGeoTileFetcherOsm;
+ friend class QGeoMapReplyOsm;
+ friend class QGeoTiledMappingManagerEngineOsm;
+public:
enum Status {Idle,
Resolving,
- Valid,
- Invalid };
-
- QGeoTileProviderOsm(const QString &urlRedir,
- QNetworkAccessManager *nm,
- const QGeoMapType &mapType,
- const TileProvider &providerFallback);
+ Resolved };
+ QGeoTileProviderOsm(QNetworkAccessManager *nm,
+ const QGeoMapType &mapType,
+ const QVector<TileProvider *> &providers);
~QGeoTileProviderOsm();
-
-
QUrl tileAddress(int x, int y, int z) const;
QString mapCopyRight() const;
QString dataCopyRight() const;
QString styleCopyRight() const;
QString format() const;
+ int minimumZoomLevel() const;
+ int maximumZoomLevel() const;
+ bool isHighDpi() const;
const QGeoMapType &mapType() const;
bool isValid() const;
bool isResolved() const;
+ const QDateTime timestamp() const;
Q_SIGNALS:
void resolutionFinished(const QGeoTileProviderOsm *provider);
- void resolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error);
+ void resolutionError(const QGeoTileProviderOsm *provider);
+ void resolutionRequired();
public Q_SLOTS:
- void onNetworkReplyFinished();
- void onNetworkReplyError(QNetworkReply::NetworkError error);
void resolveProvider();
+ void disableRedirection();
+
+protected Q_SLOTS:
+ void onResolutionFinished(TileProvider *provider);
+ void onResolutionError(TileProvider *provider);
protected:
- void disableRedirection();
- void handleError(QNetworkReply::NetworkError error);
+ void addProvider(TileProvider *provider);
+
+/* Data members */
QNetworkAccessManager *m_nm;
- QUrl m_urlRedirector; // The URL from where to fetch the URL template
- TileProvider m_provider;
- TileProvider m_providerFallback;
+ QVector<TileProvider *> m_providerList;
+ TileProvider *m_provider;
+ int m_providerId;
QGeoMapType m_mapType;
Status m_status;
- QTimer m_retryTimer;
};
QT_END_NAMESPACE