1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-11-24 11:45:53 +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 <QDir>
#include <QPixmapCache>
#include <QtConcurrent>
#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<HTTPHeader> &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<<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)
{
int base = _scalable ? qMin(_base, _zoom) : _zoom;
@ -145,37 +195,48 @@ void OnlineMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
else
_tileLoader->loadTilesAsync(fetchTiles);
QList<OnlineTile> renderTiles;
QList<OnlineMapTile> 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<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();
for (int i = 0; i < renderTiles.size(); i++) {
const OnlineTile &mt = renderTiles.at(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);
}
} else
runJob(new OnlineMapJob(renderTiles));
}
}
void OnlineMap::drawTile(QPainter *painter, QPixmap &pixmap, QPointF &tp)

View File

@ -3,17 +3,18 @@
#include <QImageReader>
#include <QPixmap>
#include <QtConcurrent>
#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<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
{
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<OnlineMapJob*> _jobs;
};
#endif // ONLINEMAP_H