From 77e9fae19d4ad8b475d2b2dea18336ce436b6ca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20T=C5=AFma?= Date: Mon, 11 Dec 2023 20:11:16 +0100 Subject: [PATCH] Asynchronous rendering of online vector maps --- src/map/onlinemap.cpp | 105 +++++++++++++++++++++++++++++++++--------- src/map/onlinemap.h | 60 +++++++++++++++++++++--- 2 files changed, 136 insertions(+), 29 deletions(-) diff --git a/src/map/onlinemap.cpp b/src/map/onlinemap.cpp index eacdd322..f9b29620 100644 --- a/src/map/onlinemap.cpp +++ b/src/map/onlinemap.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include "common/rectc.h" #include "common/programpaths.h" #include "common/downloader.h" @@ -11,18 +10,13 @@ #define MAX_OVERZOOM 3 -static QString cacheName(const QString &file, unsigned overzoom) -{ - return overzoom ? file + ":" + QString::number(overzoom) : file; -} - OnlineMap::OnlineMap(const QString &fileName, const QString &name, const QString &url, const Range &zooms, const RectC &bounds, qreal tileRatio, const QList &headers, int tileSize, bool scalable, bool invertY, bool quadTiles, QObject *parent) : Map(fileName, parent), _name(name), _zooms(zooms), _bounds(bounds), _zoom(_zooms.max()), _tileSize(tileSize), _base(0), _mapRatio(1.0), - _tileRatio(tileRatio), _scalable(scalable), _invertY(invertY) + _tileRatio(tileRatio), _scalable(scalable), _scaledSize(0), _invertY(invertY) { _tileLoader = new TileLoader(QDir(ProgramPaths::tilesDir()).filePath(_name), this); @@ -72,12 +66,16 @@ qreal OnlineMap::resolution(const QRectF &rect) int OnlineMap::zoomIn() { + cancelJobs(false); + _zoom = qMin(_zoom + 1, _zooms.max()); return _zoom; } int OnlineMap::zoomOut() { + cancelJobs(false); + _zoom = qMax(_zoom - 1, _zooms.min()); return _zoom; } @@ -96,6 +94,11 @@ void OnlineMap::load(const Projection &in, const Projection &out, } } +void OnlineMap::unload() +{ + cancelJobs(true); +} + qreal OnlineMap::coordinatesRatio() const { return _mapRatio > 1.0 ? _mapRatio / _tileRatio : 1.0; @@ -116,6 +119,53 @@ QPoint OnlineMap::tileCoordinates(int x, int y, int zoom) return QPoint(x, _invertY ? (1< &tiles = _jobs.at(i)->tiles(); + for (int j = 0; j < tiles.size(); j++) + if (tiles.at(j).key() == key) + return true; + } + + return false; +} + +void OnlineMap::runJob(OnlineMapJob *job) +{ + _jobs.append(job); + + connect(job, &OnlineMapJob::finished, this, &OnlineMap::jobFinished); + job->run(); +} + +void OnlineMap::removeJob(OnlineMapJob *job) +{ + _jobs.removeOne(job); + job->deleteLater(); +} + +void OnlineMap::jobFinished(OnlineMapJob *job) +{ + const QList &tiles = job->tiles(); + + for (int i = 0; i < tiles.size(); i++) { + const OnlineMapTile &mt = tiles.at(i); + if (!mt.pixmap().isNull()) + QPixmapCache::insert(mt.key(), mt.pixmap()); + } + + removeJob(job); + + emit tilesLoaded(); +} + +void OnlineMap::cancelJobs(bool wait) +{ + for (int i = 0; i < _jobs.size(); i++) + _jobs.at(i)->cancel(wait); +} + void OnlineMap::draw(QPainter *painter, const QRectF &rect, Flags flags) { int base = _scalable ? qMin(_base, _zoom) : _zoom; @@ -145,36 +195,47 @@ void OnlineMap::draw(QPainter *painter, const QRectF &rect, Flags flags) else _tileLoader->loadTilesAsync(fetchTiles); - QList renderTiles; + QList renderTiles; for (int i = 0; i < fetchTiles.count(); i++) { const TileLoader::Tile &t = fetchTiles.at(i); if (t.file().isNull()) continue; + QString key(overzoom + ? t.file() + ":" + QString::number(overzoom) : t.file()); + if (isRunning(key)) + continue; + QPixmap pm; - if (QPixmapCache::find(cacheName(t.file(), overzoom), &pm)) { + if (QPixmapCache::find(key, &pm)) { QPointF tp(tl.x() + (t.xy().x() - tile.x()) * tileSize() * f, tl.y() + (t.xy().y() - tile.y()) * tileSize() * f); drawTile(painter, pm, tp); } else - renderTiles.append(OnlineTile(t.xy(), t.file(), _zoom, overzoom, - _scaledSize)); + renderTiles.append(OnlineMapTile(t.xy(), t.file(), _zoom, overzoom, + _scaledSize, key)); } - QFuture future = QtConcurrent::map(renderTiles, &OnlineTile::load); - future.waitForFinished(); + if (!renderTiles.isEmpty()) { + if (flags & Map::Block || !_scalable) { + QFuture future = QtConcurrent::map(renderTiles, + &OnlineMapTile::load); + future.waitForFinished(); - for (int i = 0; i < renderTiles.size(); i++) { - const OnlineTile &mt = renderTiles.at(i); - QPixmap pm(mt.pixmap()); - if (pm.isNull()) - continue; + for (int i = 0; i < renderTiles.size(); i++) { + const OnlineMapTile &mt = renderTiles.at(i); + QPixmap pm(mt.pixmap()); + if (pm.isNull()) + continue; - QPixmapCache::insert(cacheName(mt.file(), overzoom), pm); + QPixmapCache::insert(mt.key(), pm); - QPointF tp(tl.x() + (mt.xy().x() - tile.x()) * tileSize() * f, - tl.y() + (mt.xy().y() - tile.y()) * tileSize() * f); - drawTile(painter, pm, tp); + QPointF tp(tl.x() + (mt.xy().x() - tile.x()) * tileSize() * f, + tl.y() + (mt.xy().y() - tile.y()) * tileSize() * f); + drawTile(painter, pm, tp); + } + } else + runJob(new OnlineMapJob(renderTiles)); } } diff --git a/src/map/onlinemap.h b/src/map/onlinemap.h index 9821bec0..94f6b21a 100644 --- a/src/map/onlinemap.h +++ b/src/map/onlinemap.h @@ -3,17 +3,18 @@ #include #include +#include #include "common/range.h" #include "common/rectc.h" #include "map.h" #include "tileloader.h" -class OnlineTile +class OnlineMapTile { public: - OnlineTile(const QPoint &xy, const QString &file, int zoom, int overzoom, - int scaledSize) : _xy(xy), _file(file), _zoom(zoom), _overzoom(overzoom), - _scaledSize(scaledSize) {} + OnlineMapTile(const QPoint &xy, const QString &file, int zoom, int overzoom, + int scaledSize, const QString &key) : _zoom(zoom), _overzoom(overzoom), + _scaledSize(scaledSize), _xy(xy), _file(file), _key(key) {} void load() { @@ -27,18 +28,53 @@ public: } const QPoint &xy() const {return _xy;} - const QString &file() const {return _file;} const QPixmap &pixmap() const {return _pixmap;} + const QString &key() const {return _key;} private: - QPoint _xy; - QString _file; int _zoom; int _overzoom; int _scaledSize; + QPoint _xy; + QString _file; + QString _key; QPixmap _pixmap; }; +class OnlineMapJob : public QObject +{ + Q_OBJECT + +public: + OnlineMapJob(const QList &tiles) : _tiles(tiles) {} + + void run() + { + connect(&_watcher, &QFutureWatcher::finished, this, + &OnlineMapJob::handleFinished); + _future = QtConcurrent::map(_tiles, &OnlineMapTile::load); + _watcher.setFuture(_future); + } + void cancel(bool wait) + { + _future.cancel(); + if (wait) + _future.waitForFinished(); + } + const QList &tiles() const {return _tiles;} + +signals: + void finished(OnlineMapJob *job); + +private slots: + void handleFinished() {emit finished(this);} + +private: + QFutureWatcher _watcher; + QFuture _future; + QList _tiles; +}; + class OnlineMap : public Map { Q_OBJECT @@ -68,8 +104,12 @@ public: void load(const Projection &in, const Projection &out, qreal deviceRatio, bool hidpi); + void unload(); void clearCache(); +private slots: + void jobFinished(OnlineMapJob *job); + private: int limitZoom(int zoom) const; qreal tileSize() const; @@ -77,6 +117,10 @@ private: qreal imageRatio() const; QPoint tileCoordinates(int x, int y, int zoom); void drawTile(QPainter *painter, QPixmap &pixmap, QPointF &tp); + bool isRunning(const QString &key) const; + void runJob(OnlineMapJob *job); + void removeJob(OnlineMapJob *job); + void cancelJobs(bool wait); TileLoader *_tileLoader; QString _name; @@ -89,6 +133,8 @@ private: bool _scalable; int _scaledSize; bool _invertY; + + QList _jobs; }; #endif // ONLINEMAP_H