2019-05-10 18:56:19 +02:00
|
|
|
#include <QFile>
|
|
|
|
#include <QPainter>
|
|
|
|
#include <QPixmapCache>
|
|
|
|
#include <QtConcurrent>
|
|
|
|
#include "common/rectc.h"
|
2020-01-19 17:05:26 +01:00
|
|
|
#include "common/range.h"
|
2020-04-26 01:17:54 +02:00
|
|
|
#include "common/wgs84.h"
|
2021-04-10 15:27:40 +02:00
|
|
|
#include "IMG/imgdata.h"
|
|
|
|
#include "IMG/gmapdata.h"
|
2020-04-26 01:17:54 +02:00
|
|
|
#include "IMG/rastertile.h"
|
2020-04-19 11:36:17 +02:00
|
|
|
#include "osm.h"
|
2019-05-10 18:56:19 +02:00
|
|
|
#include "pcs.h"
|
|
|
|
#include "rectd.h"
|
|
|
|
#include "imgmap.h"
|
|
|
|
|
2021-04-10 15:27:40 +02:00
|
|
|
using namespace IMG;
|
2019-05-10 18:56:19 +02:00
|
|
|
|
2019-10-05 12:41:42 +02:00
|
|
|
#define TILE_SIZE 384
|
|
|
|
#define TEXT_EXTENT 160
|
2019-05-10 18:56:19 +02:00
|
|
|
|
2020-04-26 15:46:42 +02:00
|
|
|
static QList<MapData*> overlays(const QString &fileName)
|
|
|
|
{
|
|
|
|
QList<MapData*> list;
|
|
|
|
|
2020-04-30 21:46:41 +02:00
|
|
|
for (int i = 1; i < 32; i++) {
|
2020-04-26 15:46:42 +02:00
|
|
|
QString ol(fileName + "." + QString::number(i));
|
|
|
|
if (QFileInfo(ol).isFile()) {
|
2021-04-10 15:27:40 +02:00
|
|
|
MapData *data = new IMGData(ol);
|
2020-04-26 15:46:42 +02:00
|
|
|
if (data->isValid())
|
|
|
|
list.append(data);
|
|
|
|
else {
|
|
|
|
qWarning("%s: %s", qPrintable(data->fileName()),
|
|
|
|
qPrintable(data->errorString()));
|
|
|
|
delete data;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
2022-06-09 00:38:25 +02:00
|
|
|
IMGMap::IMGMap(const QString &fileName, bool GMAP, QObject *parent)
|
2022-05-10 01:05:30 +02:00
|
|
|
: Map(fileName, parent), _projection(PCS::pcs(3857)), _tileRatio(1.0),
|
|
|
|
_valid(false)
|
2019-05-10 18:56:19 +02:00
|
|
|
{
|
2022-06-09 00:38:25 +02:00
|
|
|
if (GMAP)
|
2021-04-10 15:27:40 +02:00
|
|
|
_data.append(new GMAPData(fileName));
|
2020-04-26 15:46:42 +02:00
|
|
|
else {
|
2021-04-10 15:27:40 +02:00
|
|
|
_data.append(new IMGData(fileName));
|
2020-04-26 15:46:42 +02:00
|
|
|
_data.append(overlays(fileName));
|
|
|
|
}
|
2020-02-09 23:24:48 +01:00
|
|
|
|
2020-04-26 15:46:42 +02:00
|
|
|
if (!_data.first()->isValid()) {
|
|
|
|
_errorString = _data.first()->errorString();
|
2019-05-10 18:56:19 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-26 15:46:42 +02:00
|
|
|
_dataBounds = _data.first()->bounds() & OSM::BOUNDS;
|
|
|
|
_zoom = _data.first()->zooms().min();
|
2019-05-10 18:56:19 +02:00
|
|
|
updateTransform();
|
|
|
|
|
|
|
|
_valid = true;
|
|
|
|
}
|
|
|
|
|
2019-07-04 18:18:03 +02:00
|
|
|
void IMGMap::load()
|
|
|
|
{
|
2020-04-26 15:46:42 +02:00
|
|
|
for (int i = 0; i < _data.size(); i++)
|
|
|
|
_data.at(i)->load();
|
2019-07-04 18:18:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void IMGMap::unload()
|
|
|
|
{
|
2022-06-02 23:22:34 +02:00
|
|
|
cancelJobs(true);
|
|
|
|
|
2020-04-26 15:46:42 +02:00
|
|
|
for (int i = 0; i < _data.size(); i++)
|
|
|
|
_data.at(i)->clear();
|
2019-07-04 18:18:03 +02:00
|
|
|
}
|
|
|
|
|
2019-05-10 18:56:19 +02:00
|
|
|
int IMGMap::zoomFit(const QSize &size, const RectC &rect)
|
|
|
|
{
|
2020-04-26 15:46:42 +02:00
|
|
|
const Range &zooms = _data.first()->zooms();
|
|
|
|
|
2019-05-10 18:56:19 +02:00
|
|
|
if (rect.isValid()) {
|
2019-05-29 18:27:11 +02:00
|
|
|
RectD pr(rect, _projection, 10);
|
2019-05-10 18:56:19 +02:00
|
|
|
|
2020-04-26 15:46:42 +02:00
|
|
|
_zoom = zooms.min();
|
|
|
|
for (int i = zooms.min() + 1; i <= zooms.max(); i++) {
|
2019-05-29 18:27:11 +02:00
|
|
|
Transform t(transform(i));
|
|
|
|
QRectF r(t.proj2img(pr.topLeft()), t.proj2img(pr.bottomRight()));
|
|
|
|
if (size.width() < r.width() || size.height() < r.height())
|
2019-05-10 18:56:19 +02:00
|
|
|
break;
|
|
|
|
_zoom = i;
|
|
|
|
}
|
|
|
|
} else
|
2020-04-26 15:46:42 +02:00
|
|
|
_zoom = zooms.max();
|
2019-05-10 18:56:19 +02:00
|
|
|
|
|
|
|
updateTransform();
|
|
|
|
|
|
|
|
return _zoom;
|
|
|
|
}
|
|
|
|
|
|
|
|
int IMGMap::zoomIn()
|
|
|
|
{
|
2022-06-02 23:22:34 +02:00
|
|
|
cancelJobs(false);
|
2022-06-02 18:31:40 +02:00
|
|
|
|
2020-04-26 15:46:42 +02:00
|
|
|
_zoom = qMin(_zoom + 1, _data.first()->zooms().max());
|
2019-05-10 18:56:19 +02:00
|
|
|
updateTransform();
|
|
|
|
return _zoom;
|
|
|
|
}
|
|
|
|
|
|
|
|
int IMGMap::zoomOut()
|
|
|
|
{
|
2022-06-02 23:22:34 +02:00
|
|
|
cancelJobs(false);
|
2022-06-02 18:31:40 +02:00
|
|
|
|
2020-04-26 15:46:42 +02:00
|
|
|
_zoom = qMax(_zoom - 1, _data.first()->zooms().min());
|
2019-05-10 18:56:19 +02:00
|
|
|
updateTransform();
|
|
|
|
return _zoom;
|
|
|
|
}
|
|
|
|
|
2019-05-21 17:44:47 +02:00
|
|
|
void IMGMap::setZoom(int zoom)
|
|
|
|
{
|
|
|
|
_zoom = zoom;
|
|
|
|
updateTransform();
|
|
|
|
}
|
|
|
|
|
2019-05-29 18:27:11 +02:00
|
|
|
Transform IMGMap::transform(int zoom) const
|
2019-05-10 18:56:19 +02:00
|
|
|
{
|
2019-08-01 08:36:58 +02:00
|
|
|
double scale = _projection.isGeographic()
|
|
|
|
? 360.0 / (1<<zoom) : (2.0 * M_PI * WGS84_RADIUS) / (1<<zoom);
|
2020-04-19 11:36:17 +02:00
|
|
|
PointD topLeft(_projection.ll2xy(_dataBounds.topLeft()));
|
2019-05-29 18:27:11 +02:00
|
|
|
return Transform(ReferencePoint(PointD(0, 0), topLeft),
|
2019-05-10 18:56:19 +02:00
|
|
|
PointD(scale, scale));
|
|
|
|
}
|
|
|
|
|
2019-05-29 18:27:11 +02:00
|
|
|
void IMGMap::updateTransform()
|
|
|
|
{
|
|
|
|
_transform = transform(_zoom);
|
2020-03-07 19:24:39 +01:00
|
|
|
|
2020-04-19 11:36:17 +02:00
|
|
|
RectD prect(_dataBounds, _projection);
|
2020-03-07 19:24:39 +01:00
|
|
|
_bounds = QRectF(_transform.proj2img(prect.topLeft()),
|
|
|
|
_transform.proj2img(prect.bottomRight()));
|
2020-10-27 16:46:09 +01:00
|
|
|
// Adjust the bounds of world maps to avoid problems with wrapping
|
|
|
|
if (_dataBounds.left() == -180.0 || _dataBounds.right() == 180.0)
|
|
|
|
_bounds.adjust(0.5, 0, -0.5, 0);
|
2019-05-29 18:27:11 +02:00
|
|
|
}
|
|
|
|
|
2022-06-02 18:31:40 +02:00
|
|
|
bool IMGMap::isRunning(const QString &key) const
|
|
|
|
{
|
|
|
|
for (int i = 0; i < _jobs.size(); i++) {
|
|
|
|
const QList<IMG::RasterTile> &tiles = _jobs.at(i)->tiles();
|
|
|
|
for (int j = 0; j < tiles.size(); j++)
|
|
|
|
if (tiles.at(j).key() == key)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void IMGMap::runJob(IMGMapJob *job)
|
|
|
|
{
|
|
|
|
_jobs.append(job);
|
|
|
|
|
|
|
|
connect(job, &IMGMapJob::finished, this, &IMGMap::jobFinished);
|
|
|
|
job->run();
|
|
|
|
}
|
|
|
|
|
|
|
|
void IMGMap::removeJob(IMGMapJob *job)
|
|
|
|
{
|
|
|
|
_jobs.removeOne(job);
|
|
|
|
job->deleteLater();
|
|
|
|
}
|
|
|
|
|
|
|
|
void IMGMap::jobFinished(IMGMapJob *job)
|
|
|
|
{
|
|
|
|
const QList<IMG::RasterTile> &tiles = job->tiles();
|
|
|
|
|
|
|
|
for (int i = 0; i < tiles.size(); i++) {
|
|
|
|
const IMG::RasterTile &mt = tiles.at(i);
|
|
|
|
if (mt.isValid())
|
|
|
|
QPixmapCache::insert(mt.key(), mt.pixmap());
|
|
|
|
}
|
|
|
|
|
|
|
|
removeJob(job);
|
|
|
|
|
|
|
|
emit tilesLoaded();
|
|
|
|
}
|
|
|
|
|
2022-06-02 23:22:34 +02:00
|
|
|
void IMGMap::cancelJobs(bool wait)
|
2022-06-02 18:31:40 +02:00
|
|
|
{
|
|
|
|
for (int i = 0; i < _jobs.size(); i++)
|
2022-06-02 23:22:34 +02:00
|
|
|
_jobs.at(i)->cancel(wait);
|
2022-06-02 18:31:40 +02:00
|
|
|
}
|
|
|
|
|
2019-05-10 18:56:19 +02:00
|
|
|
void IMGMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
|
|
|
|
{
|
|
|
|
QPointF tl(floor(rect.left() / TILE_SIZE)
|
|
|
|
* TILE_SIZE, floor(rect.top() / TILE_SIZE) * TILE_SIZE);
|
|
|
|
QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
|
|
|
|
int width = ceil(s.width() / TILE_SIZE);
|
|
|
|
int height = ceil(s.height() / TILE_SIZE);
|
|
|
|
|
|
|
|
QList<RasterTile> tiles;
|
|
|
|
|
2020-04-26 15:46:42 +02:00
|
|
|
for (int n = 0; n < _data.size(); n++) {
|
|
|
|
for (int i = 0; i < width; i++) {
|
|
|
|
for (int j = 0; j < height; j++) {
|
|
|
|
QPixmap pm;
|
|
|
|
QPoint ttl(tl.x() + i * TILE_SIZE, tl.y() + j * TILE_SIZE);
|
2021-09-28 12:47:14 +02:00
|
|
|
QString key(_data.at(n)->fileName() + "-" + QString::number(_zoom)
|
|
|
|
+ "_" + QString::number(ttl.x()) + "_" + QString::number(ttl.y()));
|
|
|
|
|
2022-06-02 18:31:40 +02:00
|
|
|
if (isRunning(key))
|
|
|
|
continue;
|
|
|
|
|
2020-12-22 22:09:09 +01:00
|
|
|
if (QPixmapCache::find(key, &pm))
|
2020-04-26 15:46:42 +02:00
|
|
|
painter->drawPixmap(ttl, pm);
|
|
|
|
else {
|
|
|
|
QList<MapData::Poly> polygons, lines;
|
|
|
|
QList<MapData::Point> points;
|
|
|
|
|
|
|
|
QRectF polyRect(ttl, QPointF(ttl.x() + TILE_SIZE,
|
|
|
|
ttl.y() + TILE_SIZE));
|
2021-04-10 15:27:40 +02:00
|
|
|
polyRect &= _bounds;
|
2020-04-26 15:46:42 +02:00
|
|
|
RectD polyRectD(_transform.img2proj(polyRect.topLeft()),
|
|
|
|
_transform.img2proj(polyRect.bottomRight()));
|
2020-12-14 22:06:59 +01:00
|
|
|
_data.at(n)->polys(polyRectD.toRectC(_projection, 20), _zoom,
|
2020-04-26 15:46:42 +02:00
|
|
|
&polygons, &lines);
|
|
|
|
|
|
|
|
QRectF pointRect(QPointF(ttl.x() - TEXT_EXTENT,
|
|
|
|
ttl.y() - TEXT_EXTENT), QPointF(ttl.x() + TILE_SIZE
|
|
|
|
+ TEXT_EXTENT, ttl.y() + TILE_SIZE + TEXT_EXTENT));
|
2021-04-10 15:27:40 +02:00
|
|
|
pointRect &= _bounds;
|
2020-04-26 15:46:42 +02:00
|
|
|
RectD pointRectD(_transform.img2proj(pointRect.topLeft()),
|
|
|
|
_transform.img2proj(pointRect.bottomRight()));
|
2020-12-14 22:06:59 +01:00
|
|
|
_data.at(n)->points(pointRectD.toRectC(_projection, 20),
|
2020-04-26 15:46:42 +02:00
|
|
|
_zoom, &points);
|
|
|
|
|
2022-06-02 18:31:40 +02:00
|
|
|
tiles.append(RasterTile(_projection, _transform,
|
|
|
|
_data.at(n)->style(), _zoom,
|
2022-05-10 01:05:30 +02:00
|
|
|
QRect(ttl, QSize(TILE_SIZE, TILE_SIZE)), _tileRatio, key,
|
|
|
|
polygons, lines, points));
|
2020-04-26 15:46:42 +02:00
|
|
|
}
|
2019-05-10 18:56:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-02 18:31:40 +02:00
|
|
|
if (!tiles.isEmpty()) {
|
|
|
|
if (flags & Map::Block) {
|
|
|
|
QFuture<void> future = QtConcurrent::map(tiles, &RasterTile::render);
|
|
|
|
future.waitForFinished();
|
2019-05-10 18:56:19 +02:00
|
|
|
|
2022-06-02 18:31:40 +02:00
|
|
|
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(mt.key(), pm);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
runJob(new IMGMapJob(tiles));
|
2019-05-10 18:56:19 +02:00
|
|
|
}
|
|
|
|
}
|
2019-05-14 23:01:24 +02:00
|
|
|
|
2022-05-10 01:05:30 +02:00
|
|
|
void IMGMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio)
|
|
|
|
{
|
|
|
|
Q_UNUSED(mapRatio);
|
|
|
|
|
|
|
|
_tileRatio = deviceRatio;
|
|
|
|
}
|
|
|
|
|
2020-12-24 16:33:17 +01:00
|
|
|
void IMGMap::setOutputProjection(const Projection &projection)
|
2019-05-14 23:01:24 +02:00
|
|
|
{
|
2020-04-21 23:26:35 +02:00
|
|
|
if (projection == _projection)
|
|
|
|
return;
|
|
|
|
|
2019-05-14 23:01:24 +02:00
|
|
|
_projection = projection;
|
2020-12-14 22:06:59 +01:00
|
|
|
// Limit the bounds for some well known projections
|
2020-10-27 16:46:09 +01:00
|
|
|
// (world maps have N/S bounds up to 90/-90!)
|
2020-12-14 22:06:59 +01:00
|
|
|
if (_projection == PCS::pcs(3857) || _projection == PCS::pcs(3395))
|
|
|
|
_dataBounds = _data.first()->bounds() & OSM::BOUNDS;
|
|
|
|
else if (_projection == PCS::pcs(3031) || _projection == PCS::pcs(3976))
|
|
|
|
_dataBounds = _data.first()->bounds() & RectC(Coordinates(-180, -60),
|
|
|
|
Coordinates(180, -90));
|
|
|
|
else if (_projection == PCS::pcs(3995) || _projection == PCS::pcs(3413))
|
|
|
|
_dataBounds = _data.first()->bounds() & RectC(Coordinates(-180, 90),
|
|
|
|
Coordinates(180, 60));
|
|
|
|
else
|
|
|
|
_dataBounds = _data.first()->bounds();
|
2020-04-21 23:26:35 +02:00
|
|
|
|
2019-05-14 23:01:24 +02:00
|
|
|
updateTransform();
|
|
|
|
QPixmapCache::clear();
|
|
|
|
}
|
2022-04-29 23:16:10 +02:00
|
|
|
|
2022-06-09 00:38:25 +02:00
|
|
|
Map* IMGMap::createIMG(const QString &path, const Projection &, bool *isDir)
|
2022-04-29 23:16:10 +02:00
|
|
|
{
|
|
|
|
if (isDir)
|
2022-06-09 00:38:25 +02:00
|
|
|
*isDir = false;
|
2022-04-29 23:16:10 +02:00
|
|
|
|
2022-06-09 00:38:25 +02:00
|
|
|
return new IMGMap(path, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
Map* IMGMap::createGMAP(const QString &path, const Projection &, bool *isDir)
|
|
|
|
{
|
|
|
|
if (isDir)
|
|
|
|
*isDir = true;
|
|
|
|
|
|
|
|
return new IMGMap(path, true);
|
2022-04-29 23:16:10 +02:00
|
|
|
}
|