summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaolo Angelelli <paolo.angelelli@qt.io>2016-08-01 19:44:41 +0200
committerPaolo Angelelli <paolo.angelelli@qt.io>2016-11-08 15:19:24 +0000
commit4f192687cc07dbe5de1dbed3187d90045ed38703 (patch)
tree157d0d481f7d51215f0e197dfa3160017e820f32
parent219e7b84ac048ff585c36f47a1c783af9ae4e4bc (diff)
Evict obsolete provider data on init()
During initialization, this patch makes sure that the cached tiles belong to the provider currently in use by using the file lastModified() value and comparing it against the (optional) Timestamp in the provider records. If this value is not present in the provider record, or if it is older than the newest modified file, the data is untouched. This operation is performed separately for each map id. This method isn't perfect in all use cases, though. E.g., if we are forced to shut down one of the provider and run on the hardcoded fallback, which has an older TS. These are however rare edge cases that most likely won't happen in practice (in the case above we could put the content of the hardcoded provider in the remote json files too) Change-Id: Ie29cf05c1fbc835ce4e3363fc0caa38a97800214 Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
-rw-r--r--src/location/maps/qcache3q_p.h11
-rw-r--r--src/location/maps/qgeofiletilecache.cpp30
-rw-r--r--src/location/maps/qgeofiletilecache_p.h1
-rw-r--r--src/plugins/geoservices/osm/qgeofiletilecacheosm.cpp109
-rw-r--r--src/plugins/geoservices/osm/qgeofiletilecacheosm.h11
-rw-r--r--src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp44
-rw-r--r--src/plugins/geoservices/osm/qgeotileproviderosm.cpp59
-rw-r--r--src/plugins/geoservices/osm/qgeotileproviderosm.h5
8 files changed, 221 insertions, 49 deletions
diff --git a/src/location/maps/qcache3q_p.h b/src/location/maps/qcache3q_p.h
index 4348e14b..cd21961a 100644
--- a/src/location/maps/qcache3q_p.h
+++ b/src/location/maps/qcache3q_p.h
@@ -161,7 +161,7 @@ public:
QSharedPointer<T> object(const Key &key) const;
QSharedPointer<T> operator[](const Key &key) const;
- void remove(const Key &key);
+ void remove(const Key &key, bool force = false);
QList<Key> keys() const;
void printStats();
@@ -411,14 +411,14 @@ void QCache3Q<Key,T,EvPolicy>::rebalance()
}
template <class Key, class T, class EvPolicy>
-void QCache3Q<Key,T,EvPolicy>::remove(const Key &key)
+void QCache3Q<Key,T,EvPolicy>::remove(const Key &key, bool force)
{
if (!lookup_.contains(key)) {
return;
}
Node *n = lookup_[key];
unlink(n);
- if (n->q != q1_evicted_)
+ if (n->q != q1_evicted_ && !force)
EvPolicy::aboutToBeRemoved(n->k, n->v);
lookup_.remove(key);
delete n;
@@ -427,10 +427,7 @@ void QCache3Q<Key,T,EvPolicy>::remove(const Key &key)
template <class Key, class T, class EvPolicy>
QList<Key> QCache3Q<Key,T,EvPolicy>::keys() const
{
- QList<Key> keys;
- for (auto i = lookup_.constBegin(); i != lookup_.constEnd(); ++i)
- keys.append(i.key());
- return keys;
+ return lookup_.keys();
}
template <class Key, class T, class EvPolicy>
diff --git a/src/location/maps/qgeofiletilecache.cpp b/src/location/maps/qgeofiletilecache.cpp
index 6e8fa856..95bce268 100644
--- a/src/location/maps/qgeofiletilecache.cpp
+++ b/src/location/maps/qgeofiletilecache.cpp
@@ -119,7 +119,7 @@ void QGeoFileTileCache::init()
QDir::root().mkpath(directory_);
// default values
- setMaxDiskUsage(20 * 1024 * 1024);
+ setMaxDiskUsage(50 * 1024 * 1024);
setMaxMemoryUsage(3 * 1024 * 1024);
setExtraTextureUsage(6 * 1024 * 1024);
@@ -284,6 +284,34 @@ void QGeoFileTileCache::clearAll()
}
}
+void QGeoFileTileCache::clearMapId(const int mapId)
+{
+ for (const QGeoTileSpec &k : diskCache_.keys())
+ if (k.mapId() == mapId)
+ diskCache_.remove(k, true);
+ for (const QGeoTileSpec &k : memoryCache_.keys())
+ if (k.mapId() == mapId)
+ memoryCache_.remove(k);
+ for (const QGeoTileSpec &k : textureCache_.keys())
+ if (k.mapId() == mapId)
+ textureCache_.remove(k);
+
+ // TODO: It seems the cache leaves residues, like some tiles do not get picked up.
+ // After the above calls, files that shouldnt be left behind are still on disk.
+ // Do an additional pass and make sure what has to be deleted gets deleted.
+ QDir dir(directory_);
+ QStringList formats;
+ formats << QLatin1String("*.*");
+ QStringList files = dir.entryList(formats, QDir::Files);
+ qWarning() << "Old tile data detected. Cache eviction left out "<< files.size() << "tiles";
+ for (const QString &tileFileName : files) {
+ QGeoTileSpec spec = filenameToTileSpec(tileFileName);
+ if (spec.mapId() != mapId)
+ continue;
+ QFile::remove(dir.filePath(tileFileName));
+ }
+}
+
QSharedPointer<QGeoTileTexture> QGeoFileTileCache::get(const QGeoTileSpec &spec)
{
QSharedPointer<QGeoTileTexture> tt = getFromMemory(spec);
diff --git a/src/location/maps/qgeofiletilecache_p.h b/src/location/maps/qgeofiletilecache_p.h
index fc023bc7..e850a13b 100644
--- a/src/location/maps/qgeofiletilecache_p.h
+++ b/src/location/maps/qgeofiletilecache_p.h
@@ -117,6 +117,7 @@ public:
int minTextureUsage() const Q_DECL_OVERRIDE;
int textureUsage() const Q_DECL_OVERRIDE;
void clearAll() Q_DECL_OVERRIDE;
+ void clearMapId(const int mapId);
QSharedPointer<QGeoTileTexture> get(const QGeoTileSpec &spec) Q_DECL_OVERRIDE;
diff --git a/src/plugins/geoservices/osm/qgeofiletilecacheosm.cpp b/src/plugins/geoservices/osm/qgeofiletilecacheosm.cpp
index 5028a82b..a563cced 100644
--- a/src/plugins/geoservices/osm/qgeofiletilecacheosm.cpp
+++ b/src/plugins/geoservices/osm/qgeofiletilecacheosm.cpp
@@ -45,13 +45,16 @@
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)
+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);
}
@@ -61,6 +64,8 @@ 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)
@@ -75,9 +80,10 @@ QSharedPointer<QGeoTileTexture> QGeoFileTileCacheOsm::get(const QGeoTileSpec &sp
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]) {
+ 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();
@@ -85,17 +91,60 @@ void QGeoFileTileCacheOsm::onProviderResolutionFinished(const QGeoTileProviderOs
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);
+ m_future = QtConcurrent::run(this, &QGeoFileTileCacheOsm::initOfflineRegistry, -1);
}
QSharedPointer<QGeoTileTexture> QGeoFileTileCacheOsm::getFromOfflineStorage(const QGeoTileSpec &spec)
@@ -159,7 +208,7 @@ void QGeoFileTileCacheOsm::loadTiles(int mapId)
}
}
-void QGeoFileTileCacheOsm::initOfflineRegistry()
+void QGeoFileTileCacheOsm::initOfflineRegistry(int mapId)
{
// Dealing with duplicates: picking the newest
QMap<QString, QPair<QString, QDateTime> > fileDates; // key is filename, value is <filepath, lastmodified>
@@ -173,11 +222,33 @@ void QGeoFileTileCacheOsm::initOfflineRegistry()
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.begin(); i != fileDates.end(); ++i) {
+ 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;
@@ -185,7 +256,7 @@ void QGeoFileTileCacheOsm::initOfflineRegistry()
if (m_requestCancel)
return;
}
- qWarning() << "OSM Offline tiles: "<<count;
+ //qInfo() << "OSM plugin has found and is using "<< count <<" offline tiles";
}
QString QGeoFileTileCacheOsm::tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const
@@ -264,4 +335,26 @@ QGeoTileSpec QGeoFileTileCacheOsm::filenameToTileSpec(const QString &filename) c
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
index 52d57747..d26cad4a 100644
--- a/src/plugins/geoservices/osm/qgeofiletilecacheosm.h
+++ b/src/plugins/geoservices/osm/qgeofiletilecacheosm.h
@@ -49,7 +49,10 @@ class QGeoFileTileCacheOsm : public QGeoFileTileCache
{
Q_OBJECT
public:
- QGeoFileTileCacheOsm(const QVector<QGeoTileProviderOsm *> &providers, const QString &offlineDirectory = QString(), const QString &directory = QString(), QObject *parent = 0);
+ 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;
@@ -59,6 +62,7 @@ Q_SIGNALS:
protected Q_SLOTS:
void onProviderResolutionFinished(const QGeoTileProviderOsm *provider);
+ void onProviderResolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error);
protected:
void init() Q_DECL_OVERRIDE;
@@ -68,15 +72,18 @@ protected:
void dropTiles(int mapId);
void loadTiles(int mapId);
- void initOfflineRegistry();
+ 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
diff --git a/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp
index f8e18de9..780874a3 100644
--- a/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp
+++ b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp
@@ -70,7 +70,10 @@ QGeoTiledMappingManagerEngineOsm::QGeoTiledMappingManagerEngineOsm(const QVarian
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;
}
bool highdpi = false;
@@ -105,37 +108,50 @@ QGeoTiledMappingManagerEngineOsm::QGeoTiledMappingManagerEngineOsm(const QVarian
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")));
- // No available satellite backup
+ 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 */
@@ -208,15 +224,8 @@ QGeoTiledMappingManagerEngineOsm::QGeoTiledMappingManagerEngineOsm(const QVarian
}
updateMapTypes();
- 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);
+ /* TILE CACHE */
if (parameters.contains(QStringLiteral("osm.mapping.cache.directory"))) {
m_cacheDirectory = parameters.value(QStringLiteral("osm.mapping.cache.directory")).toString();
} else {
@@ -225,11 +234,7 @@ QGeoTiledMappingManagerEngineOsm::QGeoTiledMappingManagerEngineOsm(const QVarian
}
if (parameters.contains(QStringLiteral("osm.mapping.offline.directory")))
m_offlineDirectory = parameters.value(QStringLiteral("osm.mapping.offline.directory")).toString();
-
- QAbstractGeoTileCache *tileCache = new QGeoFileTileCacheOsm(m_providers, m_offlineDirectory, m_cacheDirectory);
-
- // 50mb of disk cache by default to minimize n. of accesses to public OSM servers
- tileCache->setMaxDiskUsage(50 * 1024 * 1024);
+ QGeoFileTileCacheOsm *tileCache = new QGeoFileTileCacheOsm(m_providers, m_offlineDirectory, m_cacheDirectory);
if (parameters.contains(QStringLiteral("osm.mapping.cache.disk.size"))) {
bool ok = false;
@@ -253,6 +258,15 @@ QGeoTiledMappingManagerEngineOsm::QGeoTiledMappingManagerEngineOsm(const QVarian
}
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);
+
*error = QGeoServiceProvider::NoError;
errorString->clear();
}
diff --git a/src/plugins/geoservices/osm/qgeotileproviderosm.cpp b/src/plugins/geoservices/osm/qgeotileproviderosm.cpp
index 0d99c828..1989c44f 100644
--- a/src/plugins/geoservices/osm/qgeotileproviderosm.cpp
+++ b/src/plugins/geoservices/osm/qgeotileproviderosm.cpp
@@ -43,6 +43,7 @@
QT_BEGIN_NAMESPACE
static const int maxValidZoom = 30;
+static const QDateTime defaultTs = QDateTime::fromString(QStringLiteral("2016-06-01T00:00:00"), Qt::ISODate);
QGeoTileProviderOsm::QGeoTileProviderOsm(QNetworkAccessManager *nm,
const QGeoMapType &mapType,
@@ -120,6 +121,13 @@ bool QGeoTileProviderOsm::isHighDpi() const
return m_provider->isHighDpi();
}
+const QDateTime QGeoTileProviderOsm::timestamp() const
+{
+ if (!m_provider)
+ return QDateTime();
+ return m_provider->timestamp();
+}
+
const QGeoMapType &QGeoTileProviderOsm::mapType() const
{
return m_mapType;
@@ -190,8 +198,10 @@ void QGeoTileProviderOsm::onResolutionError(TileProvider *provider)
m_provider = p;
if (!p->isValid()) {
m_status = Idle;
-// m_status = Resolving;
-// p->resolveProvider();
+#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;
@@ -204,7 +214,9 @@ void QGeoTileProviderOsm::onResolutionError(TileProvider *provider)
emit resolutionFinished(this);
} else { // still not resolved. But network error is recoverable.
m_status = Idle;
- //m_provider->resolveProvider();
+#if 0 // leaving triggering the retry to the tile fetcher
+ m_provider->resolveProvider();
+#endif
}
}
@@ -226,7 +238,7 @@ void QGeoTileProviderOsm::addProvider(TileProvider *provider)
/*
- QGeoTileProviderOsm::TileProvder
+ Class TileProvder
*/
static void sort2(int &a, int &b)
@@ -238,13 +250,13 @@ static void sort2(int &a, int &b)
}
}
-TileProvider::TileProvider() : m_status(Invalid), m_nm(nullptr), m_highDpi(false)
+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_highDpi(highDpi)
+: m_status(Idle), m_urlRedirector(urlRedirector), m_nm(nullptr), m_timestamp(defaultTs), m_highDpi(highDpi)
{
if (!m_urlRedirector.isValid())
m_status = Invalid;
@@ -259,7 +271,7 @@ TileProvider::TileProvider(const QString &urlTemplate,
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_highDpi(highDpi)
+ m_minimumZoomLevel(minimumZoomLevel), m_maximumZoomLevel(maximumZoomLevel), m_timestamp(defaultTs), m_highDpi(highDpi)
{
setupProvider();
}
@@ -352,6 +364,7 @@ void TileProvider::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
@@ -361,27 +374,31 @@ void TileProvider::onNetworkReplyFinished()
* 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;
@@ -435,6 +452,10 @@ void TileProvider::onNetworkReplyFinished()
if (maxZoom.isDouble())
m_maximumZoomLevel = qBound(0, int(maxZoom.toDouble()), maxValidZoom);
+ const QJsonValue ts = json.value(QLatin1String("Timestamp"));
+ if (ts.isString())
+ m_timestamp = QDateTime::fromString(ts.toString(), Qt::ISODate);
+
setupProvider();
if (isValid()) {
QObject::disconnect(errorEmitterConnection);
@@ -553,6 +574,11 @@ int TileProvider::maximumZoomLevel() const
return m_maximumZoomLevel;
}
+const QDateTime &TileProvider::timestamp() const
+{
+ return m_timestamp;
+}
+
bool TileProvider::isHighDpi() const
{
return m_highDpi;
@@ -563,6 +589,11 @@ 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)
@@ -591,7 +622,3 @@ TileProvider::Status TileProvider::status() const
QT_END_NAMESPACE
-
-
-
-
diff --git a/src/plugins/geoservices/osm/qgeotileproviderosm.h b/src/plugins/geoservices/osm/qgeotileproviderosm.h
index cea832f0..b8647244 100644
--- a/src/plugins/geoservices/osm/qgeotileproviderosm.h
+++ b/src/plugins/geoservices/osm/qgeotileproviderosm.h
@@ -48,6 +48,7 @@
#include <algorithm>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
+#include <QDateTime>
QT_BEGIN_NAMESPACE
@@ -90,11 +91,13 @@ public:
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;
// Optional properties, not needed to construct a provider
void setStyleCopyRight(const QString &copyright);
+ void setTimestamp(const QDateTime &timestamp);
Status m_status;
QUrl m_urlRedirector; // The URL from where to fetch the URL template in case of a provider to resolve.
@@ -108,6 +111,7 @@ public:
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
@@ -152,6 +156,7 @@ public:
const QGeoMapType &mapType() const;
bool isValid() const;
bool isResolved() const;
+ const QDateTime timestamp() const;
Q_SIGNALS:
void resolutionFinished(const QGeoTileProviderOsm *provider);