Commit ef3b73b8 authored by Volker Krause's avatar Volker Krause
Browse files

Allow to control tile cache times externally

Needed for KDE Itinerary to preload map data for an entire trip.
parent 2f2d88a3
......@@ -13,6 +13,7 @@
#include <osm/osmpbfparser.h>
#include <osm/xmlparser.h>
#include <QDateTime>
#include <QElapsedTimer>
#include <QFile>
#include <QUrl>
......@@ -71,15 +72,21 @@ void MapLoader::loadFromFile(const QString &fileName)
void MapLoader::loadForCoordinate(double lat, double lon)
{
loadForCoordinate(lat, lon, {});
}
void MapLoader::loadForCoordinate(double lat, double lon, const QDateTime &ttl)
{
m_ttl = ttl;
m_tileBbox = {};
m_pendingTiles.clear();
m_boundarySearcher.init(OSM::Coordinate(lat, lon));
m_errorMessage.clear();
m_marbleMerger.setDataSet(&m_dataSet);
const auto tile = Tile::fromCoordinate(lat, lon, TileZoomLevel);
m_pendingTiles.push_back(tile);
auto tile = Tile::fromCoordinate(lat, lon, TileZoomLevel);
m_loadedTiles = QRect(tile.x, tile.y, 1, 1);
m_pendingTiles.push_back(std::move(tile));
downloadTiles();
}
......@@ -141,13 +148,13 @@ void MapLoader::loadTiles()
if (bbox.min.longitude < m_tileBbox.min.longitude) {
m_loadedTiles.setLeft(m_loadedTiles.left() - 1);
for (int y = m_loadedTiles.top(); y <= m_loadedTiles.bottom(); ++y) {
m_pendingTiles.push_back(Tile(m_loadedTiles.left(), y, TileZoomLevel));
m_pendingTiles.push_back(makeTile(m_loadedTiles.left(), y));
}
}
if (bbox.max.longitude > m_tileBbox.max.longitude) {
m_loadedTiles.setRight(m_loadedTiles.right() + 1);
for (int y = m_loadedTiles.top(); y <= m_loadedTiles.bottom(); ++y) {
m_pendingTiles.push_back(Tile(m_loadedTiles.right(), y, TileZoomLevel));
m_pendingTiles.push_back(makeTile(m_loadedTiles.right(), y));
}
}
......@@ -155,13 +162,13 @@ void MapLoader::loadTiles()
if (bbox.max.latitude > m_tileBbox.max.latitude) {
m_loadedTiles.setTop(m_loadedTiles.top() - 1);
for (int x = m_loadedTiles.left(); x <= m_loadedTiles.right(); ++x) {
m_pendingTiles.push_back(Tile(x, m_loadedTiles.top(), TileZoomLevel));
m_pendingTiles.push_back(makeTile(x, m_loadedTiles.top()));
}
}
if (bbox.min.latitude < m_tileBbox.min.latitude) {
m_loadedTiles.setBottom(m_loadedTiles.bottom() + 1);
for (int x = m_loadedTiles.left(); x <= m_loadedTiles.right(); ++x) {
m_pendingTiles.push_back(Tile(x, m_loadedTiles.bottom(), TileZoomLevel));
m_pendingTiles.push_back(makeTile(x, m_loadedTiles.bottom()));
}
}
......@@ -179,6 +186,13 @@ void MapLoader::loadTiles()
Q_EMIT done();
}
Tile MapLoader::makeTile(uint32_t x, uint32_t y) const
{
auto tile = Tile(x, y, TileZoomLevel);
tile.ttl = m_ttl;
return tile;
}
void MapLoader::downloadFailed(Tile tile, const QString& errorMessage)
{
Q_UNUSED(tile);
......
......@@ -15,6 +15,7 @@
#include <osm/datatypes.h>
#include <osm/datasetmergebuffer.h>
#include <QDateTime>
#include <QObject>
#include <QRect>
......@@ -36,6 +37,8 @@ public:
* This can involve online access.
*/
Q_INVOKABLE void loadForCoordinate(double lat, double lon);
/** Same as the above, but ensureing the requested data is cached until @p ttl. */
void loadForCoordinate(double lat, double lon, const QDateTime &ttl);
/** Take out the completely loaded result.
* Do this before loading the next map with the same loader.
......@@ -57,6 +60,7 @@ private:
void downloadFinished();
void downloadFailed(Tile tile, const QString &errorMessage);
void loadTiles();
Tile makeTile(uint32_t x, uint32_t y) const;
OSM::DataSet m_dataSet;
OSM::DataSetMergeBuffer m_mergeBuffer;
......@@ -67,6 +71,7 @@ private:
QRect m_loadedTiles;
std::vector<Tile> m_pendingTiles;
BoundarySearch m_boundarySearcher;
QDateTime m_ttl;
QString m_errorMessage;
};
......
......@@ -23,6 +23,10 @@
using namespace KOSMIndoorMap;
enum {
DefaultCacheDays = 14,
};
Tile Tile::fromCoordinate(double lat, double lon, uint8_t z)
{
Tile t;
......@@ -78,10 +82,15 @@ QString TileCache::cachedTile(Tile tile) const
void TileCache::ensureCached(Tile tile)
{
if (!cachedTile(tile).isEmpty()) {
const auto t = cachedTile(tile);
if (t.isEmpty()) {
downloadTile(tile);
return;
}
downloadTile(tile);
if (tile.ttl.isValid()) {
updateTtl(t, tile.ttl);
}
}
void TileCache::downloadTile(Tile tile)
......@@ -160,7 +169,13 @@ void TileCache::downloadFinished(QNetworkReply* reply, Tile tile)
return;
}
m_output.rename(cachePath(tile));
const auto t = cachePath(tile);
m_output.rename(t);
if (tile.ttl.isValid()) {
updateTtl(t, tile.ttl);
} else {
updateTtl(t, QDateTime::currentDateTimeUtc().addDays(DefaultCacheDays));
}
Q_EMIT tileLoaded(tile);
downloadNext();
......@@ -188,7 +203,7 @@ static void expireRecursive(const QString &path)
qCDebug(Log) << "removing empty tile directory" << it.fileName();
QDir(path).rmdir(it.filePath());
}
} else if (it.fileInfo().lastModified().daysTo(QDateTime::currentDateTimeUtc()) > 14) {
} else if (it.fileInfo().lastModified() < QDateTime::currentDateTimeUtc()) {
qCDebug(Log) << "removing expired tile" << it.filePath();
QDir(path).remove(it.filePath());
}
......@@ -199,3 +214,10 @@ void TileCache::expire()
const QString base = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/org.kde.osm/vectorosm/");
expireRecursive(base);
}
void TileCache::updateTtl(const QString &filePath, const QDateTime &ttl)
{
QFile f(filePath);
f.open(QFile::WriteOnly | QFile::Append);
f.setFileTime(std::max(f.fileTime(QFileDevice::FileModificationTime), ttl), QFile::FileModificationTime);
}
......@@ -7,6 +7,7 @@
#ifndef KOSMINDOORMAP_TILECACHE_H
#define KOSMINDOORMAP_TILECACHE_H
#include <QDateTime>
#include <QFile>
#include <QObject>
......@@ -28,8 +29,8 @@ namespace KOSMIndoorMap {
class Tile
{
public:
constexpr inline Tile() = default;
explicit inline constexpr Tile(uint32_t _x, uint32_t _y, uint8_t _z)
inline Tile() = default;
explicit inline Tile(uint32_t _x, uint32_t _y, uint8_t _z)
: x(_x) , y(_y) , z(_z) {}
static Tile fromCoordinate(double lat, double lon, uint8_t z);
......@@ -40,6 +41,7 @@ public:
uint32_t x = 0;
uint32_t y = 0;
uint8_t z = 0;
QDateTime ttl;
};
/** OSM vector tile downloading and cache management. */
......@@ -77,6 +79,7 @@ private:
void downloadNext();
void dataReceived(QNetworkReply *reply);
void downloadFinished(QNetworkReply *reply, Tile tile);
void updateTtl(const QString &filePath, const QDateTime &ttl);
QNetworkAccessManager *m_nam;
QFile m_output;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment