2018-10-06 23:14:46 +02:00
|
|
|
#include <QtCore>
|
2018-02-20 23:37:19 +01:00
|
|
|
#include <QPainter>
|
2018-11-02 20:01:19 +01:00
|
|
|
#include <QDir>
|
2018-02-20 23:37:19 +01:00
|
|
|
#include "common/rectc.h"
|
2018-02-22 21:02:56 +01:00
|
|
|
#include "common/wgs84.h"
|
2018-11-02 20:01:19 +01:00
|
|
|
#include "common/programpaths.h"
|
2018-02-20 23:37:19 +01:00
|
|
|
#include "transform.h"
|
2018-04-28 19:08:21 +02:00
|
|
|
#include "tileloader.h"
|
2018-02-20 23:37:19 +01:00
|
|
|
#include "wmts.h"
|
|
|
|
#include "wmtsmap.h"
|
|
|
|
|
|
|
|
|
|
|
|
#define CAPABILITIES_FILE "capabilities.xml"
|
|
|
|
|
2018-03-30 10:25:05 +02:00
|
|
|
bool WMTSMap::loadWMTS()
|
2018-02-20 23:37:19 +01:00
|
|
|
{
|
2018-03-30 10:25:05 +02:00
|
|
|
QString file = tilesDir() + "/" + CAPABILITIES_FILE;
|
2018-02-20 23:37:19 +01:00
|
|
|
|
2018-03-30 10:25:05 +02:00
|
|
|
WMTS wmts(file, _setup);
|
|
|
|
if (!wmts.isValid()) {
|
2018-02-20 23:37:19 +01:00
|
|
|
_errorString = wmts.errorString();
|
2018-03-30 10:25:05 +02:00
|
|
|
return false;
|
2018-02-20 23:37:19 +01:00
|
|
|
}
|
2018-03-30 10:25:05 +02:00
|
|
|
|
2018-02-20 23:37:19 +01:00
|
|
|
_zooms = wmts.zooms();
|
|
|
|
_projection = wmts.projection();
|
2018-04-27 19:31:27 +02:00
|
|
|
_tileLoader->setUrl(wmts.tileUrl());
|
2019-01-08 21:42:28 +01:00
|
|
|
_bounds = RectD(wmts.bounds(), _projection);
|
2018-03-30 10:25:05 +02:00
|
|
|
|
2018-04-07 18:42:25 +02:00
|
|
|
if (_setup.coordinateSystem().axisOrder() == CoordinateSystem::Unknown)
|
|
|
|
_cs = _projection.coordinateSystem();
|
2018-04-05 20:38:23 +02:00
|
|
|
else
|
2018-04-07 18:42:25 +02:00
|
|
|
_cs = _setup.coordinateSystem();
|
2018-04-05 20:38:23 +02:00
|
|
|
|
2018-02-20 23:37:19 +01:00
|
|
|
updateTransform();
|
|
|
|
|
2018-03-30 10:25:05 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-08-18 21:06:36 +02:00
|
|
|
WMTSMap::WMTSMap(const QString &name, const WMTS::Setup &setup, qreal tileRatio,
|
|
|
|
QObject *parent) : Map(parent), _name(name), _setup(setup), _tileLoader(0),
|
2018-11-17 10:10:35 +01:00
|
|
|
_zoom(0), _mapRatio(1.0), _tileRatio(tileRatio), _valid(false)
|
2018-03-30 10:25:05 +02:00
|
|
|
{
|
2018-09-24 22:49:10 +02:00
|
|
|
_tileLoader = new TileLoader(tilesDir(), this);
|
2018-04-27 23:08:44 +02:00
|
|
|
_tileLoader->setAuthorization(_setup.authorization());
|
2018-04-27 19:31:27 +02:00
|
|
|
connect(_tileLoader, SIGNAL(finished()), this, SIGNAL(loaded()));
|
|
|
|
|
2018-03-30 10:25:05 +02:00
|
|
|
_valid = loadWMTS();
|
|
|
|
}
|
|
|
|
|
|
|
|
void WMTSMap::clearCache()
|
|
|
|
{
|
2018-04-27 19:31:27 +02:00
|
|
|
_tileLoader->clearCache();
|
2018-03-30 10:25:05 +02:00
|
|
|
_zoom = 0;
|
|
|
|
|
|
|
|
if (!loadWMTS())
|
2018-09-24 23:07:11 +02:00
|
|
|
qWarning("%s: %s", qPrintable(_name), qPrintable(_errorString));
|
2018-03-30 10:25:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QString WMTSMap::tilesDir() const
|
|
|
|
{
|
2018-11-02 20:01:19 +01:00
|
|
|
return QString(QDir(ProgramPaths::tilesDir()).filePath(_name));
|
2018-02-20 23:37:19 +01:00
|
|
|
}
|
|
|
|
|
2018-04-13 21:14:12 +02:00
|
|
|
double WMTSMap::sd2res(double scaleDenominator) const
|
2018-02-20 23:37:19 +01:00
|
|
|
{
|
|
|
|
return scaleDenominator * 0.28e-3 * _projection.units().fromMeters(1.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WMTSMap::updateTransform()
|
|
|
|
{
|
|
|
|
const WMTS::Zoom &z = _zooms.at(_zoom);
|
|
|
|
|
2018-04-15 16:27:47 +02:00
|
|
|
PointD topLeft = (_cs.axisOrder() == CoordinateSystem::YX)
|
|
|
|
? PointD(z.topLeft().y(), z.topLeft().x()) : z.topLeft();
|
2018-04-05 20:38:23 +02:00
|
|
|
|
2018-04-13 21:14:12 +02:00
|
|
|
double pixelSpan = sd2res(z.scaleDenominator());
|
2018-02-22 21:02:56 +01:00
|
|
|
if (_projection.isGeographic())
|
|
|
|
pixelSpan /= deg2rad(WGS84_RADIUS);
|
2019-01-08 21:42:28 +01:00
|
|
|
_transform = Transform(ReferencePoint(PointD(0, 0), topLeft),
|
|
|
|
PointD(pixelSpan, pixelSpan));
|
2018-02-20 23:37:19 +01:00
|
|
|
}
|
|
|
|
|
2018-07-13 09:51:41 +02:00
|
|
|
QRectF WMTSMap::bounds()
|
2018-02-20 23:37:19 +01:00
|
|
|
{
|
|
|
|
const WMTS::Zoom &z = _zooms.at(_zoom);
|
2018-02-24 16:44:30 +01:00
|
|
|
QRectF tileBounds, bounds;
|
2018-02-24 10:34:27 +01:00
|
|
|
|
2018-03-30 10:25:05 +02:00
|
|
|
tileBounds = (z.limits().isNull()) ?
|
|
|
|
QRectF(QPointF(0, 0), QSize(z.tile().width() * z.matrix().width(),
|
|
|
|
z.tile().height() * z.matrix().height()))
|
|
|
|
: QRectF(QPointF(z.limits().left() * z.tile().width(), z.limits().top()
|
|
|
|
* z.tile().height()), QSize(z.tile().width() * z.limits().width(),
|
|
|
|
z.tile().height() * z.limits().height()));
|
2018-02-24 16:44:30 +01:00
|
|
|
|
2019-01-08 21:42:28 +01:00
|
|
|
if (_bounds.isValid())
|
|
|
|
bounds = QRectF(_transform.proj2img(_bounds.topLeft())
|
|
|
|
/ coordinatesRatio(), _transform.proj2img(_bounds.bottomRight())
|
|
|
|
/ coordinatesRatio());
|
|
|
|
return bounds.isValid() ? tileBounds.intersected(bounds) : tileBounds;
|
2018-02-20 23:37:19 +01:00
|
|
|
}
|
|
|
|
|
2018-04-16 20:26:10 +02:00
|
|
|
int WMTSMap::zoomFit(const QSize &size, const RectC &rect)
|
2018-02-20 23:37:19 +01:00
|
|
|
{
|
2018-04-16 20:26:10 +02:00
|
|
|
if (rect.isValid()) {
|
2019-01-14 23:47:24 +01:00
|
|
|
RectD prect(rect, _projection);
|
|
|
|
PointD sc(prect.width() / size.width(), prect.height() / size.height());
|
2018-04-16 20:26:10 +02:00
|
|
|
double resolution = qMax(qAbs(sc.x()), qAbs(sc.y()));
|
2018-02-22 21:02:56 +01:00
|
|
|
if (_projection.isGeographic())
|
|
|
|
resolution *= deg2rad(WGS84_RADIUS);
|
2018-02-20 23:37:19 +01:00
|
|
|
|
2018-02-25 19:33:12 +01:00
|
|
|
_zoom = 0;
|
2018-02-20 23:37:19 +01:00
|
|
|
for (int i = 0; i < _zooms.size(); i++) {
|
2018-08-18 21:06:36 +02:00
|
|
|
if (sd2res(_zooms.at(i).scaleDenominator()) < resolution
|
|
|
|
/ coordinatesRatio())
|
2018-02-20 23:37:19 +01:00
|
|
|
break;
|
|
|
|
_zoom = i;
|
|
|
|
}
|
2018-02-25 19:33:12 +01:00
|
|
|
} else
|
|
|
|
_zoom = _zooms.size() - 1;
|
2018-02-20 23:37:19 +01:00
|
|
|
|
|
|
|
updateTransform();
|
|
|
|
return _zoom;
|
|
|
|
}
|
|
|
|
|
2018-04-28 22:18:11 +02:00
|
|
|
void WMTSMap::setZoom(int zoom)
|
|
|
|
{
|
|
|
|
_zoom = zoom;
|
|
|
|
updateTransform();
|
|
|
|
}
|
|
|
|
|
2018-02-28 22:19:46 +01:00
|
|
|
int WMTSMap::zoomIn()
|
2018-02-20 23:37:19 +01:00
|
|
|
{
|
|
|
|
_zoom = qMin(_zoom + 1, _zooms.size() - 1);
|
|
|
|
updateTransform();
|
|
|
|
return _zoom;
|
|
|
|
}
|
|
|
|
|
2018-02-28 22:19:46 +01:00
|
|
|
int WMTSMap::zoomOut()
|
2018-02-20 23:37:19 +01:00
|
|
|
{
|
|
|
|
_zoom = qMax(_zoom - 1, 0);
|
|
|
|
updateTransform();
|
|
|
|
return _zoom;
|
|
|
|
}
|
|
|
|
|
2018-08-18 21:06:36 +02:00
|
|
|
qreal WMTSMap::coordinatesRatio() const
|
|
|
|
{
|
2018-11-17 10:10:35 +01:00
|
|
|
return _mapRatio > 1.0 ? _mapRatio / _tileRatio : 1.0;
|
2018-08-18 21:06:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
qreal WMTSMap::imageRatio() const
|
|
|
|
{
|
2018-11-17 10:10:35 +01:00
|
|
|
return _mapRatio > 1.0 ? _mapRatio : _tileRatio;
|
2018-08-18 21:06:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QSizeF WMTSMap::tileSize(const WMTS::Zoom &zoom) const
|
|
|
|
{
|
|
|
|
return QSizeF(zoom.tile().width() / coordinatesRatio(),
|
|
|
|
zoom.tile().height() / coordinatesRatio());
|
|
|
|
}
|
|
|
|
|
2018-08-23 20:26:10 +02:00
|
|
|
void WMTSMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
|
2018-02-20 23:37:19 +01:00
|
|
|
{
|
|
|
|
const WMTS::Zoom &z = _zooms.at(_zoom);
|
2018-08-18 21:06:36 +02:00
|
|
|
QSizeF ts(tileSize(z));
|
|
|
|
|
2018-10-06 21:15:06 +02:00
|
|
|
QPoint tl = QPoint(qFloor(rect.left() / ts.width()),
|
|
|
|
qFloor(rect.top() / ts.height()));
|
|
|
|
QPoint br = QPoint(qCeil(rect.right() / ts.width()),
|
|
|
|
qCeil(rect.bottom() / ts.height()));
|
2018-02-20 23:37:19 +01:00
|
|
|
|
2018-10-05 07:10:49 +02:00
|
|
|
QVector<Tile> tiles;
|
|
|
|
tiles.reserve((br.x() - tl.x()) * (br.y() - tl.y()));
|
2018-02-24 01:59:03 +01:00
|
|
|
for (int i = tl.x(); i < br.x(); i++)
|
|
|
|
for (int j = tl.y(); j < br.y(); j++)
|
2018-03-30 10:25:05 +02:00
|
|
|
tiles.append(Tile(QPoint(i, j), z.id()));
|
2018-02-20 23:37:19 +01:00
|
|
|
|
2018-08-23 20:26:10 +02:00
|
|
|
if (flags & Map::Block)
|
2018-04-27 19:31:27 +02:00
|
|
|
_tileLoader->loadTilesSync(tiles);
|
2018-02-20 23:37:19 +01:00
|
|
|
else
|
2018-04-27 19:31:27 +02:00
|
|
|
_tileLoader->loadTilesAsync(tiles);
|
2018-02-20 23:37:19 +01:00
|
|
|
|
|
|
|
for (int i = 0; i < tiles.count(); i++) {
|
|
|
|
Tile &t = tiles[i];
|
2018-08-18 21:06:36 +02:00
|
|
|
QPointF tp(t.xy().x() * ts.width(), t.xy().y() * ts.height());
|
|
|
|
if (!t.pixmap().isNull()) {
|
|
|
|
#ifdef ENABLE_HIDPI
|
|
|
|
t.pixmap().setDevicePixelRatio(imageRatio());
|
|
|
|
#endif // ENABLE_HIDPI
|
2018-02-20 23:37:19 +01:00
|
|
|
painter->drawPixmap(tp, t.pixmap());
|
2018-08-18 21:06:36 +02:00
|
|
|
}
|
2018-02-20 23:37:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-13 09:51:41 +02:00
|
|
|
QPointF WMTSMap::ll2xy(const Coordinates &c)
|
2018-02-20 23:37:19 +01:00
|
|
|
{
|
2018-08-18 21:06:36 +02:00
|
|
|
return _transform.proj2img(_projection.ll2xy(c)) / coordinatesRatio();
|
2018-02-20 23:37:19 +01:00
|
|
|
}
|
|
|
|
|
2018-07-13 09:51:41 +02:00
|
|
|
Coordinates WMTSMap::xy2ll(const QPointF &p)
|
2018-02-20 23:37:19 +01:00
|
|
|
{
|
2018-08-18 21:06:36 +02:00
|
|
|
return _projection.xy2ll(_transform.img2proj(p * coordinatesRatio()));
|
2018-02-20 23:37:19 +01:00
|
|
|
}
|