From bbc03ae59fc5fff64889d916098ac506526bd93d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20T=C5=AFma?= Date: Fri, 17 Nov 2023 17:39:33 +0100 Subject: [PATCH] Render MBTiles maps asynchronous if they include vector tiles --- src/map/mbtilesmap.cpp | 117 +++++++++++++++++++++++++---------------- src/map/mbtilesmap.h | 79 ++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+), 45 deletions(-) diff --git a/src/map/mbtilesmap.cpp b/src/map/mbtilesmap.cpp index 943f40c1..9d0f671b 100644 --- a/src/map/mbtilesmap.cpp +++ b/src/map/mbtilesmap.cpp @@ -4,8 +4,6 @@ #include #include #include -#include -#include #include #include "common/util.h" #include "osm.h" @@ -14,37 +12,6 @@ #define META_TYPE(type) static_cast(type) -class MBTile -{ -public: - MBTile(int zoom, int scaledSize, const QPoint &xy, const QByteArray &data, - const QString &key) : _zoom(zoom), _scaledSize(scaledSize), _xy(xy), - _data(data), _key(key) {} - - const QPoint &xy() const {return _xy;} - const QString &key() const {return _key;} - const QPixmap &pixmap() const {return _pixmap;} - - void load() { - QByteArray z(QString::number(_zoom).toLatin1()); - - QBuffer buffer(&_data); - QImageReader reader(&buffer, z); - if (_scaledSize) - reader.setScaledSize(QSize(_scaledSize, _scaledSize)); - _pixmap = QPixmap::fromImage(reader.read()); - } - -private: - int _zoom; - int _scaledSize; - QPoint _xy; - QByteArray _data; - QString _key; - QPixmap _pixmap; -}; - - MBTilesMap::MBTilesMap(const QString &fileName, QObject *parent) : Map(fileName, parent), _mapRatio(1.0), _tileRatio(1.0), _scalable(false), _scaledSize(0), _valid(false) @@ -183,6 +150,7 @@ void MBTilesMap::load(const Projection &in, const Projection &out, void MBTilesMap::unload() { + cancelJobs(true); _db.close(); } @@ -219,12 +187,16 @@ qreal MBTilesMap::resolution(const QRectF &rect) int MBTilesMap::zoomIn() { + cancelJobs(false); + _zi = qMin(_zi + 1, _zooms.size() - 1); return _zi; } int MBTilesMap::zoomOut() { + cancelJobs(false); + _zi = qMax(_zi - 1, 0); return _zi; } @@ -260,6 +232,53 @@ QByteArray MBTilesMap::tileData(int zoom, const QPoint &tile) const return QByteArray(); } +bool MBTilesMap::isRunning(const QString &key) const +{ + for (int i = 0; i < _jobs.size(); i++) { + const QList &tiles = _jobs.at(i)->tiles(); + for (int j = 0; j < tiles.size(); j++) + if (tiles.at(j).key() == key) + return true; + } + + return false; +} + +void MBTilesMap::runJob(MBTilesMapJob *job) +{ + _jobs.append(job); + + connect(job, &MBTilesMapJob::finished, this, &MBTilesMap::jobFinished); + job->run(); +} + +void MBTilesMap::removeJob(MBTilesMapJob *job) +{ + _jobs.removeOne(job); + job->deleteLater(); +} + +void MBTilesMap::jobFinished(MBTilesMapJob *job) +{ + const QList &tiles = job->tiles(); + + for (int i = 0; i < tiles.size(); i++) { + const MBTile &mt = tiles.at(i); + if (!mt.pixmap().isNull()) + QPixmapCache::insert(mt.key(), mt.pixmap()); + } + + removeJob(job); + + emit tilesLoaded(); +} + +void MBTilesMap::cancelJobs(bool wait) +{ + for (int i = 0; i < _jobs.size(); i++) + _jobs.at(i)->cancel(wait); +} + void MBTilesMap::draw(QPainter *painter, const QRectF &rect, Flags flags) { Q_UNUSED(flags); @@ -288,6 +307,9 @@ void MBTilesMap::draw(QPainter *painter, const QRectF &rect, Flags flags) QString key = path() + "-" + QString::number(zoom) + "_" + QString::number(t.x()) + "_" + QString::number(t.y()); + if (isRunning(key)) + continue; + if (QPixmapCache::find(key, &pm)) { QPointF tp(qMax(tl.x(), b.left()) + (t.x() - tile.x()) * tileSize(), qMax(tl.y(), b.top()) + (t.y() - tile.y()) @@ -300,21 +322,26 @@ void MBTilesMap::draw(QPainter *painter, const QRectF &rect, Flags flags) } } - QFuture future = QtConcurrent::map(tiles, &MBTile::load); - future.waitForFinished(); + if (!tiles.isEmpty()) { + if (flags & Map::Block || !_scalable) { + QFuture future = QtConcurrent::map(tiles, &MBTile::load); + future.waitForFinished(); - for (int i = 0; i < tiles.size(); i++) { - const MBTile &mt = tiles.at(i); - QPixmap pm(mt.pixmap()); - if (pm.isNull()) - continue; + for (int i = 0; i < tiles.size(); i++) { + const MBTile &mt = tiles.at(i); + QPixmap pm(mt.pixmap()); + if (pm.isNull()) + continue; - QPixmapCache::insert(mt.key(), pm); + QPixmapCache::insert(mt.key(), pm); - QPointF tp(qMax(tl.x(), b.left()) + (mt.xy().x() - tile.x()) - * tileSize(), qMax(tl.y(), b.top()) + (mt.xy().y() - tile.y()) - * tileSize()); - drawTile(painter, pm, tp); + QPointF tp(qMax(tl.x(), b.left()) + (mt.xy().x() - tile.x()) + * tileSize(), qMax(tl.y(), b.top()) + (mt.xy().y() - tile.y()) + * tileSize()); + drawTile(painter, pm, tp); + } + } else + runJob(new MBTilesMapJob(tiles)); } } diff --git a/src/map/mbtilesmap.h b/src/map/mbtilesmap.h index 28511739..667b9abb 100644 --- a/src/map/mbtilesmap.h +++ b/src/map/mbtilesmap.h @@ -3,10 +3,80 @@ #include #include +#include +#include +#include +#include #include "map.h" +class MBTile +{ +public: + MBTile(int zoom, int scaledSize, const QPoint &xy, const QByteArray &data, + const QString &key) : _zoom(zoom), _scaledSize(scaledSize), _xy(xy), + _data(data), _key(key) {} + + const QPoint &xy() const {return _xy;} + const QString &key() const {return _key;} + const QPixmap &pixmap() const {return _pixmap;} + + void load() { + QByteArray z(QString::number(_zoom).toLatin1()); + + QBuffer buffer(&_data); + QImageReader reader(&buffer, z); + if (_scaledSize) + reader.setScaledSize(QSize(_scaledSize, _scaledSize)); + _pixmap = QPixmap::fromImage(reader.read()); + } + +private: + int _zoom; + int _scaledSize; + QPoint _xy; + QByteArray _data; + QString _key; + QPixmap _pixmap; +}; + +class MBTilesMapJob : public QObject +{ + Q_OBJECT + +public: + MBTilesMapJob(const QList &tiles) : _tiles(tiles) {} + + void run() + { + connect(&_watcher, &QFutureWatcher::finished, this, + &MBTilesMapJob::handleFinished); + _future = QtConcurrent::map(_tiles, &MBTile::load); + _watcher.setFuture(_future); + } + void cancel(bool wait) + { + _future.cancel(); + if (wait) + _future.waitForFinished(); + } + const QList &tiles() const {return _tiles;} + +signals: + void finished(MBTilesMapJob *job); + +private slots: + void handleFinished() {emit finished(this);} + +private: + QFutureWatcher _watcher; + QFuture _future; + QList _tiles; +}; + class MBTilesMap : public Map { + Q_OBJECT + public: MBTilesMap(const QString &fileName, QObject *parent = 0); @@ -36,12 +106,19 @@ public: static Map *create(const QString &path, const Projection &proj, bool *isDir); +private slots: + void jobFinished(MBTilesMapJob *job); + private: qreal tileSize() const; qreal coordinatesRatio() const; qreal imageRatio() const; QByteArray tileData(int zoom, const QPoint &tile) const; void drawTile(QPainter *painter, QPixmap &pixmap, QPointF &tp); + bool isRunning(const QString &key) const; + void runJob(MBTilesMapJob *job); + void removeJob(MBTilesMapJob *job); + void cancelJobs(bool wait); QSqlDatabase _db; @@ -54,6 +131,8 @@ private: bool _scalable; int _scaledSize; + QList _jobs; + bool _valid; QString _errorString; };