From e3e8fdbacf85deb29e2f4f79621f8da7dc96e9c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20T=C5=AFma?= Date: Sun, 1 Apr 2018 20:01:25 +0200 Subject: [PATCH] Fixed WMS 1.1.x support Added support for HTTP basic authorization --- src/map/downloader.cpp | 20 ++++++++++--- src/map/downloader.h | 23 +++++++++++---- src/map/mapsource.cpp | 11 +++++-- src/map/mapsource.h | 2 ++ src/map/tileloader.cpp | 4 +-- src/map/tileloader.h | 9 +++--- src/map/wms.cpp | 66 ++++++++++++++++++++++++++++-------------- src/map/wms.h | 23 ++++++++++----- src/map/wmsmap.cpp | 8 ++--- src/map/wmts.cpp | 7 +++-- src/map/wmts.h | 13 ++++++--- src/map/wmtsmap.cpp | 3 +- 12 files changed, 131 insertions(+), 58 deletions(-) diff --git a/src/map/downloader.cpp b/src/map/downloader.cpp index d75f4b40..6976686d 100644 --- a/src/map/downloader.cpp +++ b/src/map/downloader.cpp @@ -29,6 +29,13 @@ #define TIMEOUT 30 /* s */ +Authorization::Authorization(const QString &username, const QString &password) +{ + QString concatenated = username + ":" + password; + QByteArray data = concatenated.toLocal8Bit().toBase64(); + _header = "Basic " + data; +} + class Downloader::ReplyTimeout : public QObject { public: @@ -79,7 +86,8 @@ Downloader::Downloader(QObject *parent) : QObject(parent) SLOT(downloadFinished(QNetworkReply*))); } -bool Downloader::doDownload(const Download &dl, const Redirect *redirect) +bool Downloader::doDownload(const Download &dl, + const QByteArray &authorization, const Redirect *redirect) { QUrl url(dl.url()); @@ -95,6 +103,8 @@ bool Downloader::doDownload(const Download &dl, const Redirect *redirect) request.setAttribute(ATTR_LEVEL, QVariant(redirect->level())); } request.setRawHeader("User-Agent", USER_AGENT); + if (!authorization.isNull()) + request.setRawHeader("Authorization", authorization); QNetworkReply *reply = _manager.get(request); if (reply) { @@ -159,7 +169,8 @@ void Downloader::downloadFinished(QNetworkReply *reply) } else { Redirect redirect(origin.isEmpty() ? url : origin, level + 1); Download dl(location, filename); - doDownload(dl, &redirect); + doDownload(dl, reply->request().rawHeader("Authorization"), + &redirect); } } else if (!saveToDisk(filename, reply)) @@ -173,12 +184,13 @@ void Downloader::downloadFinished(QNetworkReply *reply) emit finished(); } -bool Downloader::get(const QList &list) +bool Downloader::get(const QList &list, + const Authorization &authorization) { bool finishEmitted = false; for (int i = 0; i < list.count(); i++) - finishEmitted |= doDownload(list.at(i)); + finishEmitted |= doDownload(list.at(i), authorization.header()); return finishEmitted; } diff --git a/src/map/downloader.h b/src/map/downloader.h index 2db99f74..3adb6eb9 100644 --- a/src/map/downloader.h +++ b/src/map/downloader.h @@ -1,4 +1,4 @@ -#ifndef DOWNLOADER_H +#ifndef DOWNLOADER_H #define DOWNLOADER_H #include @@ -11,8 +11,8 @@ class QNetworkReply; class Download { public: - Download(const QUrl &url, const QString &file) - {_url = url; _file = file;} + Download(const QUrl &url, const QString &file) : _url(url), _file(file) {} + const QUrl& url() const {return _url;} const QString& file() const {return _file;} @@ -21,6 +21,17 @@ private: QString _file; }; +class Authorization +{ +public: + Authorization() {} + Authorization(const QString &username, const QString &password); + + const QByteArray &header() const {return _header;} + +private: + QByteArray _header; +}; class Downloader : public QObject { @@ -29,7 +40,8 @@ class Downloader : public QObject public: Downloader(QObject *parent = 0); - bool get(const QList &list); + bool get(const QList &list, const Authorization &authorization + = Authorization()); void clearErrors() {_errorDownloads.clear();} signals: @@ -42,7 +54,8 @@ private: class Redirect; class ReplyTimeout; - bool doDownload(const Download &dl, const Redirect *redirect = 0); + bool doDownload(const Download &dl, const QByteArray &authorization, + const Redirect *redirect = 0); bool saveToDisk(const QString &filename, QIODevice *data); QNetworkAccessManager _manager; diff --git a/src/map/mapsource.cpp b/src/map/mapsource.cpp index 5bbd2d85..98910b0f 100644 --- a/src/map/mapsource.cpp +++ b/src/map/mapsource.cpp @@ -144,6 +144,12 @@ void MapSource::map(QXmlStreamReader &reader, Config &config) config.yx = (reader.attributes().value("axis") == "yx") ? true : false; config.crs = reader.readElementText(); + } else if (reader.name() == "authorization") { + QXmlStreamAttributes attr = reader.attributes(); + config.authorization = Authorization( + attr.value("username").toString(), + attr.value("password").toString()); + reader.skipCurrentElement(); } else reader.skipCurrentElement(); } @@ -213,10 +219,11 @@ Map *MapSource::loadFile(const QString &path) if (config.type == WMTS) m = new WMTSMap(config.name, WMTS::Setup(config.url, config.layer, config.set, config.style, config.format, config.rest, config.yx, - config.dimensions)); + config.dimensions, config.authorization)); else if (config.type == WMS) m = new WMSMap(config.name, WMS::Setup(config.url, config.layer, - config.style, config.format, config.crs, config.yx)); + config.style, config.format, config.crs, config.yx, + config.authorization)); else m = new OnlineMap(config.name, config.url, config.zooms, config.bounds); diff --git a/src/map/mapsource.h b/src/map/mapsource.h index 67f5140c..bda626e7 100644 --- a/src/map/mapsource.h +++ b/src/map/mapsource.h @@ -4,6 +4,7 @@ #include #include "common/range.h" #include "common/rectc.h" +#include "downloader.h" class Map; class QXmlStreamReader; @@ -35,6 +36,7 @@ private: bool rest; bool yx; QList > dimensions; + Authorization authorization; Config(); }; diff --git a/src/map/tileloader.cpp b/src/map/tileloader.cpp index 0f7ef1fc..a768b2d9 100644 --- a/src/map/tileloader.cpp +++ b/src/map/tileloader.cpp @@ -33,7 +33,7 @@ void TileLoader::loadTilesAsync(QList &list) } if (!dl.empty()) - _downloader->get(dl); + _downloader->get(dl, _authorization); } void TileLoader::loadTilesSync(QList &list) @@ -56,7 +56,7 @@ void TileLoader::loadTilesSync(QList &list) QEventLoop wait; QObject::connect(_downloader, SIGNAL(finished()), &wait, SLOT(quit())); - if (_downloader->get(dl)) + if (_downloader->get(dl, _authorization)) wait.exec(); for (int i = 0; i < list.size(); i++) { diff --git a/src/map/tileloader.h b/src/map/tileloader.h index 7df8460c..30571348 100644 --- a/src/map/tileloader.h +++ b/src/map/tileloader.h @@ -3,15 +3,15 @@ #include #include "tile.h" - -class Downloader; +#include "downloader.h" class TileLoader { public: TileLoader() {} - TileLoader(const QString &url, const QString &dir) - : _url(url), _dir(dir) {} + TileLoader(const QString &url, const QString &dir, + const Authorization &authorization = Authorization()) + : _url(url), _dir(dir), _authorization(authorization) {} void loadTilesAsync(QList &list); void loadTilesSync(QList &list); @@ -27,6 +27,7 @@ private: QString _url; QString _dir; + Authorization _authorization; static Downloader *_downloader; }; diff --git a/src/map/wms.cpp b/src/map/wms.cpp index 75246889..b5a9003b 100644 --- a/src/map/wms.cpp +++ b/src/map/wms.cpp @@ -43,20 +43,40 @@ QString WMS::style(QXmlStreamReader &reader) return name; } +RectC WMS::geographicBoundingBox(QXmlStreamReader &reader) +{ + qreal left, top, right, bottom; + + while (reader.readNextStartElement()) { + if (reader.name() == "westBoundLongitude") + left = reader.readElementText().toDouble(); + else if (reader.name() == "eastBoundLongitude") + right = reader.readElementText().toDouble(); + else if (reader.name() == "northBoundLatitude") + top = reader.readElementText().toDouble(); + else if (reader.name() == "southBoundLatitude") + bottom = reader.readElementText().toDouble(); + else + reader.skipCurrentElement(); + } + + return RectC(Coordinates(left, top), Coordinates(right, bottom)); +} + void WMS::layer(QXmlStreamReader &reader, CTX &ctx, - const QList &pCRSs, const QList &pStyles) + const QList &pCRSs, const QList &pStyles, + RangeF &pScaleDenominator, RectC &pBoundingBox) { QString name; QList CRSs(pCRSs); QList styles(pStyles); - RangeF scaleDenominator(2132.729583849784, 559082264.0287178); - QRectF boundingBox; - + RangeF scaleDenominator(pScaleDenominator); + RectC boundingBox(pBoundingBox); while (reader.readNextStartElement()) { if (reader.name() == "Name") name = reader.readElementText(); - else if (reader.name() == "CRS") + else if (reader.name() == "CRS" || reader.name() == "SRS") CRSs.append(reader.readElementText()); else if (reader.name() == "Style") styles.append(style(reader)); @@ -64,18 +84,18 @@ void WMS::layer(QXmlStreamReader &reader, CTX &ctx, scaleDenominator.setMin(reader.readElementText().toDouble()); else if (reader.name() == "MaxScaleDenominator") scaleDenominator.setMax(reader.readElementText().toDouble()); - else if (reader.name() == "BoundingBox") { + else if (reader.name() == "LatLonBoundingBox") { QXmlStreamAttributes attr = reader.attributes(); - if (attr.value("CRS") == ctx.setup.crs()) { - boundingBox = QRectF(QPointF( - attr.value("minx").toString().toDouble(), - attr.value("maxy").toString().toDouble()), - QPointF(attr.value("maxx").toString().toDouble(), - attr.value("miny").toString().toDouble())); - } + boundingBox = RectC(Coordinates( + attr.value("minx").toString().toDouble(), + attr.value("maxy").toString().toDouble()), + Coordinates(attr.value("maxx").toString().toDouble(), + attr.value("miny").toString().toDouble())); reader.skipCurrentElement(); - } else if (reader.name() == "Layer") - layer(reader, ctx, CRSs, styles); + } else if (reader.name() == "EX_GeographicBoundingBox") + boundingBox = geographicBoundingBox(reader); + else if (reader.name() == "Layer") + layer(reader, ctx, CRSs, styles, scaleDenominator, boundingBox); else reader.skipCurrentElement(); } @@ -93,10 +113,12 @@ void WMS::capability(QXmlStreamReader &reader, CTX &ctx) { QList CRSs; QList styles; + RangeF scaleDenominator(2132.729583849784, 559082264.0287178); + RectC boundingBox; while (reader.readNextStartElement()) { if (reader.name() == "Layer") - layer(reader, ctx, CRSs, styles); + layer(reader, ctx, CRSs, styles, scaleDenominator, boundingBox); else if (reader.name() == "Request") request(reader, ctx); else @@ -129,10 +151,11 @@ bool WMS::parseCapabilities(const QString &path, const Setup &setup) reader.setDevice(&file); if (reader.readNextStartElement()) { - if (reader.name() == "WMS_Capabilities") + if (reader.name() == "WMS_Capabilities" + || reader.name() == "WMT_MS_Capabilities") capabilities(reader, ctx); else - reader.raiseError("Not a WMS_Capabilities XML file"); + reader.raiseError("Not a WMS Capabilities XML file"); } if (reader.error()) { _errorString = QString("%1:%2: %3").arg(path).arg(reader.lineNumber()) @@ -176,7 +199,8 @@ bool WMS::parseCapabilities(const QString &path, const Setup &setup) return true; } -bool WMS::getCapabilities(const QString &url, const QString &file) +bool WMS::getCapabilities(const QString &url, const QString &file, + const Authorization &authorization) { QList dl; @@ -184,7 +208,7 @@ bool WMS::getCapabilities(const QString &url, const QString &file) QEventLoop wait; QObject::connect(_downloader, SIGNAL(finished()), &wait, SLOT(quit())); - if (_downloader->get(dl)) + if (_downloader->get(dl, authorization)) wait.exec(); if (QFileInfo(file).exists()) @@ -201,7 +225,7 @@ WMS::WMS(const QString &file, const WMS::Setup &setup) : _valid(false) .arg(setup.url()); if (!QFileInfo(file).exists()) - if (!getCapabilities(capaUrl, file)) + if (!getCapabilities(capaUrl, file, setup.authorization())) return; if (!parseCapabilities(file, setup)) return; diff --git a/src/map/wms.h b/src/map/wms.h index 27cfc508..00f1f82a 100644 --- a/src/map/wms.h +++ b/src/map/wms.h @@ -4,10 +4,11 @@ #include #include #include "common/range.h" +#include "common/rectc.h" #include "projection.h" +#include "downloader.h" class QXmlStreamReader; -class Downloader; class WMS { @@ -16,11 +17,13 @@ public: { public: Setup(const QString &url, const QString &layer, const QString &style, - const QString &format, const QString &crs, bool yx) + const QString &format, const QString &crs, bool yx, + const Authorization &authorization = Authorization()) : _url(url), _layer(layer), _style(style), _format(format), _crs(crs), - _yx(yx) {} + _yx(yx), _authorization(authorization) {} const QString &url() const {return _url;} + const Authorization &authorization() const {return _authorization;} const QString &layer() const {return _layer;} const QString &style() const {return _style;} const QString &format() const {return _format;} @@ -34,6 +37,7 @@ public: QString _format; QString _crs; bool _yx; + Authorization _authorization; }; @@ -41,7 +45,7 @@ public: const Projection &projection() const {return _projection;} const RangeF &scaleDenominator() const {return _scaleDenominator;} - const QRectF &boundingBox() const {return _boundingBox;} + const RectC &boundingBox() const {return _boundingBox;} const QString &version() const {return _version;} bool isValid() const {return _valid;} @@ -54,7 +58,7 @@ private: struct CTX { const Setup &setup; RangeF scaleDenominator; - QRectF boundingBox; + RectC boundingBox; bool layer; bool style; bool format; @@ -64,19 +68,22 @@ private: format(false), crs(false) {} }; + RectC geographicBoundingBox(QXmlStreamReader &reader); QString style(QXmlStreamReader &reader); void getMap(QXmlStreamReader &reader, CTX &ctx); void request(QXmlStreamReader &reader, CTX &ctx); void layer(QXmlStreamReader &reader, CTX &ctx, const QList &pCRSs, - const QList &pStyles); + const QList &pStyles, RangeF &pScaleDenominator, + RectC &pBoundingBox); void capability(QXmlStreamReader &reader, CTX &ctx); void capabilities(QXmlStreamReader &reader, CTX &ctx); bool parseCapabilities(const QString &path, const Setup &setup); - bool getCapabilities(const QString &url, const QString &file); + bool getCapabilities(const QString &url, const QString &file, + const Authorization &authorization); Projection _projection; RangeF _scaleDenominator; - QRectF _boundingBox; + RectC _boundingBox; QString _version; bool _valid; diff --git a/src/map/wmsmap.cpp b/src/map/wmsmap.cpp index 6064ef4b..90aead13 100644 --- a/src/map/wmsmap.cpp +++ b/src/map/wmsmap.cpp @@ -80,10 +80,10 @@ bool WMSMap::loadWMS() _yx = (_setup.yx() && wms.version() >= "1.3.0") ? true : false; _projection = wms.projection(); - _boundingBox = _yx ? QRectF(QPointF(wms.boundingBox().bottom(), - wms.boundingBox().right()), QPointF(wms.boundingBox().top(), - wms.boundingBox().left())) : wms.boundingBox(); - _tileLoader = TileLoader(tileUrl(wms.version()), tilesDir()); + _boundingBox = QRectF(_projection.ll2xy(wms.boundingBox().topLeft()), + _projection.ll2xy(wms.boundingBox().bottomRight())); + _tileLoader = TileLoader(tileUrl(wms.version()), tilesDir(), + _setup.authorization()); computeZooms(wms.scaleDenominator()); updateTransform(); diff --git a/src/map/wmts.cpp b/src/map/wmts.cpp index e3557bb6..fd61c4db 100644 --- a/src/map/wmts.cpp +++ b/src/map/wmts.cpp @@ -282,7 +282,8 @@ bool WMTS::parseCapabilities(const QString &path, const Setup &setup) return true; } -bool WMTS::getCapabilities(const QString &url, const QString &file) +bool WMTS::getCapabilities(const QString &url, const QString &file, + const Authorization &authorization) { QList dl; @@ -290,7 +291,7 @@ bool WMTS::getCapabilities(const QString &url, const QString &file) QEventLoop wait; QObject::connect(_downloader, SIGNAL(finished()), &wait, SLOT(quit())); - if (_downloader->get(dl)) + if (_downloader->get(dl, authorization)) wait.exec(); if (QFileInfo(file).exists()) @@ -308,7 +309,7 @@ WMTS::WMTS(const QString &file, const WMTS::Setup &setup) : _valid(false) .arg(setup.url()); if (!QFileInfo(file).exists()) - if (!getCapabilities(capaUrl, file)) + if (!getCapabilities(capaUrl, file, setup.authorization())) return; if (!parseCapabilities(file, setup)) return; diff --git a/src/map/wmts.h b/src/map/wmts.h index b538b4fa..b0cc986c 100644 --- a/src/map/wmts.h +++ b/src/map/wmts.h @@ -8,8 +8,8 @@ #include #include "common/rectc.h" #include "projection.h" +#include "downloader.h" -class Downloader; class QXmlStreamReader; class WMTS @@ -20,11 +20,14 @@ public: public: Setup(const QString &url, const QString &layer, const QString &set, const QString &style, const QString &format, bool rest, bool yx, - const QList > &dimensions) : + const QList > &dimensions, + const Authorization &authorization = Authorization()) : _url(url), _layer(layer), _set(set), _style(style), _format(format), - _rest(rest), _yx(yx), _dimensions(dimensions) {} + _rest(rest), _yx(yx), _dimensions(dimensions), + _authorization(authorization) {} const QString &url() const {return _url;} + const Authorization &authorization() const {return _authorization;} const QString &layer() const {return _layer;} const QString &set() const {return _set;} const QString &style() const {return _style;} @@ -43,6 +46,7 @@ public: bool _rest; bool _yx; QList > _dimensions; + Authorization _authorization; }; class Zoom @@ -136,7 +140,8 @@ private: void contents(QXmlStreamReader &reader, CTX &ctx); void capabilities(QXmlStreamReader &reader, CTX &ctx); bool parseCapabilities(const QString &path, const Setup &setup); - bool getCapabilities(const QString &url, const QString &file); + bool getCapabilities(const QString &url, const QString &file, + const Authorization &authorization); QSet _matrixes; QSet _limits; diff --git a/src/map/wmtsmap.cpp b/src/map/wmtsmap.cpp index a527e4d0..f83ebadf 100644 --- a/src/map/wmtsmap.cpp +++ b/src/map/wmtsmap.cpp @@ -23,7 +23,8 @@ bool WMTSMap::loadWMTS() _bounds = wmts.bounds(); _zooms = wmts.zooms(); _projection = wmts.projection(); - _tileLoader = TileLoader(wmts.tileUrl(), tilesDir()); + _tileLoader = TileLoader(wmts.tileUrl(), tilesDir(), + _setup.authorization()); updateTransform();