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

Render MBTiles maps asynchronous if they include vector tiles

This commit is contained in:
Martin Tůma 2023-11-17 17:39:33 +01:00
parent 69e1198efa
commit bbc03ae59f
2 changed files with 151 additions and 45 deletions

View File

@ -4,8 +4,6 @@
#include <QSqlError> #include <QSqlError>
#include <QPainter> #include <QPainter>
#include <QPixmapCache> #include <QPixmapCache>
#include <QImageReader>
#include <QBuffer>
#include <QtConcurrent> #include <QtConcurrent>
#include "common/util.h" #include "common/util.h"
#include "osm.h" #include "osm.h"
@ -14,37 +12,6 @@
#define META_TYPE(type) static_cast<QMetaType::Type>(type) #define META_TYPE(type) static_cast<QMetaType::Type>(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) MBTilesMap::MBTilesMap(const QString &fileName, QObject *parent)
: Map(fileName, parent), _mapRatio(1.0), _tileRatio(1.0), _scalable(false), : Map(fileName, parent), _mapRatio(1.0), _tileRatio(1.0), _scalable(false),
_scaledSize(0), _valid(false) _scaledSize(0), _valid(false)
@ -183,6 +150,7 @@ void MBTilesMap::load(const Projection &in, const Projection &out,
void MBTilesMap::unload() void MBTilesMap::unload()
{ {
cancelJobs(true);
_db.close(); _db.close();
} }
@ -219,12 +187,16 @@ qreal MBTilesMap::resolution(const QRectF &rect)
int MBTilesMap::zoomIn() int MBTilesMap::zoomIn()
{ {
cancelJobs(false);
_zi = qMin(_zi + 1, _zooms.size() - 1); _zi = qMin(_zi + 1, _zooms.size() - 1);
return _zi; return _zi;
} }
int MBTilesMap::zoomOut() int MBTilesMap::zoomOut()
{ {
cancelJobs(false);
_zi = qMax(_zi - 1, 0); _zi = qMax(_zi - 1, 0);
return _zi; return _zi;
} }
@ -260,6 +232,53 @@ QByteArray MBTilesMap::tileData(int zoom, const QPoint &tile) const
return QByteArray(); return QByteArray();
} }
bool MBTilesMap::isRunning(const QString &key) const
{
for (int i = 0; i < _jobs.size(); i++) {
const QList<MBTile> &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<MBTile> &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) void MBTilesMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
{ {
Q_UNUSED(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 key = path() + "-" + QString::number(zoom) + "_"
+ QString::number(t.x()) + "_" + QString::number(t.y()); + QString::number(t.x()) + "_" + QString::number(t.y());
if (isRunning(key))
continue;
if (QPixmapCache::find(key, &pm)) { if (QPixmapCache::find(key, &pm)) {
QPointF tp(qMax(tl.x(), b.left()) + (t.x() - tile.x()) QPointF tp(qMax(tl.x(), b.left()) + (t.x() - tile.x())
* tileSize(), qMax(tl.y(), b.top()) + (t.y() - tile.y()) * 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<void> future = QtConcurrent::map(tiles, &MBTile::load); if (!tiles.isEmpty()) {
future.waitForFinished(); if (flags & Map::Block || !_scalable) {
QFuture<void> future = QtConcurrent::map(tiles, &MBTile::load);
future.waitForFinished();
for (int i = 0; i < tiles.size(); i++) { for (int i = 0; i < tiles.size(); i++) {
const MBTile &mt = tiles.at(i); const MBTile &mt = tiles.at(i);
QPixmap pm(mt.pixmap()); QPixmap pm(mt.pixmap());
if (pm.isNull()) if (pm.isNull())
continue; continue;
QPixmapCache::insert(mt.key(), pm); QPixmapCache::insert(mt.key(), pm);
QPointF tp(qMax(tl.x(), b.left()) + (mt.xy().x() - tile.x()) QPointF tp(qMax(tl.x(), b.left()) + (mt.xy().x() - tile.x())
* tileSize(), qMax(tl.y(), b.top()) + (mt.xy().y() - tile.y()) * tileSize(), qMax(tl.y(), b.top()) + (mt.xy().y() - tile.y())
* tileSize()); * tileSize());
drawTile(painter, pm, tp); drawTile(painter, pm, tp);
}
} else
runJob(new MBTilesMapJob(tiles));
} }
} }

View File

@ -3,10 +3,80 @@
#include <QSqlDatabase> #include <QSqlDatabase>
#include <QVector> #include <QVector>
#include <QImageReader>
#include <QBuffer>
#include <QPixmap>
#include <QtConcurrent>
#include "map.h" #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<MBTile> &tiles) : _tiles(tiles) {}
void run()
{
connect(&_watcher, &QFutureWatcher<void>::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<MBTile> &tiles() const {return _tiles;}
signals:
void finished(MBTilesMapJob *job);
private slots:
void handleFinished() {emit finished(this);}
private:
QFutureWatcher<void> _watcher;
QFuture<void> _future;
QList<MBTile> _tiles;
};
class MBTilesMap : public Map class MBTilesMap : public Map
{ {
Q_OBJECT
public: public:
MBTilesMap(const QString &fileName, QObject *parent = 0); MBTilesMap(const QString &fileName, QObject *parent = 0);
@ -36,12 +106,19 @@ public:
static Map *create(const QString &path, const Projection &proj, bool *isDir); static Map *create(const QString &path, const Projection &proj, bool *isDir);
private slots:
void jobFinished(MBTilesMapJob *job);
private: private:
qreal tileSize() const; qreal tileSize() const;
qreal coordinatesRatio() const; qreal coordinatesRatio() const;
qreal imageRatio() const; qreal imageRatio() const;
QByteArray tileData(int zoom, const QPoint &tile) const; QByteArray tileData(int zoom, const QPoint &tile) const;
void drawTile(QPainter *painter, QPixmap &pixmap, QPointF &tp); 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; QSqlDatabase _db;
@ -54,6 +131,8 @@ private:
bool _scalable; bool _scalable;
int _scaledSize; int _scaledSize;
QList<MBTilesMapJob*> _jobs;
bool _valid; bool _valid;
QString _errorString; QString _errorString;
}; };