1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-03-18 12:37:45 +01:00
GPXSee/src/map/mapsforgemap.cpp

225 lines
5.0 KiB
C++
Raw Normal View History

2021-04-10 15:27:40 +02:00
#include <QPainter>
2022-06-02 18:35:27 +02:00
#include <QPixmapCache>
2021-04-10 15:27:40 +02:00
#include "common/wgs84.h"
2022-11-04 09:03:36 +01:00
#include "common/util.h"
2021-04-10 15:27:40 +02:00
#include "rectd.h"
#include "pcs.h"
2021-04-10 15:27:40 +02:00
#include "mapsforgemap.h"
using namespace Mapsforge;
#define EPSILON 1e-6
2021-04-10 15:27:40 +02:00
MapsforgeMap::MapsforgeMap(const QString &fileName, QObject *parent)
2021-04-18 12:20:07 +02:00
: Map(fileName, parent), _data(fileName), _zoom(0),
_projection(PCS::pcs(3857)), _tileRatio(1.0)
2021-04-10 15:27:40 +02:00
{
if (_data.isValid())
_zoom = _data.zooms().min();
2021-04-10 15:27:40 +02:00
}
void MapsforgeMap::load(const Projection &in, const Projection &out,
qreal deviceRatio, bool hidpi)
2021-04-10 15:27:40 +02:00
{
Q_UNUSED(in);
Q_UNUSED(hidpi);
_tileRatio = deviceRatio;
_projection = out;
2021-04-10 15:27:40 +02:00
_data.load();
_style.load(_data, _tileRatio);
updateTransform();
QPixmapCache::clear();
2021-04-10 15:27:40 +02:00
}
void MapsforgeMap::unload()
{
cancelJobs(true);
2021-04-10 15:27:40 +02:00
_data.clear();
_style.clear();
2021-04-10 15:27:40 +02:00
}
int MapsforgeMap::zoomFit(const QSize &size, const RectC &rect)
{
if (rect.isValid()) {
RectD pr(rect, _projection, 10);
_zoom = _data.zooms().min();
for (int i = _data.zooms().min() + 1; i <= _data.zooms().max(); i++) {
Transform t(transform(i));
QRectF r(t.proj2img(pr.topLeft()), t.proj2img(pr.bottomRight()));
if (size.width() + EPSILON < r.width()
|| size.height() + EPSILON < r.height())
2021-04-10 15:27:40 +02:00
break;
_zoom = i;
}
} else
_zoom = _data.zooms().max();
updateTransform();
return _zoom;
}
int MapsforgeMap::zoomIn()
{
cancelJobs(false);
2021-04-10 15:27:40 +02:00
_zoom = qMin(_zoom + 1, _data.zooms().max());
updateTransform();
return _zoom;
}
int MapsforgeMap::zoomOut()
{
cancelJobs(false);
2021-04-10 15:27:40 +02:00
_zoom = qMax(_zoom - 1, _data.zooms().min());
updateTransform();
return _zoom;
}
void MapsforgeMap::setZoom(int zoom)
{
_zoom = zoom;
updateTransform();
}
Transform MapsforgeMap::transform(int zoom) const
{
2022-11-04 09:03:36 +01:00
int z = zoom + Util::log2i(_data.tileSize());
2021-04-10 15:27:40 +02:00
double scale = _projection.isGeographic()
? 360.0 / (1<<z) : (2.0 * M_PI * WGS84_RADIUS) / (1<<z);
PointD topLeft(_projection.ll2xy(_data.bounds().topLeft()));
return Transform(ReferencePoint(PointD(0, 0), topLeft),
PointD(scale, scale));
}
void MapsforgeMap::updateTransform()
{
_transform = transform(_zoom);
RectD prect(_data.bounds(), _projection);
_bounds = QRectF(_transform.proj2img(prect.topLeft()),
_transform.proj2img(prect.bottomRight()));
// Adjust the bounds of world maps to avoid problems with wrapping
if (_data.bounds().left() <= -180.0 || _data.bounds().right() >= 180.0)
_bounds.adjust(0.5, 0, -0.5, 0);
}
2021-09-17 23:22:32 +02:00
QString MapsforgeMap::key(int zoom, const QPoint &xy) const
{
return path() + "-" + QString::number(zoom) + "_"
+ QString::number(xy.x()) + "_" + QString::number(xy.y());
}
bool MapsforgeMap::isRunning(int zoom, const QPoint &xy) const
2021-04-10 15:27:40 +02:00
{
for (int i = 0; i < _jobs.size(); i++) {
const QList<Mapsforge::RasterTile> &tiles = _jobs.at(i)->tiles();
for (int j = 0; j < tiles.size(); j++) {
const Mapsforge::RasterTile &mt = tiles.at(j);
if (mt.zoom() == zoom && mt.xy() == xy)
return true;
}
}
return false;
2021-04-10 15:27:40 +02:00
}
void MapsforgeMap::runJob(MapsforgeMapJob *job)
2021-04-10 15:27:40 +02:00
{
_jobs.append(job);
connect(job, &MapsforgeMapJob::finished, this, &MapsforgeMap::jobFinished);
job->run();
2021-04-10 15:27:40 +02:00
}
void MapsforgeMap::removeJob(MapsforgeMapJob *job)
2021-04-10 15:27:40 +02:00
{
_jobs.removeOne(job);
job->deleteLater();
2021-04-10 15:27:40 +02:00
}
void MapsforgeMap::jobFinished(MapsforgeMapJob *job)
2021-04-10 15:27:40 +02:00
{
const QList<Mapsforge::RasterTile> &tiles = job->tiles();
for (int i = 0; i < tiles.size(); i++) {
const Mapsforge::RasterTile &mt = tiles.at(i);
2024-02-21 08:49:09 +01:00
if (!mt.pixmap().isNull())
2021-09-17 23:22:32 +02:00
QPixmapCache::insert(key(mt.zoom(), mt.xy()), mt.pixmap());
}
removeJob(job);
2021-04-10 15:27:40 +02:00
emit tilesLoaded();
}
void MapsforgeMap::cancelJobs(bool wait)
{
for (int i = 0; i < _jobs.size(); i++)
_jobs.at(i)->cancel(wait);
}
2021-04-10 15:27:40 +02:00
void MapsforgeMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
{
int tileSize = _data.tileSize();
QPointF tl(floor(rect.left() / tileSize) * tileSize,
floor(rect.top() / tileSize) * tileSize);
2021-04-10 15:27:40 +02:00
QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
int width = ceil(s.width() / tileSize);
int height = ceil(s.height() / tileSize);
2021-04-10 15:27:40 +02:00
QList<RasterTile> tiles;
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
QPoint ttl(tl.x() + i * tileSize, tl.y() + j * tileSize);
if (isRunning(_zoom, ttl))
continue;
QPixmap pm;
2021-09-17 23:22:32 +02:00
if (QPixmapCache::find(key(_zoom, ttl), &pm))
2021-04-10 15:27:40 +02:00
painter->drawPixmap(ttl, pm);
2021-04-18 18:10:23 +02:00
else {
tiles.append(RasterTile(_projection, _transform, &_style, &_data,
2024-02-21 08:49:09 +01:00
_zoom, QRect(ttl, QSize(tileSize, tileSize)), _tileRatio,
flags & Map::HillShading));
2021-04-10 15:27:40 +02:00
}
}
}
if (!tiles.isEmpty()) {
if (flags & Map::Block) {
QFuture<void> future = QtConcurrent::map(tiles, &RasterTile::render);
future.waitForFinished();
for (int i = 0; i < tiles.size(); i++) {
const RasterTile &mt = tiles.at(i);
const QPixmap &pm = mt.pixmap();
painter->drawPixmap(mt.xy(), pm);
QPixmapCache::insert(key(mt.zoom(), mt.xy()), pm);
}
} else
runJob(new MapsforgeMapJob(tiles));
}
2021-04-10 15:27:40 +02:00
}
Map *MapsforgeMap::create(const QString &path, const Projection &proj,
bool *isMap)
{
Q_UNUSED(proj);
if (isMap)
*isMap = false;
return new MapsforgeMap(path);
}