Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgeofiletilecacheosm.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include <QtLocation/private/qgeotilespec_p.h>
6#include <QDir>
7#include <QDirIterator>
8#include <QPair>
9#include <QDateTime>
10
12
13QGeoFileTileCacheOsm::QGeoFileTileCacheOsm(const QList<QGeoTileProviderOsm *> &providers,
14 const QString &offlineDirectory,
15 const QString &directory, QObject *parent)
16 : QGeoFileTileCache(directory, parent),
17 m_offlineDirectory(offlineDirectory),
18 m_offlineData(false),
19 m_providers(providers)
20{
21 m_highDpi.resize(providers.size());
22 if (!offlineDirectory.isEmpty()) {
23 m_offlineDirectory = QDir(offlineDirectory);
24 if (m_offlineDirectory.exists())
25 m_offlineData = true;
26 }
27 for (int i = 0; i < providers.size(); i++) {
28 providers[i]->setParent(this);
29 m_highDpi[i] = providers[i]->isHighDpi();
30 connect(providers[i], &QGeoTileProviderOsm::resolutionFinished, this, &QGeoFileTileCacheOsm::onProviderResolutionFinished);
31 connect(providers[i], &QGeoTileProviderOsm::resolutionError, this, &QGeoFileTileCacheOsm::onProviderResolutionFinished);
32 }
33}
34
38
40{
41 QSharedPointer<QGeoTileTexture> tt = getFromMemory(spec);
42 if (tt)
43 return tt;
44 if ((tt = getFromOfflineStorage(spec)))
45 return tt;
46 return getFromDisk(spec);
47}
48
49void QGeoFileTileCacheOsm::onProviderResolutionFinished(const QGeoTileProviderOsm *provider)
50{
52 Q_UNUSED(provider);
53 for (int i = 0; i < m_providers.size(); i++) {
54 if (m_providers[i]->isHighDpi() != m_highDpi[i]) { // e.g., HiDpi was requested but only LoDpi is available
55 int mapId = m_providers[i]->mapType().mapId();
56 m_highDpi[i] = m_providers[i]->isHighDpi();
57
58 // reload cache for mapId i
59 dropTiles(mapId);
60 loadTiles(mapId);
61
62 // send signal to clear scene in all maps created through this provider that use the reloaded tiles
63 emit mapDataUpdated(mapId);
64 }
65 }
66}
67
68// On resolution error the provider is removed.
69// This happens ONLY if there is no enabled hardcoded fallback for the mapId.
70// Hardcoded fallbacks also have a timestamp, that can get updated with Qt releases.
71void QGeoFileTileCacheOsm::onProviderResolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error)
72{
73 Q_UNUSED(error);
74 clearObsoleteTiles(provider); // this still removes tiles who happen to be older than qgeotileproviderosm.cpp defaultTs
75}
76
77// init() is always called before the provider resolution starts
79{
80 if (directory_.isEmpty())
81 directory_ = baseLocationCacheDirectory();
82 QDir::root().mkpath(directory_);
83
84 // find max mapId
85 int max = 0;
86 for (auto p: m_providers)
87 if (p->mapType().mapId() > max)
88 max = p->mapType().mapId();
89 // Create a mapId to maxTimestamp LUT..
90 m_maxMapIdTimestamps.resize(max+1); // initializes to invalid QDateTime
91
92 // .. by finding the newest file in each tileset (tileset = mapId).
93 QDir dir(directory_);
94 QStringList formats;
95 formats << QLatin1String("*.*");
96 QStringList files = dir.entryList(formats, QDir::Files);
97
98 for (const QString &tileFileName : files) {
99 QGeoTileSpec spec = filenameToTileSpec(tileFileName);
100 if (spec.zoom() == -1)
101 continue;
102 QFileInfo fi(dir.filePath(tileFileName));
103 if (fi.lastModified() > m_maxMapIdTimestamps[spec.mapId()])
104 m_maxMapIdTimestamps[spec.mapId()] = fi.lastModified();
105 }
106
107 // Base class ::init()
108 QGeoFileTileCache::init();
109
110 for (QGeoTileProviderOsm * p: m_providers)
111 clearObsoleteTiles(p);
112}
113
115{
116 if (!m_offlineData)
117 return QSharedPointer<QGeoTileTexture>();
118
119 int providerId = spec.mapId() - 1;
120 if (providerId < 0 || providerId >= m_providers.size())
121 return QSharedPointer<QGeoTileTexture>();
122
123 const QString fileName = tileSpecToFilename(spec, QStringLiteral("*"), providerId);
124 QStringList validTiles = m_offlineDirectory.entryList({fileName});
125 if (!validTiles.size())
126 return QSharedPointer<QGeoTileTexture>();
127
128 QFile file(m_offlineDirectory.absoluteFilePath(validTiles.first()));
129 if (!file.open(QIODevice::ReadOnly))
130 return QSharedPointer<QGeoTileTexture>();
131 QByteArray bytes = file.readAll();
132 file.close();
133
134 QImage image;
135 if (!image.loadFromData(bytes)) {
136 handleError(spec, QLatin1String("Problem with tile image"));
137 return QSharedPointer<QGeoTileTexture>();
138 }
139
140 addToMemoryCache(spec, bytes, QString());
141 return addToTextureCache(spec, image);
142}
143
145{
146 QList<QGeoTileSpec> keys;
147 keys = textureCache_.keys();
148 for (const QGeoTileSpec &k : keys)
149 if (k.mapId() == mapId)
150 textureCache_.remove(k);
151
152 keys = memoryCache_.keys();
153 for (const QGeoTileSpec &k : keys)
154 if (k.mapId() == mapId)
155 memoryCache_.remove(k);
156
157 keys = diskCache_.keys();
158 for (const QGeoTileSpec &k : keys)
159 if (k.mapId() == mapId)
160 diskCache_.remove(k);
161}
162
164{
165 QStringList formats;
166 formats << QLatin1String("*.*");
167
168 QDir dir(directory_);
169 QStringList files = dir.entryList(formats, QDir::Files);
170
171 for (int i = 0; i < files.size(); ++i) {
172 QGeoTileSpec spec = filenameToTileSpec(files.at(i));
173 if (spec.zoom() == -1 || spec.mapId() != mapId)
174 continue;
175 QString filename = dir.filePath(files.at(i));
176 addToDiskCache(spec, filename);
177 }
178}
179
180QString QGeoFileTileCacheOsm::tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const
181{
182 int providerId = spec.mapId() - 1;
183 if (providerId < 0 || providerId >= m_providers.size())
184 return QString();
185
186 QDir dir = QDir(directory);
187 return dir.filePath(tileSpecToFilename(spec, format, providerId));
188}
189
190QString QGeoFileTileCacheOsm::tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, int providerId) const
191{
192 QString filename = spec.plugin();
193 filename += QLatin1String("-");
194 filename += (m_providers[providerId]->isHighDpi()) ? QLatin1Char('h') : QLatin1Char('l');
195 filename += QLatin1String("-");
196 filename += QString::number(spec.mapId());
197 filename += QLatin1String("-");
198 filename += QString::number(spec.zoom());
199 filename += QLatin1String("-");
200 filename += QString::number(spec.x());
201 filename += QLatin1String("-");
202 filename += QString::number(spec.y());
203
204 //Append version if real version number to ensure backwards compatibility and eviction of old tiles
205 if (spec.version() != -1) {
206 filename += QLatin1String("-");
207 filename += QString::number(spec.version());
208 }
209
210 filename += QLatin1String(".");
211 filename += format;
212 return filename;
213}
214
216{
217 QGeoTileSpec emptySpec;
218
219 QStringList parts = filename.split('.');
220
221 if (parts.length() != 2)
222 return emptySpec;
223
224 QString name = parts.at(0);
225 QStringList fields = name.split('-');
226
227 int length = fields.length();
228 if (length != 6 && length != 7)
229 return emptySpec;
230
231 QList<int> numbers;
232
233 bool ok = false;
234 for (int i = 2; i < length; ++i) {
235 ok = false;
236 int value = fields.at(i).toInt(&ok);
237 if (!ok)
238 return emptySpec;
239 numbers.append(value);
240 }
241
242 if (numbers.at(0) > m_providers.size())
243 return emptySpec;
244
245 bool highDpi = m_providers[numbers.at(0) - 1]->isHighDpi();
246 if (highDpi && fields.at(1) != QLatin1Char('h'))
247 return emptySpec;
248 else if (!highDpi && fields.at(1) != QLatin1Char('l'))
249 return emptySpec;
250
251 //File name without version, append default
252 if (numbers.length() < 5)
253 numbers.append(-1);
254
255 return QGeoTileSpec(fields.at(0),
256 numbers.at(0),
257 numbers.at(1),
258 numbers.at(2),
259 numbers.at(3),
260 numbers.at(4));
261}
262
264{
265 // process initialized providers, and connect the others
266
267 if (p->isResolved()) {
268 if (m_maxMapIdTimestamps[p->mapType().mapId()].isValid() && // there are tiles in the cache
269 p->timestamp() > m_maxMapIdTimestamps[p->mapType().mapId()]) { // and they are older than the provider
270 qInfo() << "provider for " << p->mapType().name() << " timestamp: " << p->timestamp()
271 << " -- data last modified: " << m_maxMapIdTimestamps[p->mapType().mapId()] << ". Clearing.";
272 clearMapId(p->mapType().mapId());
273 m_maxMapIdTimestamps[p->mapType().mapId()] = p->timestamp(); // don't do it again.
274 }
275 } else {
276 connect(p, &QGeoTileProviderOsm::resolutionFinished,
277 this, &QGeoFileTileCacheOsm::onProviderResolutionFinished);
278#if 0 // If resolution fails, better not try to remove anything. Beside, on error, resolutionFinished is also emitted.
279 connect(p, &QGeoTileProviderOsm::resolutionError,
280 this, &QGeoFileTileCacheOsm::onProviderResolutionError);
281#endif
282 }
283}
284
285QT_END_NAMESPACE
void onProviderResolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error)
QSharedPointer< QGeoTileTexture > getFromOfflineStorage(const QGeoTileSpec &spec)
QString tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const override
QGeoTileSpec filenameToTileSpec(const QString &filename) const override
QString tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, int providerId) const
QSharedPointer< QGeoTileTexture > get(const QGeoTileSpec &spec) override
void clearObsoleteTiles(const QGeoTileProviderOsm *p)