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