Skip to content

Commit

Permalink
Asynchronous rendering of online vector maps
Browse files Browse the repository at this point in the history
  • Loading branch information
tumic0 committed Dec 11, 2023
1 parent 148fc76 commit 77e9fae
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 29 deletions.
105 changes: 83 additions & 22 deletions src/map/onlinemap.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include <QPainter>
#include <QDir>
#include <QPixmapCache>
#include <QtConcurrent>
#include "common/rectc.h"
#include "common/programpaths.h"
#include "common/downloader.h"
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -145,36 +195,47 @@ 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);
future.waitForFinished();
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);
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));
}
}

Expand Down
60 changes: 53 additions & 7 deletions src/map/onlinemap.h
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand All @@ -27,18 +28,53 @@ class OnlineTile
}

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
Expand Down Expand Up @@ -68,15 +104,23 @@ class OnlineMap : public Map

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;
qreal coordinatesRatio() const;
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;
Expand All @@ -89,6 +133,8 @@ class OnlineMap : public Map
bool _scalable;
int _scaledSize;
bool _invertY;

QList<OnlineMapJob*> _jobs;
};

#endif // ONLINEMAP_H

0 comments on commit 77e9fae

Please sign in to comment.