From e845e216bd8c41a4d247745ef99f062d92b19788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20T=C5=AFma?= Date: Mon, 2 Apr 2018 19:59:52 +0200 Subject: [PATCH] Added support for WMS multi-layer maps --- src/common/range.cpp | 9 +++++ src/common/range.h | 4 ++ src/map/wms.cpp | 93 ++++++++++++++++++++++++++++++-------------- src/map/wms.h | 27 +++++++++---- 4 files changed, 96 insertions(+), 37 deletions(-) diff --git a/src/common/range.cpp b/src/common/range.cpp index e07011e7..484df700 100644 --- a/src/common/range.cpp +++ b/src/common/range.cpp @@ -8,6 +8,15 @@ void RangeF::resize(qreal size) _max += adj; } +RangeF RangeF::operator|(const RangeF &r) const +{ + if (isNull()) + return r; + if (r.isNull()) + return *this; + return RangeF(qMax(this->_min, r._min), qMin(this->_max, r._max)); +} + #ifndef QT_NO_DEBUG QDebug operator<<(QDebug dbg, const Range &range) { diff --git a/src/common/range.h b/src/common/range.h index cb8c875a..b4d2f771 100644 --- a/src/common/range.h +++ b/src/common/range.h @@ -29,10 +29,14 @@ public: RangeF() {_min = 0; _max = 0;} RangeF(qreal min, qreal max) {_min = min, _max = max;} + RangeF operator|(const RangeF &r) const; + RangeF &operator|=(const RangeF &r) {*this = *this | r; return *this;} + qreal min() const {return _min;} qreal max() const {return _max;} qreal size() const {return (_max - _min);} + bool isNull() const {return _min == 0 && _max == 0;} bool isValid() const {return size() >= 0;} void setMin(qreal min) {_min = min;} diff --git a/src/map/wms.cpp b/src/map/wms.cpp index c68e5c31..b8e9dab6 100644 --- a/src/map/wms.cpp +++ b/src/map/wms.cpp @@ -8,12 +8,27 @@ Downloader *WMS::_downloader = 0; +WMS::CTX::CTX(const Setup &setup) : setup(setup), formatSupported(false) +{ + QStringList ll = setup.layer().split(','); + QStringList sl = setup.style().split(','); + + if (ll.size() != sl.size()) + return; + + for (int i = 0; i < ll.size(); i++) { + layers.append(Layer(ll.at(i), sl.at(i))); + if (sl.at(i) == "default") + layers[i].hasStyle = true; + } +} + void WMS::getMap(QXmlStreamReader &reader, CTX &ctx) { while (reader.readNextStartElement()) { if (reader.name() == "Format") { if (reader.readElementText() == ctx.setup.format()) - ctx.format = true; + ctx.formatSupported = true; } else reader.skipCurrentElement(); } @@ -72,6 +87,7 @@ void WMS::layer(QXmlStreamReader &reader, CTX &ctx, QList styles(pStyles); RangeF scaleDenominator(pScaleDenominator); RectC boundingBox(pBoundingBox); + int index; while (reader.readNextStartElement()) { if (reader.name() == "Name") @@ -100,12 +116,13 @@ void WMS::layer(QXmlStreamReader &reader, CTX &ctx, reader.skipCurrentElement(); } - if (name == ctx.setup.layer()) { - ctx.scaleDenominator = scaleDenominator; - ctx.boundingBox = boundingBox; - ctx.layer = true; - ctx.style = styles.contains(ctx.setup.style()); - ctx.crs = CRSs.contains(ctx.setup.crs()); + if ((index = ctx.layers.indexOf(name)) >= 0) { + Layer &layer = ctx.layers[index]; + layer.scaleDenominator = scaleDenominator; + layer.boundingBox = boundingBox; + layer.isDefined = true; + layer.hasStyle = styles.contains(layer.style); + layer.hasCRS = CRSs.contains(ctx.setup.crs()); } } @@ -144,6 +161,12 @@ bool WMS::parseCapabilities(const QString &path, const Setup &setup) CTX ctx(setup); QXmlStreamReader reader; + + if (ctx.layers.isEmpty()) { + _errorString = "Invalid layers definition"; + return false; + } + if (!file.open(QFile::ReadOnly | QFile::Text)) { _errorString = file.errorString(); return false; @@ -163,38 +186,50 @@ bool WMS::parseCapabilities(const QString &path, const Setup &setup) return false; } - if (!ctx.layer) { - _errorString = ctx.setup.layer() + ": layer not provided"; - return false; - } - if (!ctx.style && ctx.setup.style() != "default") { - _errorString = ctx.setup.style() + ": style not provided"; - return false; - } - if (!ctx.format) { + if (!ctx.formatSupported) { _errorString = ctx.setup.format() + ": format not provided"; return false; } - if (!ctx.crs) { - _errorString = ctx.setup.crs() + ": CRS not provided"; - return false; + + for (int i = 0; i < ctx.layers.size(); i++) { + const Layer &layer = ctx.layers.at(i); + + if (!layer.isDefined) { + _errorString = ctx.setup.layer() + ": layer not provided"; + return false; + } + if (!layer.hasStyle) { + _errorString = ctx.setup.style() + ": style not provided for layer " + + layer.name; + return false; + } + if (!layer.hasCRS) { + _errorString = ctx.setup.crs() + ": CRS not provided for layer " + + layer.name; + return false; + } + if (!layer.scaleDenominator.isValid()) { + _errorString = "Invalid scale denominator range for layer" + + layer.name; + return false; + } + if (!layer.boundingBox.isValid()) { + _errorString = "Invalid/missing bounding box for layer" + + layer.name; + return false; + } } + _projection = CRS::projection(ctx.setup.crs()); if (_projection.isNull()) { _errorString = ctx.setup.crs() + ": unknown CRS"; return false; } - if (!ctx.scaleDenominator.isValid()) { - _errorString = "Invalid scale denominator range"; - return false; - } - if (ctx.boundingBox.isNull()) { - _errorString = "Missing bounding box"; - return false; - } - _boundingBox = ctx.boundingBox; - _scaleDenominator = ctx.scaleDenominator; + for (int i = 0; i < ctx.layers.size(); i++) + _boundingBox |= ctx.layers.at(i).boundingBox; + for (int i = 0; i < ctx.layers.size(); i++) + _scaleDenominator |= ctx.layers.first().scaleDenominator; return true; } diff --git a/src/map/wms.h b/src/map/wms.h index 00f1f82a..4968e0be 100644 --- a/src/map/wms.h +++ b/src/map/wms.h @@ -55,17 +55,28 @@ public: {_downloader = downloader;} private: - struct CTX { - const Setup &setup; + struct Layer { + QString name; + QString style; RangeF scaleDenominator; RectC boundingBox; - bool layer; - bool style; - bool format; - bool crs; + bool isDefined; + bool hasStyle; + bool hasCRS; - CTX(const Setup &setup) : setup(setup), layer(false), style(false), - format(false), crs(false) {} + Layer(const QString &name, const QString &style = "default") + : name(name), style(style), isDefined(false), hasStyle(false), + hasCRS(false) {} + bool operator==(const Layer &other) const + {return this->name == other.name;} + }; + + struct CTX { + const Setup &setup; + QList layers; + bool formatSupported; + + CTX(const Setup &setup); }; RectC geographicBoundingBox(QXmlStreamReader &reader);