1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-11-28 05:34:47 +01:00

Asynchronous rendering of online vector maps

This commit is contained in:
Martin Tůma 2023-12-11 20:11:16 +01:00
parent 148fc76d5a
commit 77e9fae19d
2 changed files with 136 additions and 29 deletions

View File

@ -1,7 +1,6 @@
#include <QPainter> #include <QPainter>
#include <QDir> #include <QDir>
#include <QPixmapCache> #include <QPixmapCache>
#include <QtConcurrent>
#include "common/rectc.h" #include "common/rectc.h"
#include "common/programpaths.h" #include "common/programpaths.h"
#include "common/downloader.h" #include "common/downloader.h"
@ -11,18 +10,13 @@
#define MAX_OVERZOOM 3 #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, OnlineMap::OnlineMap(const QString &fileName, const QString &name,
const QString &url, const Range &zooms, const RectC &bounds, qreal tileRatio, const QString &url, const Range &zooms, const RectC &bounds, qreal tileRatio,
const QList<HTTPHeader> &headers, int tileSize, bool scalable, bool invertY, const QList<HTTPHeader> &headers, int tileSize, bool scalable, bool invertY,
bool quadTiles, QObject *parent) bool quadTiles, QObject *parent)
: Map(fileName, parent), _name(name), _zooms(zooms), _bounds(bounds), : Map(fileName, parent), _name(name), _zooms(zooms), _bounds(bounds),
_zoom(_zooms.max()), _tileSize(tileSize), _base(0), _mapRatio(1.0), _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), _tileLoader = new TileLoader(QDir(ProgramPaths::tilesDir()).filePath(_name),
this); this);
@ -72,12 +66,16 @@ qreal OnlineMap::resolution(const QRectF &rect)
int OnlineMap::zoomIn() int OnlineMap::zoomIn()
{ {
cancelJobs(false);
_zoom = qMin(_zoom + 1, _zooms.max()); _zoom = qMin(_zoom + 1, _zooms.max());
return _zoom; return _zoom;
} }
int OnlineMap::zoomOut() int OnlineMap::zoomOut()
{ {
cancelJobs(false);
_zoom = qMax(_zoom - 1, _zooms.min()); _zoom = qMax(_zoom - 1, _zooms.min());
return _zoom; return _zoom;
} }
@ -96,6 +94,11 @@ void OnlineMap::load(const Projection &in, const Projection &out,
} }
} }
void OnlineMap::unload()
{
cancelJobs(true);
}
qreal OnlineMap::coordinatesRatio() const qreal OnlineMap::coordinatesRatio() const
{ {
return _mapRatio > 1.0 ? _mapRatio / _tileRatio : 1.0; 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<<zoom) - y - 1 : y); return QPoint(x, _invertY ? (1<<zoom) - y - 1 : y);
} }
bool OnlineMap::isRunning(const QString &key) const
{
for (int i = 0; i < _jobs.size(); i++) {
const QList<OnlineMapTile> &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<OnlineMapTile> &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) void OnlineMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
{ {
int base = _scalable ? qMin(_base, _zoom) : _zoom; int base = _scalable ? qMin(_base, _zoom) : _zoom;
@ -145,37 +195,48 @@ void OnlineMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
else else
_tileLoader->loadTilesAsync(fetchTiles); _tileLoader->loadTilesAsync(fetchTiles);
QList<OnlineTile> renderTiles; QList<OnlineMapTile> renderTiles;
for (int i = 0; i < fetchTiles.count(); i++) { for (int i = 0; i < fetchTiles.count(); i++) {
const TileLoader::Tile &t = fetchTiles.at(i); const TileLoader::Tile &t = fetchTiles.at(i);
if (t.file().isNull()) if (t.file().isNull())
continue; continue;
QString key(overzoom
? t.file() + ":" + QString::number(overzoom) : t.file());
if (isRunning(key))
continue;
QPixmap pm; 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, QPointF tp(tl.x() + (t.xy().x() - tile.x()) * tileSize() * f,
tl.y() + (t.xy().y() - tile.y()) * tileSize() * f); tl.y() + (t.xy().y() - tile.y()) * tileSize() * f);
drawTile(painter, pm, tp); drawTile(painter, pm, tp);
} else } else
renderTiles.append(OnlineTile(t.xy(), t.file(), _zoom, overzoom, renderTiles.append(OnlineMapTile(t.xy(), t.file(), _zoom, overzoom,
_scaledSize)); _scaledSize, key));
} }
QFuture<void> future = QtConcurrent::map(renderTiles, &OnlineTile::load); if (!renderTiles.isEmpty()) {
if (flags & Map::Block || !_scalable) {
QFuture<void> future = QtConcurrent::map(renderTiles,
&OnlineMapTile::load);
future.waitForFinished(); future.waitForFinished();
for (int i = 0; i < renderTiles.size(); i++) { for (int i = 0; i < renderTiles.size(); i++) {
const OnlineTile &mt = renderTiles.at(i); const OnlineMapTile &mt = renderTiles.at(i);
QPixmap pm(mt.pixmap()); QPixmap pm(mt.pixmap());
if (pm.isNull()) if (pm.isNull())
continue; continue;
QPixmapCache::insert(cacheName(mt.file(), overzoom), pm); QPixmapCache::insert(mt.key(), pm);
QPointF tp(tl.x() + (mt.xy().x() - tile.x()) * tileSize() * f, QPointF tp(tl.x() + (mt.xy().x() - tile.x()) * tileSize() * f,
tl.y() + (mt.xy().y() - tile.y()) * tileSize() * f); tl.y() + (mt.xy().y() - tile.y()) * tileSize() * f);
drawTile(painter, pm, tp); drawTile(painter, pm, tp);
} }
} else
runJob(new OnlineMapJob(renderTiles));
}
} }
void OnlineMap::drawTile(QPainter *painter, QPixmap &pixmap, QPointF &tp) void OnlineMap::drawTile(QPainter *painter, QPixmap &pixmap, QPointF &tp)

View File

@ -3,17 +3,18 @@
#include <QImageReader> #include <QImageReader>
#include <QPixmap> #include <QPixmap>
#include <QtConcurrent>
#include "common/range.h" #include "common/range.h"
#include "common/rectc.h" #include "common/rectc.h"
#include "map.h" #include "map.h"
#include "tileloader.h" #include "tileloader.h"
class OnlineTile class OnlineMapTile
{ {
public: public:
OnlineTile(const QPoint &xy, const QString &file, int zoom, int overzoom, OnlineMapTile(const QPoint &xy, const QString &file, int zoom, int overzoom,
int scaledSize) : _xy(xy), _file(file), _zoom(zoom), _overzoom(overzoom), int scaledSize, const QString &key) : _zoom(zoom), _overzoom(overzoom),
_scaledSize(scaledSize) {} _scaledSize(scaledSize), _xy(xy), _file(file), _key(key) {}
void load() void load()
{ {
@ -27,18 +28,53 @@ public:
} }
const QPoint &xy() const {return _xy;} const QPoint &xy() const {return _xy;}
const QString &file() const {return _file;}
const QPixmap &pixmap() const {return _pixmap;} const QPixmap &pixmap() const {return _pixmap;}
const QString &key() const {return _key;}
private: private:
QPoint _xy;
QString _file;
int _zoom; int _zoom;
int _overzoom; int _overzoom;
int _scaledSize; int _scaledSize;
QPoint _xy;
QString _file;
QString _key;
QPixmap _pixmap; QPixmap _pixmap;
}; };
class OnlineMapJob : public QObject
{
Q_OBJECT
public:
OnlineMapJob(const QList<OnlineMapTile> &tiles) : _tiles(tiles) {}
void run()
{
connect(&_watcher, &QFutureWatcher<void>::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<OnlineMapTile> &tiles() const {return _tiles;}
signals:
void finished(OnlineMapJob *job);
private slots:
void handleFinished() {emit finished(this);}
private:
QFutureWatcher<void> _watcher;
QFuture<void> _future;
QList<OnlineMapTile> _tiles;
};
class OnlineMap : public Map class OnlineMap : public Map
{ {
Q_OBJECT Q_OBJECT
@ -68,8 +104,12 @@ public:
void load(const Projection &in, const Projection &out, qreal deviceRatio, void load(const Projection &in, const Projection &out, qreal deviceRatio,
bool hidpi); bool hidpi);
void unload();
void clearCache(); void clearCache();
private slots:
void jobFinished(OnlineMapJob *job);
private: private:
int limitZoom(int zoom) const; int limitZoom(int zoom) const;
qreal tileSize() const; qreal tileSize() const;
@ -77,6 +117,10 @@ private:
qreal imageRatio() const; qreal imageRatio() const;
QPoint tileCoordinates(int x, int y, int zoom); QPoint tileCoordinates(int x, int y, int zoom);
void drawTile(QPainter *painter, QPixmap &pixmap, QPointF &tp); 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; TileLoader *_tileLoader;
QString _name; QString _name;
@ -89,6 +133,8 @@ private:
bool _scalable; bool _scalable;
int _scaledSize; int _scaledSize;
bool _invertY; bool _invertY;
QList<OnlineMapJob*> _jobs;
}; };
#endif // ONLINEMAP_H #endif // ONLINEMAP_H