From ee80260e46df3ba4c3a197df9437cd72999be228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20T=C5=AFma?= Date: Sat, 24 Feb 2018 16:44:30 +0100 Subject: [PATCH] Improved capabilities xml parsing Improved bounds handling --- src/map/wmts.cpp | 149 +++++++++++++++++++++++++++++--------------- src/map/wmts.h | 67 +++++++++++++++++--- src/map/wmtsmap.cpp | 19 +++--- src/map/wmtsmap.h | 1 + 4 files changed, 169 insertions(+), 67 deletions(-) diff --git a/src/map/wmts.cpp b/src/map/wmts.cpp index 089f8de4..25bcae60 100644 --- a/src/map/wmts.cpp +++ b/src/map/wmts.cpp @@ -58,115 +58,156 @@ bool WMTS::createProjection(const QString &crs) return false; } -void WMTS::tileMatrix(QXmlStreamReader &reader) +WMTS::TileMatrix WMTS::tileMatrix(QXmlStreamReader &reader) { - Zoom zoom; + TileMatrix matrix; while (reader.readNextStartElement()) { if (reader.name() == "Identifier") - zoom.id = reader.readElementText(); + matrix.id = reader.readElementText(); else if (reader.name() == "ScaleDenominator") - zoom.scaleDenominator = reader.readElementText().toDouble(); + matrix.scaleDenominator = reader.readElementText().toDouble(); else if (reader.name() == "TopLeftCorner") { QString str = reader.readElementText(); - QTextStream(&str) >> zoom.topLeft.rx() >> zoom.topLeft.ry(); + QTextStream(&str) >> matrix.topLeft.rx() >> matrix.topLeft.ry(); } else if (reader.name() == "TileWidth") - zoom.tile.setWidth(reader.readElementText().toInt()); + matrix.tile.setWidth(reader.readElementText().toInt()); else if (reader.name() == "TileHeight") - zoom.tile.setHeight(reader.readElementText().toInt()); + matrix.tile.setHeight(reader.readElementText().toInt()); else if (reader.name() == "MatrixWidth") - zoom.matrix.setWidth(reader.readElementText().toInt()); + matrix.matrix.setWidth(reader.readElementText().toInt()); else if (reader.name() == "MatrixHeight") - zoom.matrix.setHeight(reader.readElementText().toInt()); + matrix.matrix.setHeight(reader.readElementText().toInt()); else reader.skipCurrentElement(); } - Zoom &z = _zooms[zoom.id]; - z.id = zoom.id; - z.matrix = zoom.matrix; - z.scaleDenominator = zoom.scaleDenominator; - z.tile = zoom.tile; - z.topLeft = zoom.topLeft; + if (!matrix.isValid()) + reader.raiseError("Invalid TileMatrix definition"); + + return matrix; } void WMTS::tileMatrixSet(QXmlStreamReader &reader, const QString &set) { - QString id; + QString id, crs; + QSet matrixes; while (reader.readNextStartElement()) { if (reader.name() == "Identifier") id = reader.readElementText(); - else if (reader.name() == "SupportedCRS" && id == set) { - if (!createProjection(reader.readElementText())) - reader.raiseError("Invalid/unknown CRS"); - } else if (reader.name() == "TileMatrix" && id == set) - tileMatrix(reader); + else if (reader.name() == "SupportedCRS") + crs = reader.readElementText(); + else if (reader.name() == "TileMatrix") + matrixes.insert(tileMatrix(reader)); else reader.skipCurrentElement(); } + + if (id == set) { + if (!createProjection(crs)) { + reader.raiseError("Invalid/unknown CRS"); + return; + } + + _matrixes.unite(matrixes); + } } -void WMTS::tileMatrixLimits(QXmlStreamReader &reader) +WMTS::MatrixLimits WMTS::tileMatrixLimits(QXmlStreamReader &reader) { - QString id; - QRect limits; + MatrixLimits limits; while (reader.readNextStartElement()) { if (reader.name() == "TileMatrix") - id = reader.readElementText(); + limits.id = reader.readElementText(); else if (reader.name() == "MinTileRow") - limits.setTop(reader.readElementText().toInt()); + limits.rect.setTop(reader.readElementText().toInt()); else if (reader.name() == "MaxTileRow") - limits.setBottom(reader.readElementText().toInt()); + limits.rect.setBottom(reader.readElementText().toInt()); else if (reader.name() == "MinTileCol") - limits.setLeft(reader.readElementText().toInt()); + limits.rect.setLeft(reader.readElementText().toInt()); else if (reader.name() == "MaxTileCol") - limits.setRight(reader.readElementText().toInt()); + limits.rect.setRight(reader.readElementText().toInt()); else reader.skipCurrentElement(); } - _zooms[id].limits = limits; + if (!limits.isValid()) + reader.raiseError("Invalid TileMatrixLimits definition"); + + return limits; } -void WMTS::tileMatrixSetLimits(QXmlStreamReader &reader) +QSet WMTS::tileMatrixSetLimits(QXmlStreamReader &reader) { + QSet limits; + while (reader.readNextStartElement()) { if (reader.name() == "TileMatrixLimits") - tileMatrixLimits(reader); + limits.insert(tileMatrixLimits(reader)); else reader.skipCurrentElement(); } + + return limits; } void WMTS::tileMatrixSetLink(QXmlStreamReader &reader, const QString &set) { QString id; + QSet limits; while (reader.readNextStartElement()) { if (reader.name() == "TileMatrixSet") id = reader.readElementText(); - else if (reader.name() == "TileMatrixSetLimits" && id == set) - tileMatrixSetLimits(reader); + else if (reader.name() == "TileMatrixSetLimits") + limits = tileMatrixSetLimits(reader); else reader.skipCurrentElement(); } + + if (id == set) + _limits.unite(limits); +} + +RectC WMTS::wgs84BoundingBox(QXmlStreamReader &reader) +{ + Coordinates topLeft, bottomRight; + + while (reader.readNextStartElement()) { + if (reader.name() == "LowerCorner") { + QString str = reader.readElementText(); + QTextStream(&str) >> topLeft.rlon() >> bottomRight.rlat(); + } else if (reader.name() == "UpperCorner") { + QString str = reader.readElementText(); + QTextStream(&str) >> bottomRight.rlon() >> topLeft.rlat(); + } else + reader.skipCurrentElement(); + } + + return RectC(topLeft, bottomRight); } void WMTS::layer(QXmlStreamReader &reader, const QString &layer, const QString &set) { QString id; + RectC bounds; while (reader.readNextStartElement()) { if (reader.name() == "Identifier") id = reader.readElementText(); - else if (reader.name() == "TileMatrixSetLink" && id == layer) + else if (reader.name() == "TileMatrixSetLink") tileMatrixSetLink(reader, set); + else if (reader.name() == "WGS84BoundingBox") + bounds = wgs84BoundingBox(reader); else reader.skipCurrentElement(); } + + if (id == layer) + _bounds = bounds; } void WMTS::contents(QXmlStreamReader &reader, const QString &layer, @@ -251,36 +292,44 @@ bool WMTS::load(const QString &file, const QString &url, const QString &layer, if (!parseCapabilities(file, layer, set)) return false; + if (_matrixes.isEmpty()) { + _errorString = "No usable tile matrix found"; + return false; + } if (_projection.isNull()) { _errorString = "Missing CRS definition"; return false; } - if (_zooms.isEmpty()) { - _errorString = "No tile matrix found"; - return false; - } - for (it = _zooms.constBegin(); it != _zooms.constEnd(); ++it) { - if (!it->isValid()) { - _errorString = it->id + ": invalid tile matrix definition"; - return false; - } - } return true; } QList WMTS::zooms() const { - QList z(_zooms.values()); - qSort(z); - return z; + QList zooms; + QSet::const_iterator mi; + QSet::const_iterator li; + + for (mi = _matrixes.constBegin(); mi != _matrixes.constEnd(); ++mi) { + if ((li = _limits.find(MatrixLimits(mi->id))) == _limits.constEnd()) + zooms.append(Zoom(mi->id, mi->scaleDenominator, mi->topLeft, + mi->tile, mi->matrix, QRect())); + else + zooms.append(Zoom(Zoom(mi->id, mi->scaleDenominator, mi->topLeft, + mi->tile, mi->matrix, li->rect))); + } + + qSort(zooms); + + return zooms; } #ifndef QT_NO_DEBUG QDebug operator<<(QDebug dbg, const WMTS::Zoom &zoom) { - dbg.nospace() << "Zoom(" << zoom.scaleDenominator << ", " << zoom.topLeft - << ", " << zoom.tile << ", " << zoom.matrix << ", " << zoom.limits << ")"; + dbg.nospace() << "Zoom(" << zoom.id << ", " << zoom.scaleDenominator << ", " + << zoom.topLeft << ", " << zoom.tile << ", " << zoom.matrix << ", " + << zoom.limits << ")"; return dbg.space(); } #endif // QT_NO_DEBUG diff --git a/src/map/wmts.h b/src/map/wmts.h index 4bab87eb..08c1b041 100644 --- a/src/map/wmts.h +++ b/src/map/wmts.h @@ -2,8 +2,10 @@ #define WMTS_H #include -#include +#include #include +#include +#include "common/rectc.h" #include "projection.h" class QXmlStreamReader; @@ -20,18 +22,20 @@ public: QSize matrix; QRect limits; - Zoom() : scaleDenominator(0) {} + Zoom() {} + Zoom(const QString &id, qreal scaleDenominator, const QPointF &topLeft, + const QSize &tile, const QSize &matrix, const QRect &limits) : + id(id), scaleDenominator(scaleDenominator), topLeft(topLeft), + tile(tile), matrix(matrix), limits(limits) {} bool operator<(const Zoom &other) const - {return this->scaleDenominator > other.scaleDenominator;} - bool isValid() const - {return scaleDenominator > 0 && !id.isEmpty() && tile.isValid() - && matrix.isValid();} + {return scaleDenominator > other.scaleDenominator;} }; bool load(const QString &path, const QString &url, const QString &layer, const QString &set); const QString &errorString() const {return _errorString;} + const RectC &bounds() const {return _bounds;} QList zooms() const; const Projection &projection() const {return _projection;} @@ -40,11 +44,39 @@ public: {_downloader = downloader;} private: + struct TileMatrix { + QString id; + qreal scaleDenominator; + QPointF topLeft; + QSize tile; + QSize matrix; + + TileMatrix() : scaleDenominator(0) {} + bool operator==(const TileMatrix &other) const + {return this->id == other.id;} + bool isValid() const + {return !id.isEmpty() && scaleDenominator > 0 && tile.isValid() + && matrix.isValid();} + }; + + struct MatrixLimits { + QString id; + QRect rect; + + MatrixLimits() {} + MatrixLimits(const QString &id) : id(id) {} + bool operator==(const MatrixLimits &other) const + {return this->id == other.id;} + bool isValid() const + {return !id.isEmpty() && rect.isValid();} + }; + bool createProjection(const QString &crs); - void tileMatrixLimits(QXmlStreamReader &reader); - void tileMatrix(QXmlStreamReader &reader); - void tileMatrixSetLimits(QXmlStreamReader &reader); + RectC wgs84BoundingBox(QXmlStreamReader &reader); + MatrixLimits tileMatrixLimits(QXmlStreamReader &reader); + TileMatrix tileMatrix(QXmlStreamReader &reader); + QSet tileMatrixSetLimits(QXmlStreamReader &reader); void tileMatrixSet(QXmlStreamReader &reader, const QString &set); void tileMatrixSetLink(QXmlStreamReader &reader, const QString &set); void layer(QXmlStreamReader &reader, const QString &layer, @@ -57,14 +89,29 @@ private: const QString &set); bool getCapabilities(const QString &url, const QString &file); - QMap _zooms; + QSet _matrixes; + QSet _limits; + RectC _bounds; Projection _projection; QString _errorString; static Downloader *_downloader; + + friend uint qHash(const WMTS::TileMatrix &key); + friend uint qHash(const WMTS::MatrixLimits &key); }; +inline uint qHash(const WMTS::TileMatrix &key) +{ + return ::qHash(key.id); +} + +inline uint qHash(const WMTS::MatrixLimits &key) +{ + return ::qHash(key.id); +} + #ifndef QT_NO_DEBUG QDebug operator<<(QDebug dbg, const WMTS::Zoom &zoom); #endif // QT_NO_DEBUG diff --git a/src/map/wmtsmap.cpp b/src/map/wmtsmap.cpp index 255f7dee..95d37d55 100644 --- a/src/map/wmtsmap.cpp +++ b/src/map/wmtsmap.cpp @@ -29,6 +29,7 @@ WMTSMap::WMTSMap(const QString &name, const QString &url, const QString &format, _errorString = wmts.errorString(); return; } + _bounds = wmts.bounds(); _zooms = wmts.zooms(); _projection = wmts.projection(); @@ -108,14 +109,18 @@ void WMTSMap::emitLoaded() QRectF WMTSMap::bounds() const { const WMTS::Zoom &z = _zooms.at(_zoom); + QRectF tileBounds, bounds; - if (z.limits.isNull()) - return QRectF(QPointF(0, 0), QSize(z.tile.width() * z.matrix.width(), - z.tile.height() * z.matrix.height())); - else - return 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())); + 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())); + + bounds = _bounds.isValid() ? QRectF(ll2xy(_bounds.topLeft()), + ll2xy(_bounds.bottomRight())) : QRectF(); + + return _bounds.isValid() ? tileBounds.intersected(bounds) : tileBounds; } qreal WMTSMap::zoomFit(const QSize &size, const RectC &br) diff --git a/src/map/wmtsmap.h b/src/map/wmtsmap.h index 40811bac..14127450 100644 --- a/src/map/wmtsmap.h +++ b/src/map/wmtsmap.h @@ -56,6 +56,7 @@ private: QString _name, _url, _layer, _set; TileLoader _tileLoader; + RectC _bounds; QList _zooms; bool _invertAxis; Projection _projection;