1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-11-28 13:41:16 +01:00
GPXSee/src/map/wmts.cpp

399 lines
11 KiB
C++
Raw Normal View History

2018-02-20 23:37:19 +01:00
#include <QXmlStreamReader>
#include <QFile>
#include <QFileInfo>
#include <QEventLoop>
#include <QTextStream>
2018-02-21 00:13:11 +01:00
#include <QStringList>
#include <QtAlgorithms>
2018-02-27 21:50:29 +01:00
#include <QXmlStreamReader>
2018-02-20 23:37:19 +01:00
#include "downloader.h"
#include "pcs.h"
#include "crs.h"
2018-02-20 23:37:19 +01:00
#include "wmts.h"
2020-03-03 09:29:16 +01:00
static QString bareFormat(const QString &format)
{
return format.left(format.indexOf(';')).trimmed();
}
static void skipParentElement(QXmlStreamReader &reader)
{
while (reader.readNextStartElement())
reader.skipCurrentElement();
}
WMTS::TileMatrix WMTS::tileMatrix(QXmlStreamReader &reader)
2018-02-20 23:37:19 +01:00
{
TileMatrix matrix;
2018-02-20 23:37:19 +01:00
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("Identifier"))
matrix.id = reader.readElementText();
else if (reader.name() == QLatin1String("ScaleDenominator"))
matrix.scaleDenominator = reader.readElementText().toDouble();
else if (reader.name() == QLatin1String("TopLeftCorner")) {
2018-02-20 23:37:19 +01:00
QString str = reader.readElementText();
QTextStream ts(&str);
ts >> matrix.topLeft.rx() >> matrix.topLeft.ry();
} else if (reader.name() == QLatin1String("TileWidth"))
matrix.tile.setWidth(reader.readElementText().toInt());
else if (reader.name() == QLatin1String("TileHeight"))
matrix.tile.setHeight(reader.readElementText().toInt());
else if (reader.name() == QLatin1String("MatrixWidth"))
matrix.matrix.setWidth(reader.readElementText().toInt());
else if (reader.name() == QLatin1String("MatrixHeight"))
matrix.matrix.setHeight(reader.readElementText().toInt());
2018-02-20 23:37:19 +01:00
else
reader.skipCurrentElement();
}
if (!matrix.isValid())
reader.raiseError("Invalid TileMatrix definition");
return matrix;
2018-02-20 23:37:19 +01:00
}
2018-02-27 21:50:29 +01:00
void WMTS::tileMatrixSet(QXmlStreamReader &reader, CTX &ctx)
2018-02-20 23:37:19 +01:00
{
2018-02-27 21:50:29 +01:00
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("Identifier")) {
if (reader.readElementText() != _setup.set()) {
skipParentElement(reader);
return;
}
} else if (reader.name() == QLatin1String("SupportedCRS"))
ctx.crs = reader.readElementText();
else if (reader.name() == QLatin1String("TileMatrix"))
ctx.matrixes.insert(tileMatrix(reader));
2018-02-20 23:37:19 +01:00
else
2018-02-27 21:50:29 +01:00
reader.skipCurrentElement();
2018-02-20 23:37:19 +01:00
}
}
WMTS::MatrixLimits WMTS::tileMatrixLimits(QXmlStreamReader &reader)
{
MatrixLimits limits;
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("TileMatrix"))
limits.id = reader.readElementText();
else if (reader.name() == QLatin1String("MinTileRow"))
limits.rect.setTop(reader.readElementText().toInt());
else if (reader.name() == QLatin1String("MaxTileRow"))
limits.rect.setBottom(reader.readElementText().toInt());
else if (reader.name() == QLatin1String("MinTileCol"))
limits.rect.setLeft(reader.readElementText().toInt());
else if (reader.name() == QLatin1String("MaxTileCol"))
limits.rect.setRight(reader.readElementText().toInt());
else
reader.skipCurrentElement();
}
if (!limits.isValid())
reader.raiseError("Invalid TileMatrixLimits definition");
return limits;
}
QSet<WMTS::MatrixLimits> WMTS::tileMatrixSetLimits(QXmlStreamReader &reader)
{
QSet<MatrixLimits> limits;
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("TileMatrixLimits"))
limits.insert(tileMatrixLimits(reader));
else
reader.skipCurrentElement();
}
return limits;
}
2018-02-27 21:50:29 +01:00
void WMTS::tileMatrixSetLink(QXmlStreamReader &reader, CTX &ctx)
{
2018-02-27 21:50:29 +01:00
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("TileMatrixSet")) {
if (reader.readElementText() == _setup.set())
ctx.hasSet = true;
else {
skipParentElement(reader);
return;
}
} else if (reader.name() == QLatin1String("TileMatrixSetLimits"))
ctx.limits = tileMatrixSetLimits(reader);
else
2018-02-27 21:50:29 +01:00
reader.skipCurrentElement();
}
}
RectC WMTS::wgs84BoundingBox(QXmlStreamReader &reader)
{
Coordinates topLeft, bottomRight;
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("LowerCorner")) {
QString str = reader.readElementText();
QTextStream(&str) >> topLeft.rlon() >> bottomRight.rlat();
} else if (reader.name() == QLatin1String("UpperCorner")) {
QString str = reader.readElementText();
QTextStream(&str) >> bottomRight.rlon() >> topLeft.rlat();
} else
reader.skipCurrentElement();
}
return RectC(topLeft, bottomRight);
}
2018-02-26 22:35:58 +01:00
QString WMTS::style(QXmlStreamReader &reader)
{
QString id;
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("Identifier"))
id = reader.readElementText();
2018-02-26 22:35:58 +01:00
else
reader.skipCurrentElement();
}
return id;
}
2018-02-27 21:50:29 +01:00
void WMTS::layer(QXmlStreamReader &reader, CTX &ctx)
2018-02-26 22:35:58 +01:00
{
2018-02-27 21:50:29 +01:00
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("Identifier")) {
if (reader.readElementText() == _setup.layer())
ctx.hasLayer = true;
else {
skipParentElement(reader);
return;
}
} else if (reader.name() == QLatin1String("TileMatrixSetLink"))
2018-02-27 21:50:29 +01:00
tileMatrixSetLink(reader, ctx);
else if (reader.name() == QLatin1String("WGS84BoundingBox"))
ctx.bbox = wgs84BoundingBox(reader);
else if (reader.name() == QLatin1String("ResourceURL")) {
2018-02-27 21:50:29 +01:00
const QXmlStreamAttributes &attr = reader.attributes();
if (attr.value("resourceType") == QLatin1String("tile")
&& _setup.rest())
_tileUrl = attr.value("template").toString();
2018-02-27 21:50:29 +01:00
reader.skipCurrentElement();
} else if (reader.name() == QLatin1String("Style")) {
const QXmlStreamAttributes &attr = reader.attributes();
bool isDefault = (attr.value("isDefault") == QLatin1String("true"));
QString s = style(reader);
if (isDefault)
ctx.defaultStyle = s;
if (s == _setup.style())
ctx.hasStyle = true;
} else if (reader.name() == QLatin1String("Format")) {
QString format(reader.readElementText());
if (bareFormat(format) == bareFormat(_setup.format()))
ctx.hasFormat = true;
} else
2018-02-27 21:50:29 +01:00
reader.skipCurrentElement();
}
}
2018-02-27 21:50:29 +01:00
void WMTS::contents(QXmlStreamReader &reader, CTX &ctx)
2018-02-20 23:37:19 +01:00
{
2018-02-27 21:50:29 +01:00
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("TileMatrixSet"))
2018-02-27 21:50:29 +01:00
tileMatrixSet(reader, ctx);
else if (reader.name() == QLatin1String("Layer"))
2018-02-27 21:50:29 +01:00
layer(reader, ctx);
2018-02-20 23:37:19 +01:00
else
2018-02-27 21:50:29 +01:00
reader.skipCurrentElement();
2018-02-20 23:37:19 +01:00
}
}
2018-02-27 21:50:29 +01:00
void WMTS::capabilities(QXmlStreamReader &reader, CTX &ctx)
2018-02-20 23:37:19 +01:00
{
2018-02-27 21:50:29 +01:00
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("Contents"))
2018-02-27 21:50:29 +01:00
contents(reader, ctx);
2018-02-20 23:37:19 +01:00
else
2018-02-27 21:50:29 +01:00
reader.skipCurrentElement();
2018-02-20 23:37:19 +01:00
}
}
void WMTS::createZooms(const CTX &ctx)
{
for (QSet<TileMatrix>::const_iterator mi = ctx.matrixes.constBegin();
mi != ctx.matrixes.constEnd(); ++mi) {
QSet<MatrixLimits>::const_iterator li = ctx.limits.find(
MatrixLimits(mi->id));
if (!ctx.limits.isEmpty() && li == ctx.limits.constEnd())
continue;
_zooms.append(Zoom(mi->id, mi->scaleDenominator, mi->topLeft, mi->tile,
mi->matrix, li == ctx.limits.constEnd() ? QRect() : li->rect));
}
std::sort(_zooms.begin(), _zooms.end());
}
bool WMTS::parseCapabilities(CTX &ctx)
2018-02-20 23:37:19 +01:00
{
QFile file(_path);
2018-02-27 21:50:29 +01:00
QXmlStreamReader reader;
2018-02-20 23:37:19 +01:00
if (!file.open(QFile::ReadOnly | QFile::Text)) {
_errorString = file.errorString();
return false;
}
2018-02-27 21:50:29 +01:00
reader.setDevice(&file);
if (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("Capabilities"))
2018-02-27 21:50:29 +01:00
capabilities(reader, ctx);
2018-02-20 23:37:19 +01:00
else
2018-02-27 21:50:29 +01:00
reader.raiseError("Not a Capabilities XML file");
2018-02-26 22:35:58 +01:00
}
2018-02-27 21:50:29 +01:00
if (reader.error()) {
_errorString = QString("%1:%2: %3").arg(_path).arg(reader.lineNumber())
2018-02-27 21:50:29 +01:00
.arg(reader.errorString());
2018-02-26 22:35:58 +01:00
return false;
2018-02-20 23:37:19 +01:00
}
if (!ctx.hasLayer) {
_errorString = _setup.layer() + ": layer not provided";
2018-02-26 22:35:58 +01:00
return false;
}
if (!ctx.hasStyle && !_setup.style().isEmpty()) {
_errorString = _setup.style() + ": style not provided";
2018-02-26 22:35:58 +01:00
return false;
}
if (!ctx.hasStyle && _setup.style().isEmpty()
2019-02-15 20:32:53 +01:00
&& ctx.defaultStyle.isEmpty()) {
_errorString = "Default style not provided";
return false;
}
if (!_setup.rest() && !ctx.hasFormat) {
_errorString = _setup.format() + ": format not provided";
2018-02-26 22:35:58 +01:00
return false;
}
if (!ctx.hasSet) {
_errorString = _setup.set() + ": set not provided";
2018-02-26 22:35:58 +01:00
return false;
}
if (ctx.crs.isNull()) {
_errorString = "Missing CRS definition";
return false;
}
_projection = CRS::projection(ctx.crs);
2018-05-16 18:52:48 +02:00
if (!_projection.isValid()) {
2018-02-26 22:35:58 +01:00
_errorString = ctx.crs + ": unknown CRS";
return false;
}
2018-10-26 18:47:34 +02:00
createZooms(ctx);
if (_zooms.isEmpty()) {
2018-02-26 22:35:58 +01:00
_errorString = "No usable tile matrix found";
return false;
}
if (_setup.rest() && _tileUrl.isNull()) {
2018-02-26 22:35:58 +01:00
_errorString = "Missing tile URL template";
return false;
}
_bbox = ctx.bbox;
_cs = (_setup.coordinateSystem().axisOrder() == CoordinateSystem::Unknown)
? _projection.coordinateSystem() : _setup.coordinateSystem();
2018-02-20 23:37:19 +01:00
2018-02-26 22:35:58 +01:00
return true;
2018-02-20 23:37:19 +01:00
}
bool WMTS::downloadCapabilities(const QString &url)
2018-02-20 23:37:19 +01:00
{
if (!_downloader) {
_downloader = new Downloader(this);
connect(_downloader, SIGNAL(finished()), this,
SLOT(capabilitiesReady()));
}
2018-02-20 23:37:19 +01:00
QList<Download> dl;
dl.append(Download(url, _path));
2018-02-20 23:37:19 +01:00
return _downloader->get(dl, _setup.authorization());
}
2018-02-20 23:37:19 +01:00
void WMTS::capabilitiesReady()
{
if (!QFileInfo(_path).exists()) {
2018-02-20 23:37:19 +01:00
_errorString = "Error downloading capabilities XML file";
_valid = false;
} else {
_ready = true;
_valid = init();
2018-02-20 23:37:19 +01:00
}
2019-03-05 22:34:50 +01:00
emit downloadFinished();
2018-02-20 23:37:19 +01:00
}
bool WMTS::init()
2018-02-20 23:37:19 +01:00
{
CTX ctx;
if (!parseCapabilities(ctx))
return false;
2018-02-20 23:37:19 +01:00
QString style = _setup.style().isEmpty() ? ctx.defaultStyle : _setup.style();
if (!_setup.rest()) {
2018-09-16 12:05:11 +02:00
_tileUrl = QString("%1%2service=WMTS&Version=1.0.0&request=GetTile"
"&Format=%3&Layer=%4&Style=%5&TileMatrixSet=%6&TileMatrix=$z"
"&TileRow=$y&TileCol=$x").arg(_setup.url(),
_setup.url().contains('?') ? "&" : "?" , _setup.format(),
_setup.layer(), style, _setup.set());
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
_tileUrl.append(QString("&%1=%2").arg(dim.key(), dim.value()));
2018-03-11 10:31:41 +01:00
}
} else {
_tileUrl.replace("{Style}", style, Qt::CaseInsensitive);
_tileUrl.replace("{TileMatrixSet}", _setup.set(), Qt::CaseInsensitive);
_tileUrl.replace("{TileMatrix}", "$z", Qt::CaseInsensitive);
_tileUrl.replace("{TileRow}", "$y", Qt::CaseInsensitive);
_tileUrl.replace("{TileCol}", "$x", Qt::CaseInsensitive);
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
_tileUrl.replace(QString("{%1}").arg(dim.key()), dim.value(),
Qt::CaseInsensitive);
2018-03-11 10:31:41 +01:00
}
2018-02-25 02:31:01 +01:00
}
return true;
}
WMTS::WMTS(const QString &file, const WMTS::Setup &setup, QObject *parent)
: QObject(parent), _setup(setup), _downloader(0), _valid(false), _ready(false)
{
QUrl url(setup.rest() ? setup.url() : QString(
"%1%2service=WMTS&Version=1.0.0&request=GetCapabilities").arg(setup.url(),
setup.url().contains('?') ? "&" : "?"));
_path = url.isLocalFile() ? url.toLocalFile() : file;
if (!url.isLocalFile() && !QFileInfo(file).exists())
_valid = downloadCapabilities(url.toString());
else {
_ready = true;
_valid = init();
}
2018-02-20 23:37:19 +01:00
}
#ifndef QT_NO_DEBUG
2018-02-25 02:31:01 +01:00
QDebug operator<<(QDebug dbg, const WMTS::Setup &setup)
{
dbg.nospace() << "Setup(" << setup.url() << ", " << setup.layer() << ", "
<< setup.set() << ", " << setup.style() << ", " << setup.format() << ", "
<< setup.rest() << ")";
2018-02-25 02:31:01 +01:00
return dbg.space();
}
QDebug operator<<(QDebug dbg, const WMTS::Zoom &zoom)
{
dbg.nospace() << "Zoom(" << zoom.id() << ", " << zoom.scaleDenominator()
<< ", " << zoom.topLeft() << ", " << zoom.tile() << ", " << zoom.matrix()
<< ", " << zoom.limits() << ")";
return dbg.space();
}
#endif // QT_NO_DEBUG