Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qgeofiletilecache.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 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
4
5#include "qgeotilespec_p.h"
6
8
9#include <QDir>
10#include <QStandardPaths>
11#include <QMetaType>
12#include <QPixmap>
13#include <QDebug>
14
16
31
32void QCache3QTileEvictionPolicy::aboutToBeRemoved(const QGeoTileSpec &key, QSharedPointer<QGeoCachedTileDisk> obj)
33{
35 // set the cache pointer to zero so we can't call evictFromDiskCache
36 obj->cache = nullptr;
37}
38
39void QCache3QTileEvictionPolicy::aboutToBeEvicted(const QGeoTileSpec &key, QSharedPointer<QGeoCachedTileDisk> obj)
40{
43 // leave the pointer set if it's a real eviction
44}
45
51
56
58{
59 const QString basePath = baseCacheDirectory() + QLatin1String("QtLocation/");
60
61 // delete old tiles from QtLocation 5.7 or prior
62 // Newer version use plugin-specific subdirectories, versioned with qt version so those are not affected.
63 // TODO Remove cache cleanup in Qt 6
64 QDir baseDir(basePath);
65 if (baseDir.exists()) {
66 const QStringList oldCacheFiles = baseDir.entryList(QDir::Files);
67 for (const QString& file : oldCacheFiles)
68 baseDir.remove(file);
69 const QStringList oldCacheDirs = { QStringLiteral("osm"), QStringLiteral("mapbox"), QStringLiteral("here") };
70 for (const QString& d : oldCacheDirs) {
71 QDir oldCacheDir(basePath + QLatin1Char('/') + d);
72 if (oldCacheDir.exists())
73 oldCacheDir.removeRecursively();
74 }
75 }
76
77 if (directory_.isEmpty()) {
79 qWarning() << "Plugin uses uninitialized QGeoFileTileCache directory which was deleted during startup";
80 }
81
82 const bool directoryCreated = QDir::root().mkpath(directory_);
83 if (!directoryCreated)
84 qWarning() << "Failed to create cache directory " << directory_;
85
86 // default values
87 if (!isDiskCostSet_) { // If setMaxDiskUsage has not been called yet
89 setMaxDiskUsage(50 * 1024 * 1024);
90 else
91 setMaxDiskUsage(1000);
92 }
93
94 if (!isMemoryCostSet_) { // If setMaxMemoryUsage has not been called yet
96 setMaxMemoryUsage(3 * 1024 * 1024);
97 else
99 }
100
101 if (!isTextureCostSet_) { // If setExtraTextureUsage has not been called yet
103 setExtraTextureUsage(6 * 1024 * 1024);
104 else
105 setExtraTextureUsage(30); // byte size of texture is >> compressed image, hence unitary cost should be lower
106 }
107
108 loadTiles();
109}
110
112{
114 formats << QLatin1String("*.*");
115
117 const QStringList files = dir.entryList(formats, QDir::Files);
118#if 0 // workaround for QTBUG-60581
119 // Method:
120 // 1. read each queue file then, if each file exists, deserialize the data into the appropriate
121 // cache queue.
122 for (int i = 1; i<=4; i++) {
123 QString filename = dir.filePath(QString::fromLatin1("queue") + QString::number(i));
124 QFile file(filename);
126 continue;
127 QList<QSharedPointer<QGeoCachedTileDisk> > queue;
128 QList<QGeoTileSpec> specs;
129 QList<int> costs;
130 while (!file.atEnd()) {
131 QByteArray line = file.readLine().trimmed();
133 if (dir.exists(filename)){
134 files.removeOne(filename);
135 QGeoTileSpec spec = filenameToTileSpec(filename);
136 if (spec.zoom() == -1)
137 continue;
138 QSharedPointer<QGeoCachedTileDisk> tileDisk(new QGeoCachedTileDisk);
139 tileDisk->filename = dir.filePath(filename);
140 tileDisk->cache = this;
141 tileDisk->spec = spec;
142 QFileInfo fi(tileDisk->filename);
143 specs.append(spec);
144 queue.append(tileDisk);
146 costs.append(fi.size());
147 else
148 costs.append(1);
149
150 }
151 }
152
153 diskCache_.deserializeQueue(i, specs, queue, costs);
154 file.close();
155 }
156#endif
157 // 2. remaining tiles that aren't registered in a queue get pushed into cache here
158 // this is a backup, in case the queue manifest files get deleted or out of sync due to
159 // the application not closing down properly
160 for (const auto &file : files) {
162 if (spec.zoom() == -1)
163 continue;
164 QString filename = dir.filePath(file);
165 addToDiskCache(spec, filename);
166 }
167}
168
170{
171#if 0 // workaround for QTBUG-60581
172 // write disk cache queues to disk
174 for (int i = 1; i<=4; i++) {
175 QString filename = dir.filePath(QString::fromLatin1("queue") + QString::number(i));
176 QFile file(filename);
178 qWarning() << "Unable to write tile cache file " << filename;
179 continue;
180 }
181 QList<QSharedPointer<QGeoCachedTileDisk> > queue;
183 for (const QSharedPointer<QGeoCachedTileDisk> &tile : queue) {
184 if (tile.isNull())
185 continue;
186
187 // we just want the filename here, not the full path
188 int index = tile->filename.lastIndexOf(QLatin1Char('/'));
189 QByteArray filename = tile->filename.mid(index + 1).toLatin1() + '\n';
190 file.write(filename);
191 }
192 file.close();
193 }
194#endif
195}
196
203
209
211{
212 return diskCache_.maxCost();
213}
214
216{
217 return diskCache_.totalCost();
218}
219
225
227{
228 return memoryCache_.maxCost();
229}
230
232{
233 return memoryCache_.totalCost();
234}
235
242
248
250{
251 return textureCache_.maxCost();
252}
253
255{
256 return minTextureUsage_;
257}
258
259
261{
262 return textureCache_.totalCost();
263}
264
266{
271 dir.setNameFilters(QStringList() << QLatin1String("*-*-*-*.*"));
272 dir.setFilter(QDir::Files);
273 for (const QString &dirFile : dir.entryList()) {
274 dir.remove(dirFile);
275 }
276}
277
278void QGeoFileTileCache::clearMapId(const int mapId)
279{
280 for (const QGeoTileSpec &k : diskCache_.keys())
281 if (k.mapId() == mapId)
282 diskCache_.remove(k, true);
283 for (const QGeoTileSpec &k : memoryCache_.keys())
284 if (k.mapId() == mapId)
286 for (const QGeoTileSpec &k : textureCache_.keys())
287 if (k.mapId() == mapId)
289
290 // TODO: It seems the cache leaves residues, like some tiles do not get picked up.
291 // After the above calls, files that shouldnt be left behind are still on disk.
292 // Do an additional pass and make sure what has to be deleted gets deleted.
295 formats << QLatin1String("*.*");
296 const QStringList files = dir.entryList(formats, QDir::Files);
297 qWarning() << "Old tile data detected. Cache eviction left out "<< files.size() << "tiles";
298 for (const QString &tileFileName : files) {
299 QGeoTileSpec spec = filenameToTileSpec(tileFileName);
300 if (spec.mapId() != mapId)
301 continue;
302 QFile::remove(dir.filePath(tileFileName));
303 }
304}
305
310
315
320
325
330
335
336QSharedPointer<QGeoTileTexture> QGeoFileTileCache::get(const QGeoTileSpec &spec)
337{
338 QSharedPointer<QGeoTileTexture> tt = getFromMemory(spec);
339 if (tt)
340 return tt;
341 return getFromDisk(spec);
342}
343
345 const QByteArray &bytes,
346 const QString &format,
347 QAbstractGeoTileCache::CacheAreas areas)
348{
349 if (bytes.isEmpty())
350 return;
351
353 QString filename = tileSpecToFilename(spec, format, directory_);
354 addToDiskCache(spec, filename, bytes);
355 }
356
358 addToMemoryCache(spec, bytes, format);
359 }
360
361 /* inserts do not hit the texture cache -- this actually reduces overall
362 * cache hit rates because many tiles come too late to be useful
363 * and act as a poison */
364}
365
367{
368 QString filename = spec.plugin();
369 filename += QLatin1String("-");
370 filename += QString::number(spec.mapId());
371 filename += QLatin1String("-");
372 filename += QString::number(spec.zoom());
373 filename += QLatin1String("-");
374 filename += QString::number(spec.x());
375 filename += QLatin1String("-");
376 filename += QString::number(spec.y());
377
378 //Append version if real version number to ensure backwards compatibility and eviction of old tiles
379 if (spec.version() != -1) {
380 filename += QLatin1String("-");
381 filename += QString::number(spec.version());
382 }
383
384 filename += QLatin1String(".");
385 filename += format;
386
388
389 return dir.filePath(filename);
390}
391
393{
394 QGeoTileSpec emptySpec;
395
396 const QStringList parts = filename.split(QLatin1Char('.'));
397
398 if (parts.length() != 2)
399 return emptySpec;
400
401 const QString name = parts.at(0);
402 const QStringList fields = name.split(QLatin1Char('-'));
403
404 qsizetype length = fields.length();
405 if (length != 5 && length != 6)
406 return emptySpec;
407
408 QList<int> numbers;
409
410 bool ok = false;
411 for (qsizetype i = 1; i < length; ++i) {
412 ok = false;
413 int value = fields.at(i).toInt(&ok);
414 if (!ok)
415 return emptySpec;
416 numbers.append(value);
417 }
418
419 //File name without version, append default
420 if (numbers.length() < 5)
421 numbers.append(-1);
422
423 return QGeoTileSpec(fields.at(0),
424 numbers.at(0),
425 numbers.at(1),
426 numbers.at(2),
427 numbers.at(3),
428 numbers.at(4));
429}
430
435
439
440QSharedPointer<QGeoCachedTileDisk> QGeoFileTileCache::addToDiskCache(const QGeoTileSpec &spec, const QString &filename)
441{
442 QSharedPointer<QGeoCachedTileDisk> td(new QGeoCachedTileDisk);
443 td->spec = spec;
444 td->filename = filename;
445 td->cache = this;
446
447 int cost = 1;
449 QFileInfo fi(filename);
450 cost = fi.size();
451 }
452 diskCache_.insert(spec, td, cost);
453 return td;
454}
455
456bool QGeoFileTileCache::addToDiskCache(const QGeoTileSpec &spec, const QString &filename, const QByteArray &bytes)
457{
458 QSharedPointer<QGeoCachedTileDisk> td(new QGeoCachedTileDisk);
459 td->spec = spec;
460 td->filename = filename;
461 td->cache = this;
462
463 int cost = 1;
465 cost = bytes.size();
466
467 if (diskCache_.insert(spec, td, cost)) {
468 QFile file(filename);
470 file.write(bytes);
471 file.close();
472 return true;
473 }
474 return false;
475}
476
478{
479 if (isTileBogus(bytes))
480 return;
481
482 QSharedPointer<QGeoCachedTileMemory> tm(new QGeoCachedTileMemory);
483 tm->spec = spec;
484 tm->cache = this;
485 tm->bytes = bytes;
486 tm->format = format;
487
488 int cost = 1;
490 cost = bytes.size();
491 memoryCache_.insert(spec, tm, cost);
492}
493
494QSharedPointer<QGeoTileTexture> QGeoFileTileCache::addToTextureCache(const QGeoTileSpec &spec, const QImage &image)
495{
496 QSharedPointer<QGeoTileTexture> tt(new QGeoTileTexture);
497 tt->spec = spec;
498 tt->image = image;
499
500 int cost = 1;
502 cost = image.width() * image.height() * image.depth() / 8;
503 textureCache_.insert(spec, tt, cost);
504
505 return tt;
506}
507
508QSharedPointer<QGeoTileTexture> QGeoFileTileCache::getFromMemory(const QGeoTileSpec &spec)
509{
510 QSharedPointer<QGeoTileTexture> tt = textureCache_.object(spec);
511 if (tt)
512 return tt;
513
514 QSharedPointer<QGeoCachedTileMemory> tm = memoryCache_.object(spec);
515 if (tm) {
517 if (!image.loadFromData(tm->bytes)) {
518 handleError(spec, QLatin1String("Problem with tile image"));
519 return QSharedPointer<QGeoTileTexture>();
520 }
521 QSharedPointer<QGeoTileTexture> tt = addToTextureCache(spec, image);
522 if (tt)
523 return tt;
524 }
525 return QSharedPointer<QGeoTileTexture>();
526}
527
528QSharedPointer<QGeoTileTexture> QGeoFileTileCache::getFromDisk(const QGeoTileSpec &spec)
529{
530 QSharedPointer<QGeoCachedTileDisk> td = diskCache_.object(spec);
531 if (td) {
532 const QString format = QFileInfo(td->filename).suffix();
533 QFile file(td->filename);
535 QByteArray bytes = file.readAll();
536 file.close();
537
539 // Some tiles from the servers could be valid images but the tile fetcher
540 // might be able to recognize them as tiles that should not be shown.
541 // If that's the case, the tile fetcher should write "NoRetry" inside the file.
542 if (isTileBogus(bytes)) {
543 QSharedPointer<QGeoTileTexture> tt(new QGeoTileTexture);
544 tt->spec = spec;
545 tt->image = image;
546 return tt;
547 }
548
549 // This is a truly invalid image. The fetcher should try again.
550 if (!image.loadFromData(bytes)) {
551 handleError(spec, QLatin1String("Problem with tile image"));
552 return QSharedPointer<QGeoTileTexture>();
553 }
554
555 // Converting it here, instead of in each QSGTexture::bind()
558
559 addToMemoryCache(spec, bytes, format);
560 QSharedPointer<QGeoTileTexture> tt = addToTextureCache(td->spec, image);
561 if (tt)
562 return tt;
563 }
564
565 return QSharedPointer<QGeoTileTexture>();
566}
567
569{
570 if (bytes.size() == 7 && bytes == QByteArrayLiteral("NoRetry"))
571 return true;
572 return false;
573}
574
579
581{
582 return filenameToTileSpecDefault(filename);
583}
584
586{
587 return directory_;
588}
589
static QString baseCacheDirectory()
static QString baseLocationCacheDirectory()
virtual void handleError(const QGeoTileSpec &spec, const QString &errorString)
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
void aboutToBeEvicted(const QGeoTileSpec &key, QSharedPointer< QGeoCachedTileDisk > obj)
void aboutToBeRemoved(const QGeoTileSpec &key, QSharedPointer< QGeoCachedTileDisk > obj)
int totalCost() const
Definition qcache3q_p.h:121
void clear()
Definition qcache3q_p.h:270
int maxCost() const
Definition qcache3q_p.h:115
void deserializeQueue(int queueNumber, const QList< Key > &keys, const QList< QSharedPointer< T > > &values, const QList< int > &costs)
Definition qcache3q_p.h:191
bool insert(const Key &key, QSharedPointer< T > object, int cost=1)
Definition qcache3q_p.h:228
void remove(const Key &key, bool force=false)
Definition qcache3q_p.h:378
QSharedPointer< T > object(const Key &key) const
Definition qcache3q_p.h:398
void setMaxCost(int maxCost, int minRecent=-1, int maxOldPopular=-1)
Definition qcache3q_p.h:215
void printStats()
Definition qcache3q_p.h:153
QList< Key > keys() const
Definition qcache3q_p.h:392
void serializeQueue(int queueNumber, QList< QSharedPointer< T > > &buffer)
Definition qcache3q_p.h:179
\inmodule QtCore
Definition qdir.h:20
static QDir root()
Returns the root directory.
Definition qdir.h:224
@ Files
Definition qdir.h:23
bool atEnd() const override
Returns true if the end of the file has been reached; otherwise returns false.
void close() override
Calls QFileDevice::flush() and closes the file.
QString suffix() const
Returns the suffix (extension) of the file.
\inmodule QtCore
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:904
bool remove()
Removes the file specified by fileName().
Definition qfile.cpp:419
QGeoFileTileCache * cache
QGeoFileTileCache * cache
QSharedPointer< QGeoTileTexture > get(const QGeoTileSpec &spec) override
void setMaxMemoryUsage(int memoryUsage) override
CostStrategy costStrategyMemory_
void setMaxDiskUsage(int diskUsage) override
virtual QGeoTileSpec filenameToTileSpec(const QString &filename) const
int minTextureUsage() const override
QCache3Q< QGeoTileSpec, QGeoCachedTileMemory > memoryCache_
void setCostStrategyDisk(CostStrategy costStrategy) override
QSharedPointer< QGeoTileTexture > getFromDisk(const QGeoTileSpec &spec)
CostStrategy costStrategyDisk() const override
QSharedPointer< QGeoTileTexture > addToTextureCache(const QGeoTileSpec &spec, const QImage &image)
int maxDiskUsage() const override
QGeoFileTileCache(const QString &directory=QString(), QObject *parent=nullptr)
CostStrategy costStrategyTexture() const override
void clearAll() override
static void evictFromMemoryCache(QGeoCachedTileMemory *tm)
QCache3Q< QGeoTileSpec, QGeoTileTexture > textureCache_
void setCostStrategyTexture(CostStrategy costStrategy) override
int diskUsage() const override
void addToMemoryCache(const QGeoTileSpec &spec, const QByteArray &bytes, const QString &format)
CostStrategy costStrategyDisk_
QString directory() const
void setExtraTextureUsage(int textureUsage) override
int maxMemoryUsage() const override
void clearMapId(int mapId)
virtual bool isTileBogus(const QByteArray &bytes) const
static QString tileSpecToFilenameDefault(const QGeoTileSpec &spec, const QString &format, const QString &directory)
void setMinTextureUsage(int textureUsage) override
void insert(const QGeoTileSpec &spec, const QByteArray &bytes, const QString &format, QAbstractGeoTileCache::CacheAreas areas=QAbstractGeoTileCache::AllCaches) override
void setCostStrategyMemory(CostStrategy costStrategy) override
int textureUsage() const override
int maxTextureUsage() const override
static void evictFromDiskCache(QGeoCachedTileDisk *td)
virtual QString tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const
QCache3Q< QGeoTileSpec, QGeoCachedTileDisk, QCache3QTileEvictionPolicy > diskCache_
CostStrategy costStrategyMemory() const override
QSharedPointer< QGeoCachedTileDisk > addToDiskCache(const QGeoTileSpec &spec, const QString &filename)
QSharedPointer< QGeoTileTexture > getFromMemory(const QGeoTileSpec &spec)
int memoryUsage() const override
CostStrategy costStrategyTexture_
void printStats() override
static QGeoTileSpec filenameToTileSpecDefault(const QString &filename)
int x() const
int version() const
int zoom() const
int mapId() const
QString plugin() const
int y() const
qint64 readLine(char *data, qint64 maxlen)
This function reads a line of ASCII characters from the device, up to a maximum of maxSize - 1 bytes,...
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
\inmodule QtGui
Definition qimage.h:37
@ Format_RGB32
Definition qimage.h:46
@ Format_ARGB32_Premultiplied
Definition qimage.h:48
\inmodule QtCore
Definition qobject.h:103
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QByteArray toLatin1() const &
Definition qstring.h:630
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
Definition qstring.cpp:8218
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1246
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:8084
QString & remove(qsizetype i, qsizetype len)
Removes n characters from the string, starting at the given position index, and returns a reference t...
Definition qstring.cpp:3466
qsizetype length() const noexcept
Returns the number of characters in this string.
Definition qstring.h:191
EGLint EGLint * formats
Combined button and popup list for selecting options.
Definition image.cpp:4
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
QList< QString > QStringList
Constructs a string list that contains the given string, str.
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qWarning
Definition qlogging.h:166
GLuint64 key
GLuint index
[2]
GLenum GLuint GLenum GLsizei length
GLuint name
GLint GLsizei GLsizei GLenum format
GLhandleARB obj
[2]
static qsizetype cost(const QPixmap &pixmap)
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define Q_UNUSED(x)
ptrdiff_t qsizetype
Definition qtypes.h:165
QFile file
[0]
QQueue< int > queue
[0]
QString dir
[11]
QStringList files
[8]
\inmodule QtCore \reentrant
Definition qchar.h:18