summaryrefslogtreecommitdiff
path: root/src/location/maps/qgeofiletilecache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/location/maps/qgeofiletilecache.cpp')
-rw-r--r--src/location/maps/qgeofiletilecache.cpp447
1 files changed, 447 insertions, 0 deletions
diff --git a/src/location/maps/qgeofiletilecache.cpp b/src/location/maps/qgeofiletilecache.cpp
new file mode 100644
index 00000000..4efe9696
--- /dev/null
+++ b/src/location/maps/qgeofiletilecache.cpp
@@ -0,0 +1,447 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 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 "qgeofiletilecache_p.h"
+
+#include "qgeotilespec_p.h"
+
+#include "qgeomappingmanager_p.h"
+
+#include <QDir>
+#include <QStandardPaths>
+#include <QMetaType>
+#include <QPixmap>
+#include <QDebug>
+
+Q_DECLARE_METATYPE(QList<QGeoTileSpec>)
+Q_DECLARE_METATYPE(QSet<QGeoTileSpec>)
+
+QT_BEGIN_NAMESPACE
+
+class QGeoCachedTileMemory
+{
+public:
+ ~QGeoCachedTileMemory()
+ {
+ if (cache)
+ cache->evictFromMemoryCache(this);
+ }
+
+ QGeoTileSpec spec;
+ QGeoFileTileCache *cache;
+ QByteArray bytes;
+ QString format;
+};
+
+void QCache3QTileEvictionPolicy::aboutToBeRemoved(const QGeoTileSpec &key, QSharedPointer<QGeoCachedTileDisk> obj)
+{
+ Q_UNUSED(key);
+ // set the cache pointer to zero so we can't call evictFromDiskCache
+ obj->cache = 0;
+}
+
+void QCache3QTileEvictionPolicy::aboutToBeEvicted(const QGeoTileSpec &key, QSharedPointer<QGeoCachedTileDisk> obj)
+{
+ Q_UNUSED(key);
+ Q_UNUSED(obj);
+ // leave the pointer set if it's a real eviction
+}
+
+QGeoCachedTileDisk::~QGeoCachedTileDisk()
+{
+ if (cache)
+ cache->evictFromDiskCache(this);
+}
+
+QGeoFileTileCache::QGeoFileTileCache(const QString &directory, QObject *parent)
+ : QAbstractGeoTileCache(parent), directory_(directory),
+ minTextureUsage_(0), extraTextureUsage_(0)
+{
+ const QString basePath = baseCacheDirectory();
+
+ // delete old tiles from QtLocation 5.4 or prior
+ // Newer version use plugin-specific subdirectories so those are not affected.
+ // TODO Remove cache cleanup in Qt 6
+ QDir baseDir(basePath);
+ if (baseDir.exists()) {
+ const QStringList oldCacheFiles = baseDir.entryList(QDir::Files);
+ foreach (const QString& file, oldCacheFiles)
+ baseDir.remove(file);
+ }
+
+ if (directory_.isEmpty()) {
+ directory_ = basePath;
+ qWarning() << "Plugin uses uninitialized QGeoFileTileCache directory which was deleted during startup";
+ }
+
+ QDir::root().mkpath(directory_);
+
+ // default values
+ setMaxDiskUsage(20 * 1024 * 1024);
+ setMaxMemoryUsage(3 * 1024 * 1024);
+ setExtraTextureUsage(6 * 1024 * 1024);
+
+ loadTiles();
+}
+
+void QGeoFileTileCache::loadTiles()
+{
+ QStringList formats;
+ formats << QLatin1String("*.*");
+
+ QDir dir(directory_);
+ QStringList files = dir.entryList(formats, QDir::Files);
+
+ // Method:
+ // 1. read each queue file then, if each file exists, deserialize the data into the appropriate
+ // cache queue.
+ for (int i = 1; i<=4; i++) {
+ QString filename = dir.filePath(QString::fromLatin1("queue") + QString::number(i));
+ QFile file(filename);
+ if (!file.open(QIODevice::ReadOnly))
+ continue;
+ QList<QSharedPointer<QGeoCachedTileDisk> > queue;
+ QList<QGeoTileSpec> specs;
+ QList<int> costs;
+ while (!file.atEnd()) {
+ QByteArray line = file.readLine().trimmed();
+ QString filename = QString::fromLatin1(line.constData(), line.length());
+ if (dir.exists(filename)){
+ files.removeOne(filename);
+ QGeoTileSpec spec = filenameToTileSpec(filename);
+ if (spec.zoom() == -1)
+ continue;
+ QSharedPointer<QGeoCachedTileDisk> tileDisk(new QGeoCachedTileDisk);
+ tileDisk->filename = dir.filePath(filename);
+ tileDisk->cache = this;
+ tileDisk->spec = spec;
+ QFileInfo fi(tileDisk->filename);
+ specs.append(spec);
+ queue.append(tileDisk);
+ costs.append(fi.size());
+ }
+ }
+
+ diskCache_.deserializeQueue(i, specs, queue, costs);
+ file.close();
+ }
+
+ // 2. remaining tiles that aren't registered in a queue get pushed into cache here
+ // this is a backup, in case the queue manifest files get deleted or out of sync due to
+ // the application not closing down properly
+ for (int i = 0; i < files.size(); ++i) {
+ QGeoTileSpec spec = filenameToTileSpec(files.at(i));
+ if (spec.zoom() == -1)
+ continue;
+ QString filename = dir.filePath(files.at(i));
+ addToDiskCache(spec, filename);
+ }
+}
+
+QGeoFileTileCache::~QGeoFileTileCache()
+{
+ // write disk cache queues to disk
+ QDir dir(directory_);
+ for (int i = 1; i<=4; i++) {
+ QString filename = dir.filePath(QString::fromLatin1("queue") + QString::number(i));
+ QFile file(filename);
+ if (!file.open(QIODevice::WriteOnly)){
+ qWarning() << "Unable to write tile cache file " << filename;
+ continue;
+ }
+ QList<QSharedPointer<QGeoCachedTileDisk> > queue;
+ diskCache_.serializeQueue(i, queue);
+ foreach (const QSharedPointer<QGeoCachedTileDisk> &tile, queue) {
+ if (tile.isNull())
+ continue;
+
+ // we just want the filename here, not the full path
+ int index = tile->filename.lastIndexOf(QLatin1Char('/'));
+ QByteArray filename = tile->filename.mid(index + 1).toLatin1() + '\n';
+ file.write(filename);
+ }
+ file.close();
+ }
+}
+
+void QGeoFileTileCache::printStats()
+{
+ textureCache_.printStats();
+ memoryCache_.printStats();
+ diskCache_.printStats();
+}
+
+void QGeoFileTileCache::setMaxDiskUsage(int diskUsage)
+{
+ diskCache_.setMaxCost(diskUsage);
+}
+
+int QGeoFileTileCache::maxDiskUsage() const
+{
+ return diskCache_.maxCost();
+}
+
+int QGeoFileTileCache::diskUsage() const
+{
+ return diskCache_.totalCost();
+}
+
+void QGeoFileTileCache::setMaxMemoryUsage(int memoryUsage)
+{
+ memoryCache_.setMaxCost(memoryUsage);
+}
+
+int QGeoFileTileCache::maxMemoryUsage() const
+{
+ return memoryCache_.maxCost();
+}
+
+int QGeoFileTileCache::memoryUsage() const
+{
+ return memoryCache_.totalCost();
+}
+
+void QGeoFileTileCache::setExtraTextureUsage(int textureUsage)
+{
+ extraTextureUsage_ = textureUsage;
+ textureCache_.setMaxCost(minTextureUsage_ + extraTextureUsage_);
+}
+
+void QGeoFileTileCache::setMinTextureUsage(int textureUsage)
+{
+ minTextureUsage_ = textureUsage;
+ textureCache_.setMaxCost(minTextureUsage_ + extraTextureUsage_);
+}
+
+int QGeoFileTileCache::maxTextureUsage() const
+{
+ return textureCache_.maxCost();
+}
+
+int QGeoFileTileCache::minTextureUsage() const
+{
+ return minTextureUsage_;
+}
+
+
+int QGeoFileTileCache::textureUsage() const
+{
+ return textureCache_.totalCost();
+}
+
+QSharedPointer<QGeoTileTexture> QGeoFileTileCache::get(const QGeoTileSpec &spec)
+{
+ QSharedPointer<QGeoTileTexture> tt = textureCache_.object(spec);
+ if (tt)
+ return tt;
+
+ QSharedPointer<QGeoCachedTileMemory> tm = memoryCache_.object(spec);
+ if (tm) {
+ QPixmap pixmap;
+ if (!pixmap.loadFromData(tm->bytes)) {
+ handleError(spec, QLatin1String("Problem with tile image"));
+ return QSharedPointer<QGeoTileTexture>(0);
+ }
+ QSharedPointer<QGeoTileTexture> tt = addToTextureCache(spec, pixmap);
+ if (tt)
+ return tt;
+ }
+
+ QSharedPointer<QGeoCachedTileDisk> td = diskCache_.object(spec);
+ if (td) {
+ const QString format = QFileInfo(td->filename).suffix();
+ QFile file(td->filename);
+ file.open(QIODevice::ReadOnly);
+ QByteArray bytes = file.readAll();
+ file.close();
+
+ QPixmap pixmap;
+ if (!pixmap.loadFromData(bytes)) {
+ handleError(spec, QLatin1String("Problem with tile image"));
+ return QSharedPointer<QGeoTileTexture>(0);
+ }
+
+ addToMemoryCache(spec, bytes, format);
+ QSharedPointer<QGeoTileTexture> tt = addToTextureCache(td->spec, pixmap);
+ if (tt)
+ return tt;
+ }
+
+ return QSharedPointer<QGeoTileTexture>();
+}
+
+void QGeoFileTileCache::insert(const QGeoTileSpec &spec,
+ const QByteArray &bytes,
+ const QString &format,
+ QGeoTiledMappingManagerEngine::CacheAreas areas)
+{
+ if (areas & QGeoTiledMappingManagerEngine::DiskCache) {
+ QString filename = tileSpecToFilename(spec, format, directory_);
+ QFile file(filename);
+ file.open(QIODevice::WriteOnly);
+ file.write(bytes);
+ file.close();
+
+ addToDiskCache(spec, filename);
+ }
+
+ if (areas & QGeoTiledMappingManagerEngine::MemoryCache) {
+ addToMemoryCache(spec, bytes, format);
+ }
+
+ /* inserts do not hit the texture cache -- this actually reduces overall
+ * cache hit rates because many tiles come too late to be useful
+ * and act as a poison */
+}
+
+void QGeoFileTileCache::evictFromDiskCache(QGeoCachedTileDisk *td)
+{
+ QFile::remove(td->filename);
+}
+
+void QGeoFileTileCache::evictFromMemoryCache(QGeoCachedTileMemory * /* tm */)
+{
+}
+
+QSharedPointer<QGeoCachedTileDisk> QGeoFileTileCache::addToDiskCache(const QGeoTileSpec &spec, const QString &filename)
+{
+ QSharedPointer<QGeoCachedTileDisk> td(new QGeoCachedTileDisk);
+ td->spec = spec;
+ td->filename = filename;
+ td->cache = this;
+
+ QFileInfo fi(filename);
+ int diskCost = fi.size();
+ diskCache_.insert(spec, td, diskCost);
+ return td;
+}
+
+QSharedPointer<QGeoCachedTileMemory> QGeoFileTileCache::addToMemoryCache(const QGeoTileSpec &spec, const QByteArray &bytes, const QString &format)
+{
+ QSharedPointer<QGeoCachedTileMemory> tm(new QGeoCachedTileMemory);
+ tm->spec = spec;
+ tm->cache = this;
+ tm->bytes = bytes;
+ tm->format = format;
+
+ int cost = bytes.size();
+ memoryCache_.insert(spec, tm, cost);
+
+ return tm;
+}
+
+QSharedPointer<QGeoTileTexture> QGeoFileTileCache::addToTextureCache(const QGeoTileSpec &spec, const QPixmap &pixmap)
+{
+ QSharedPointer<QGeoTileTexture> tt(new QGeoTileTexture);
+ tt->spec = spec;
+ tt->image = pixmap.toImage();
+
+ int textureCost = tt->image.width() * tt->image.height() * tt->image.depth() / 8;
+ textureCache_.insert(spec, tt, textureCost);
+
+ return tt;
+}
+
+QString QGeoFileTileCache::tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory)
+{
+ QString filename = spec.plugin();
+ 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 QGeoFileTileCache::filenameToTileSpec(const QString &filename)
+{
+ 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 != 5 && length != 6)
+ return emptySpec;
+
+ QList<int> numbers;
+
+ bool ok = false;
+ for (int i = 1; i < length; ++i) {
+ ok = false;
+ int value = fields.at(i).toInt(&ok);
+ if (!ok)
+ return emptySpec;
+ numbers.append(value);
+ }
+
+ //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));
+}
+
+QString QGeoFileTileCache::directory() const
+{
+ return directory_;
+}
+
+QT_END_NAMESPACE