2018-10-06 23:14:46 +02:00
|
|
|
#include <QtCore>
|
2018-03-30 10:25:05 +02:00
|
|
|
#include <QDir>
|
|
|
|
#include <QPainter>
|
|
|
|
#include "common/wgs84.h"
|
|
|
|
#include "common/rectc.h"
|
2018-11-02 20:01:19 +01:00
|
|
|
#include "common/programpaths.h"
|
2018-04-28 19:08:21 +02:00
|
|
|
#include "tileloader.h"
|
2018-03-30 10:25:05 +02:00
|
|
|
#include "wmsmap.h"
|
|
|
|
|
|
|
|
|
|
|
|
#define CAPABILITIES_FILE "capabilities.xml"
|
2019-05-28 07:26:13 +02:00
|
|
|
#define EPSILON 1e-6
|
2018-03-30 10:25:05 +02:00
|
|
|
|
2018-04-13 21:14:12 +02:00
|
|
|
double WMSMap::sd2res(double scaleDenominator) const
|
2018-03-30 10:25:05 +02:00
|
|
|
{
|
2020-03-17 21:06:51 +01:00
|
|
|
return scaleDenominator * _wms->projection().units().fromMeters(1.0)
|
|
|
|
* 0.28e-3;
|
2018-03-30 10:25:05 +02:00
|
|
|
}
|
|
|
|
|
2020-03-17 21:06:51 +01:00
|
|
|
QString WMSMap::tileUrl() const
|
2018-03-30 10:25:05 +02:00
|
|
|
{
|
2020-03-17 21:06:51 +01:00
|
|
|
const WMS::Setup &setup = _wms->setup();
|
2018-03-31 11:27:01 +02:00
|
|
|
|
2020-03-17 21:06:51 +01:00
|
|
|
QString url = QString("%1%2service=WMS&version=%3&request=GetMap&bbox=$bbox"
|
2018-09-16 12:05:11 +02:00
|
|
|
"&width=%4&height=%5&layers=%6&styles=%7&format=%8&transparent=true")
|
2020-03-17 21:06:51 +01:00
|
|
|
.arg(_wms->getMapUrl(), _wms->getMapUrl().contains('?') ? "&" : "?",
|
|
|
|
_wms->version(), QString::number(_tileSize), QString::number(_tileSize),
|
|
|
|
setup.layer(), setup.style(), setup.format());
|
2018-04-08 19:13:16 +02:00
|
|
|
|
2020-03-17 21:06:51 +01:00
|
|
|
if (_wms->version() >= "1.3.0")
|
|
|
|
url.append(QString("&CRS=%1").arg(setup.crs()));
|
2018-04-08 19:13:16 +02:00
|
|
|
else
|
2020-03-17 21:06:51 +01:00
|
|
|
url.append(QString("&SRS=%1").arg(setup.crs()));
|
2018-04-08 19:13:16 +02:00
|
|
|
|
2020-03-17 21:06:51 +01:00
|
|
|
for (int i = 0; i < setup.dimensions().size(); i++) {
|
|
|
|
const KV<QString, QString> &dim = setup.dimensions().at(i);
|
2018-09-30 12:16:41 +02:00
|
|
|
url.append(QString("&%1=%2").arg(dim.key(), dim.value()));
|
2018-04-08 19:13:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return url;
|
2018-03-30 10:25:05 +02:00
|
|
|
}
|
|
|
|
|
2020-03-17 21:06:51 +01:00
|
|
|
void WMSMap::computeZooms()
|
2018-03-30 10:25:05 +02:00
|
|
|
{
|
|
|
|
_zooms.clear();
|
|
|
|
|
2020-03-17 21:06:51 +01:00
|
|
|
const RangeF &sd = _wms->scaleDenominator();
|
|
|
|
if (sd.size() > 0) {
|
|
|
|
double ld = log2(sd.max() - EPSILON) - log2(sd.min() + EPSILON);
|
2018-10-06 21:15:06 +02:00
|
|
|
int cld = (int)ceil(ld);
|
|
|
|
double step = ld / (double)cld;
|
2020-03-17 21:06:51 +01:00
|
|
|
double lmax = log2(sd.max() - EPSILON);
|
2018-03-30 10:25:05 +02:00
|
|
|
for (int i = 0; i <= cld; i++)
|
|
|
|
_zooms.append(pow(2.0, lmax - i * step));
|
|
|
|
} else
|
2020-03-17 21:06:51 +01:00
|
|
|
_zooms.append(sd.min() + EPSILON);
|
2018-03-30 10:25:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void WMSMap::updateTransform()
|
|
|
|
{
|
2018-04-16 20:26:10 +02:00
|
|
|
double pixelSpan = sd2res(_zooms.at(_zoom));
|
2020-03-17 21:06:51 +01:00
|
|
|
if (_wms->projection().isGeographic())
|
2018-03-30 10:25:05 +02:00
|
|
|
pixelSpan /= deg2rad(WGS84_RADIUS);
|
2019-01-08 21:42:28 +01:00
|
|
|
_transform = Transform(ReferencePoint(PointD(0, 0),
|
2020-03-17 21:06:51 +01:00
|
|
|
_wms->projection().ll2xy(_wms->bbox().topLeft())),
|
|
|
|
PointD(pixelSpan, pixelSpan));
|
2018-03-30 10:25:05 +02:00
|
|
|
}
|
|
|
|
|
2020-12-02 23:58:11 +01:00
|
|
|
WMSMap::WMSMap(const QString &fileName, const QString &name,
|
|
|
|
const WMS::Setup &setup, int tileSize, QObject *parent)
|
|
|
|
: Map(fileName, parent), _name(name), _tileLoader(0), _zoom(0),
|
2020-03-17 21:06:51 +01:00
|
|
|
_tileSize(tileSize), _mapRatio(1.0)
|
2018-03-30 10:25:05 +02:00
|
|
|
{
|
2020-03-17 21:06:51 +01:00
|
|
|
QString tilesDir(QDir(ProgramPaths::tilesDir()).filePath(_name));
|
2018-03-30 10:25:05 +02:00
|
|
|
|
2020-03-17 21:06:51 +01:00
|
|
|
_tileLoader = new TileLoader(tilesDir, this);
|
|
|
|
_tileLoader->setAuthorization(setup.authorization());
|
2021-04-28 00:01:07 +02:00
|
|
|
connect(_tileLoader, &TileLoader::finished, this, &WMSMap::tilesLoaded);
|
2018-03-30 10:25:05 +02:00
|
|
|
|
2020-03-17 21:06:51 +01:00
|
|
|
_wms = new WMS(QDir(tilesDir).filePath(CAPABILITIES_FILE), setup, this);
|
2021-04-28 00:01:07 +02:00
|
|
|
connect(_wms, &WMS::downloadFinished, this, &WMSMap::wmsReady);
|
2020-03-17 21:06:51 +01:00
|
|
|
if (_wms->isReady())
|
|
|
|
init();
|
|
|
|
}
|
2018-04-05 20:38:23 +02:00
|
|
|
|
2020-03-17 21:06:51 +01:00
|
|
|
void WMSMap::init()
|
|
|
|
{
|
|
|
|
_tileLoader->setUrl(tileUrl());
|
|
|
|
_bounds = RectD(_wms->bbox(), _wms->projection());
|
|
|
|
computeZooms();
|
2018-03-30 10:25:05 +02:00
|
|
|
updateTransform();
|
|
|
|
}
|
|
|
|
|
2020-03-17 21:06:51 +01:00
|
|
|
void WMSMap::wmsReady()
|
2018-03-30 10:25:05 +02:00
|
|
|
{
|
2020-03-17 21:06:51 +01:00
|
|
|
if (_wms->isValid())
|
|
|
|
init();
|
2018-04-27 19:31:27 +02:00
|
|
|
|
2020-03-17 21:06:51 +01:00
|
|
|
emit mapLoaded();
|
2018-03-30 10:25:05 +02:00
|
|
|
}
|
|
|
|
|
2023-05-04 09:38:35 +02:00
|
|
|
void WMSMap::load(const Projection &in, const Projection &out,
|
|
|
|
qreal deviceRatio, bool hidpi)
|
|
|
|
{
|
|
|
|
Q_UNUSED(in);
|
|
|
|
Q_UNUSED(out);
|
|
|
|
|
|
|
|
_mapRatio = hidpi ? deviceRatio : 1.0;
|
|
|
|
}
|
|
|
|
|
2018-03-30 10:25:05 +02:00
|
|
|
void WMSMap::clearCache()
|
|
|
|
{
|
2018-04-27 19:31:27 +02:00
|
|
|
_tileLoader->clearCache();
|
2018-03-30 10:25:05 +02:00
|
|
|
}
|
|
|
|
|
2018-07-13 09:51:41 +02:00
|
|
|
QRectF WMSMap::bounds()
|
2018-05-02 21:25:14 +02:00
|
|
|
{
|
2019-01-08 21:42:28 +01:00
|
|
|
return QRectF(_transform.proj2img(_bounds.topLeft()) / _mapRatio,
|
|
|
|
_transform.proj2img(_bounds.bottomRight()) / _mapRatio);
|
2018-05-02 21:25:14 +02:00
|
|
|
}
|
|
|
|
|
2018-04-16 20:26:10 +02:00
|
|
|
int WMSMap::zoomFit(const QSize &size, const RectC &rect)
|
2018-03-30 10:25:05 +02:00
|
|
|
{
|
2018-04-16 20:26:10 +02:00
|
|
|
if (rect.isValid()) {
|
2020-03-17 21:06:51 +01:00
|
|
|
RectD prect(rect, _wms->projection());
|
2019-01-14 23:47:24 +01:00
|
|
|
PointD sc(prect.width() / size.width(), prect.height() / size.height());
|
2018-04-13 21:14:12 +02:00
|
|
|
double resolution = qMax(qAbs(sc.x()), qAbs(sc.y()));
|
2020-03-17 21:06:51 +01:00
|
|
|
if (_wms->projection().isGeographic())
|
2018-03-30 10:25:05 +02:00
|
|
|
resolution *= deg2rad(WGS84_RADIUS);
|
|
|
|
|
|
|
|
_zoom = 0;
|
|
|
|
for (int i = 0; i < _zooms.size(); i++) {
|
2018-11-17 10:10:35 +01:00
|
|
|
if (sd2res(_zooms.at(i)) < resolution / _mapRatio)
|
2018-03-30 10:25:05 +02:00
|
|
|
break;
|
|
|
|
_zoom = i;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
_zoom = _zooms.size() - 1;
|
|
|
|
|
|
|
|
updateTransform();
|
|
|
|
return _zoom;
|
|
|
|
}
|
|
|
|
|
2018-04-28 22:18:11 +02:00
|
|
|
void WMSMap::setZoom(int zoom)
|
|
|
|
{
|
|
|
|
_zoom = zoom;
|
|
|
|
updateTransform();
|
|
|
|
}
|
|
|
|
|
2018-03-30 10:25:05 +02:00
|
|
|
int WMSMap::zoomIn()
|
|
|
|
{
|
|
|
|
_zoom = qMin(_zoom + 1, _zooms.size() - 1);
|
|
|
|
updateTransform();
|
|
|
|
return _zoom;
|
|
|
|
}
|
|
|
|
|
|
|
|
int WMSMap::zoomOut()
|
|
|
|
{
|
|
|
|
_zoom = qMax(_zoom - 1, 0);
|
|
|
|
updateTransform();
|
|
|
|
return _zoom;
|
|
|
|
}
|
|
|
|
|
2018-06-30 12:14:58 +02:00
|
|
|
QPointF WMSMap::ll2xy(const Coordinates &c)
|
2018-03-30 10:25:05 +02:00
|
|
|
{
|
2020-03-17 21:06:51 +01:00
|
|
|
return _transform.proj2img(_wms->projection().ll2xy(c)) / _mapRatio;
|
2018-03-30 10:25:05 +02:00
|
|
|
}
|
|
|
|
|
2018-06-30 12:14:58 +02:00
|
|
|
Coordinates WMSMap::xy2ll(const QPointF &p)
|
2018-03-30 10:25:05 +02:00
|
|
|
{
|
2020-03-17 21:06:51 +01:00
|
|
|
return _wms->projection().xy2ll(_transform.img2proj(p * _mapRatio));
|
2018-08-18 21:06:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
qreal WMSMap::tileSize() const
|
|
|
|
{
|
2020-03-01 13:59:15 +01:00
|
|
|
return (_tileSize / _mapRatio);
|
2018-03-30 10:25:05 +02:00
|
|
|
}
|
|
|
|
|
2018-08-23 20:26:10 +02:00
|
|
|
void WMSMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
|
2018-03-30 10:25:05 +02:00
|
|
|
{
|
2018-10-06 21:15:06 +02:00
|
|
|
QPoint tl = QPoint(qFloor(rect.left() / tileSize()),
|
|
|
|
qFloor(rect.top() / tileSize()));
|
|
|
|
QPoint br = QPoint(qCeil(rect.right() / tileSize()),
|
|
|
|
qCeil(rect.bottom() / tileSize()));
|
2018-03-30 10:25:05 +02:00
|
|
|
|
2022-07-06 14:58:41 +02:00
|
|
|
QVector<FetchTile> tiles;
|
2018-10-05 07:10:49 +02:00
|
|
|
tiles.reserve((br.x() - tl.x()) * (br.y() - tl.y()));
|
2018-03-30 10:25:05 +02:00
|
|
|
for (int i = tl.x(); i < br.x(); i++) {
|
|
|
|
for (int j = tl.y(); j < br.y(); j++) {
|
2020-03-01 13:59:15 +01:00
|
|
|
PointD ttl(_transform.img2proj(QPointF(i * _tileSize,
|
|
|
|
j * _tileSize)));
|
|
|
|
PointD tbr(_transform.img2proj(QPointF(i * _tileSize + _tileSize,
|
|
|
|
j * _tileSize + _tileSize)));
|
2020-03-17 21:06:51 +01:00
|
|
|
RectD bbox = (_wms->cs().axisOrder() == CoordinateSystem::YX)
|
2018-05-02 21:25:14 +02:00
|
|
|
? RectD(PointD(tbr.y(), tbr.x()), PointD(ttl.y(), ttl.x()))
|
|
|
|
: RectD(ttl, tbr);
|
2018-03-30 10:25:05 +02:00
|
|
|
|
2022-07-06 14:58:41 +02:00
|
|
|
tiles.append(FetchTile(QPoint(i, j), _zoom, bbox));
|
2018-03-30 10:25:05 +02: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-03-30 10:25:05 +02:00
|
|
|
else
|
2018-04-27 19:31:27 +02:00
|
|
|
_tileLoader->loadTilesAsync(tiles);
|
2018-03-30 10:25:05 +02:00
|
|
|
|
|
|
|
for (int i = 0; i < tiles.count(); i++) {
|
2022-07-06 14:58:41 +02:00
|
|
|
FetchTile &t = tiles[i];
|
2018-08-18 21:06:36 +02:00
|
|
|
QPointF tp(t.xy().x() * tileSize(), t.xy().y() * tileSize());
|
|
|
|
if (!t.pixmap().isNull()) {
|
2018-11-17 10:10:35 +01:00
|
|
|
t.pixmap().setDevicePixelRatio(_mapRatio);
|
2018-03-30 10:25:05 +02:00
|
|
|
painter->drawPixmap(tp, t.pixmap());
|
2018-08-18 21:06:36 +02:00
|
|
|
}
|
2018-03-30 10:25:05 +02:00
|
|
|
}
|
|
|
|
}
|