Marble

OwsServiceManager.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2023 Torsten Rahn <rahn@kde.org>
4//
5
6#include "OwsServiceManager.h"
7
8#include <QAuthenticator>
9#include <QNetworkReply>
10#include <QUrl>
11#include <QUrlQuery>
12
13#include <QBuffer>
14#include <QImageReader>
15
16#include <MarbleDebug.h>
17#include <cmath>
18
19namespace Marble
20{
21
22OwsMappingCapabilities::OwsMappingCapabilities() = default;
23
24WmsCapabilities::WmsCapabilities() = default;
25
26WmtsCapabilities::WmtsCapabilities() = default;
27
28void WmtsCapabilities::setWmtsTileMatrixSets(const QMap<QString, QStringList> &wmtsTileMatrixSets)
29{
30 m_wmtsTileMatrixSets = wmtsTileMatrixSets;
31}
32
33QMap<QString, QStringList> WmtsCapabilities::wmtsTileMatrixSets() const
34{
35 return m_wmtsTileMatrixSets;
36}
37
38void WmtsCapabilities::setWmtsTileResource(const QMap<QString, QMap<QString, QString>> &wmtsTileRessource)
39{
40 m_wmtsTileResource = wmtsTileRessource;
41}
42
43QMap<QString, QMap<QString, QString>> WmtsCapabilities::wmtsTileResource() const
44{
45 return m_wmtsTileResource;
46}
47
48ImageRequestResult::ImageRequestResult()
49 : m_imageStatus(WmsImageNone)
50{
51}
52
53void ImageRequestResult::setImageStatus(WmsImageStatus imageStatus)
54{
55 m_imageStatus = imageStatus;
56}
57
58WmsImageStatus ImageRequestResult::imageStatus() const
59{
60 return m_imageStatus;
61}
62
63void ImageRequestResult::setResultImage(const QImage &image)
64{
65 m_resultImage = image;
66}
67
68QImage ImageRequestResult::resultImage() const
69{
70 return m_resultImage;
71}
72
73void ImageRequestResult::setResultRaw(const QByteArray &resultRaw)
74{
75 m_resultRaw = resultRaw;
76}
77
78QByteArray ImageRequestResult::resultRaw() const
79{
80 return m_resultRaw;
81}
82
83void ImageRequestResult::setResultFormat(const QString &resultFormat)
84{
85 m_resultFormat = resultFormat;
86}
87
88QString ImageRequestResult::resultFormat() const
89{
90 return m_resultFormat;
91}
92
93void ImageRequestResult::setResultType(const ImageResultType resultType)
94{
95 m_resultType = resultType;
96}
97
98ImageResultType ImageRequestResult::resultType() const
99{
100 return m_resultType;
101}
102
103void OwsMappingCapabilities::setVersion(const QString &version)
104{
105 m_version = version;
106}
107
108QString OwsMappingCapabilities::version() const
109{
110 return m_version;
111}
112
113void OwsMappingCapabilities::setTitle(const QString &title)
114{
115 m_title = title;
116}
117
118QString OwsMappingCapabilities::title() const
119{
120 return m_title;
121}
122
123void OwsMappingCapabilities::setAbstract(const QString &abstract)
124{
125 m_abstract = abstract;
126}
127
128QString OwsMappingCapabilities::abstract() const
129{
130 return m_abstract;
131}
132
133void WmsCapabilities::setContactInformation(const QString &info)
134{
135 m_contactInformation = info;
136}
137
138QString WmsCapabilities::contactInformation() const
139{
140 return m_contactInformation;
141}
142
143void WmsCapabilities::setFees(const QString &fees)
144{
145 m_fees = fees;
146}
147
148QString WmsCapabilities::fees() const
149{
150 return m_fees;
151}
152
153QStringList OwsMappingCapabilities::layers() const
154{
155 return m_owsLayerMetaInfo.keys();
156}
157
158QStringList WmsCapabilities::projections(const QString &layer)
159{
160 QStringList result = m_wmsLayerCoordinateSystems.value(layer).keys();
161 if (!m_wmsLayerCoordinateSystems.isEmpty()) {
162 result << m_wmsLayerCoordinateSystems.value(m_wmsLayerCoordinateSystems.firstKey()).keys();
163 }
164 result.removeDuplicates();
165
166 return result;
167}
168
169QString WmsCapabilities::boundingBox(const QString &layer, const QString &projection)
170{
171 QString result;
172 result = m_wmsLayerCoordinateSystems.value(layer).value(projection);
173 if (result.isEmpty()) {
174 result = m_wmsLayerCoordinateSystems.value(nullptr).value(projection);
175 }
176 return result;
177}
178
179QString OwsMappingCapabilities::title(const QString &layer)
180{
181 return m_owsLayerMetaInfo.value(layer).at(0);
182}
183
184QString OwsMappingCapabilities::abstract(const QString &layer)
185{
186 return m_owsLayerMetaInfo.value(layer).at(1);
187}
188
189QString WmsCapabilities::legendUrl(const QString &layer)
190{
191 return m_owsLayerMetaInfo.value(layer).at(2);
192}
193
194QString OwsMappingCapabilities::style(const QString &layer)
195{
196 return m_owsLayerMetaInfo.value(layer).at(3);
197}
198
199QStringList WmsCapabilities::styles(const QStringList &layers)
200{
201 QStringList retVal;
202 for (auto layer : layers) {
203 retVal << style(layer);
204 }
205 return retVal;
206}
207
208QString WmsCapabilities::boundingBoxNSEWDegrees(const QStringList &layers, const QString &projection)
209{
210 QString retVal;
211 for (auto layer : layers) {
212 QString layerBBox = boundingBox(layer, projection);
213 if (layerBBox.isEmpty()) {
214 retVal = QStringLiteral("90,-90,180,-180");
215 break;
216 }
217 QStringList layerBBoxList = layerBBox.split(",");
218 qreal west, south, east, north;
219 if (projection == QStringLiteral("epsg:3857")) {
220 west = layerBBoxList.at(0).toDouble() * 180 / 20037508.34;
221 south = atan(pow(2.7182818284, (layerBBoxList.at(1).toDouble() / 20037508.34 * M_PI))) * (360 / M_PI) - 90;
222 east = layerBBoxList.at(2).toDouble() * 180 / 20037508.34;
223 north = atan(pow(2.7182818284, (layerBBoxList.at(3).toDouble() / 20037508.34 * M_PI))) * (360 / M_PI) - 90;
224 } else {
225 if (projection == "crs:84" || (projection == "4326" && version() != "1.3.0")) {
226 // order: longitude-latitude
227 west = layerBBoxList.at(0).toDouble();
228 south = layerBBoxList.at(1).toDouble();
229 east = layerBBoxList.at(2).toDouble();
230 north = layerBBoxList.at(3).toDouble();
231 } else {
232 // order: latitude-longitude
233 west = layerBBoxList.at(1).toDouble();
234 south = layerBBoxList.at(0).toDouble();
235 east = layerBBoxList.at(3).toDouble();
236 north = layerBBoxList.at(2).toDouble();
237 }
238 }
239 retVal = QStringLiteral("%1,%2,%3,%4").arg(north).arg(south).arg(east).arg(west);
240 // TODO: merge possibly different layer bboxes
241 break;
242 }
243 return retVal;
244}
245
246void WmsCapabilities::setReferenceSystemType(const QString &refSystem)
247{
248 m_referenceSystemType = refSystem;
249}
250
251QString WmsCapabilities::referenceSystemType() const
252{
253 return m_referenceSystemType;
254}
255
256void OwsMappingCapabilities::setOwsLayerMetaInfo(const QMap<QString, QStringList> &wmsLayerMetaInfo)
257{
258 m_owsLayerMetaInfo = wmsLayerMetaInfo;
259}
260
261QMap<QString, QStringList> OwsMappingCapabilities::owsLayerMetaInfo() const
262{
263 return m_owsLayerMetaInfo;
264}
265
266void WmsCapabilities::setWmsLayerCoordinateSystems(const QMap<QString, QMap<QString, QString>> &wmsLayerCoordinateSystems)
267{
268 m_wmsLayerCoordinateSystems = wmsLayerCoordinateSystems;
269}
270
271QMap<QString, QMap<QString, QString>> WmsCapabilities::wmsLayerCoordinateSystems() const
272{
273 return m_wmsLayerCoordinateSystems;
274}
275
276void WmsCapabilities::setFormats(const QStringList &formats)
277{
278 m_formats = formats;
279}
280
281QStringList WmsCapabilities::formats()
282{
283 return m_formats;
284}
285
286OwsServiceManager::OwsServiceManager(QObject *parent)
287 : QObject(parent)
288 , m_capabilitiesStatus(OwsCapabilitiesNone)
289{
290 connect(&m_capabilitiesAccessManager, &QNetworkAccessManager::finished, this, &OwsServiceManager::parseOwsCapabilities);
291 connect(&m_imageAccessManager, &QNetworkAccessManager::finished, this, &OwsServiceManager::parseImageResult);
292
293 connect(&m_capabilitiesAccessManager, &QNetworkAccessManager::authenticationRequired, this, &OwsServiceManager::handleAuthentication);
294 connect(&m_imageAccessManager, &QNetworkAccessManager::authenticationRequired, this, &OwsServiceManager::handleAuthentication);
295}
296
297void OwsServiceManager::queryOwsCapabilities(const QUrl &queryUrl, const QString &serviceString)
298{
299 m_url = queryUrl;
300 QUrl url(queryUrl);
301 QUrlQuery urlQuery;
302 urlQuery.addQueryItem(QStringLiteral("service"), serviceString);
303 urlQuery.addQueryItem(QStringLiteral("request"), QStringLiteral("GetCapabilities"));
304 url.setQuery(urlQuery);
305
306 QNetworkRequest request;
307 request.setUrl(url);
309
310 mDebug() << "for url" << url;
311 m_capabilitiesAccessManager.get(request);
312}
313
314void OwsServiceManager::queryWmsMap(const QUrl &url,
315 const QString &layers,
316 const QString &projection,
317 const QString &bbox,
318 const QString &format,
319 const QString &style)
320{
321 m_imageRequestResult.setResultImage(QImage());
322 m_imageRequestResult.setImageStatus(WmsImageNone);
323 m_imageRequestResult.setResultRaw("");
324 m_imageRequestResult.setResultFormat(QString());
325
326 QUrlQuery downloadQuery;
327 downloadQuery.addQueryItem("request", "GetMap"); // Requests that the server generates a map.
328 downloadQuery.addQueryItem("service", "wms"); // Service name. Value is WMS.
329
330 QString versionkey = wmsCapabilities().version() == "1.0.0" ? "wmtver" : "version";
331 downloadQuery.addQueryItem(versionkey, wmsCapabilities().version()); // Service version. Value is one of 1.0.0, 1.1.0, 1.1.1, 1.3.0.
332 downloadQuery.addQueryItem("layers", layers); // Layers to display on map. Value is a comma-separated list of layer names.
333
334 // Spatial Reference System for map output. Value is in the form EPSG:nnn. srs was used before WMS 1.3.0, crs has been used since then.
335 downloadQuery.addQueryItem(wmsCapabilities().referenceSystemType(), projection);
336 // downloadQuery.addQueryItem( "bgcolor", "#ff0000" ); // rarely supported by servers
337
338 downloadQuery.addQueryItem("width", "256"); // Width of map output, in pixels.
339 downloadQuery.addQueryItem("height", "256"); // Height of map output, in pixels.
340
341 QString boundingBox = bbox;
342 if (boundingBox.isEmpty()) {
343 if (projection == "epsg:3857") {
344 boundingBox = "-20037508.34,-20048966.1,20037508.34,20048966.1";
345 } else if (projection == "epsg:4326") {
346 boundingBox =
347 wmsCapabilities().version() == "1.3.0" ? "-90,-180,90,180" : "-180,-90,180,90"; // flipped axes for 1.3.0 in epsg:4326 according to spec
348 } else if (projection == "crs:84") {
349 boundingBox = "-180,-90,180,90"; // order: WGS84 longitude-latitude
350 }
351 }
352 downloadQuery.addQueryItem("bbox", boundingBox);
353 downloadQuery.addQueryItem("transparent", "true");
354
355 // Format for the map output. In addition to common bitmap formats WMS servers
356 // sometimes support "vector" formats (PDF, SVG, KML, etc.)
357 // Currently Marble only supports JPEG, PNG, TIFF, GIF, BMP and their variants.
358 downloadQuery.addQueryItem("format", QStringLiteral("image/%1").arg(format));
359
360 // Styles in which layers are to be rendered. Value is a comma-separated list of style names,
361 // or empty if default styling is required. Style names may be empty in the list,
362 // to use default layer styling. However some servers do not accept empty style names.
363 downloadQuery.addQueryItem("styles", style);
364 m_imageRequestResult.setResultFormat((format == QLatin1StringView("jpeg")) ? "jpg" : format); // Is this needed here?
365
366 QUrl finalDownloadUrl(url);
367 finalDownloadUrl.setQuery(downloadQuery);
368 mDebug() << "requesting WMS image" << finalDownloadUrl;
369
370 QNetworkRequest request(finalDownloadUrl);
371
372 m_imageAccessManager.get(request);
373}
374
375void OwsServiceManager::queryWmsLevelZeroTile(const QUrl &url, const QString &layers, const QString &projection, const QString &format, const QString &style)
376{
377 QString bbox;
378 if (projection == QStringLiteral("epsg:3857")) {
379 bbox = QStringLiteral("-20037508.34,-20048966.1,20037508.34,20048966.1");
380
381 } else if (projection == QStringLiteral("epsg:4326")) {
382 bbox = wmsCapabilities().version() == QStringLiteral("1.3.0")
383 ? QStringLiteral("-90,-180,90,180")
384 : QStringLiteral("-180,-90,180,90"); // flipped axes for 1.3.0 in epsg:4326 according to spec
385 } else if (projection == QStringLiteral("crs:84")) {
386 bbox = QStringLiteral("-180,-90,180,90"); // order: WGS84 longitude-latitude
387 }
388
389 m_imageRequestResult.setResultType(LevelZeroTile);
390
391 queryWmsMap(url, layers, projection, bbox, format, style);
392}
393
394void OwsServiceManager::queryWmsPreviewImage(const QUrl &url, const QString &layers, const QString &projection, const QString &format, const QString &style)
395{
396 QString firstLayer = layers.contains(',') ? layers.section(',', 0, 0) : layers;
397 QString bbox = wmsCapabilities().boundingBox(firstLayer, projection);
398
399 m_imageRequestResult.setResultType(PreviewImage);
400
401 queryWmsMap(url, layers, projection, bbox, format, style);
402}
403
404void OwsServiceManager::queryWmsLegendImage(const QUrl &url)
405{
406 m_imageRequestResult.setResultImage(QImage());
407 m_imageRequestResult.setImageStatus(WmsImageNone);
408 m_imageRequestResult.setResultRaw("");
409 m_imageRequestResult.setResultFormat(QString());
410 m_imageRequestResult.setResultType(LegendImage);
411
412 mDebug() << "requesting legend" << url;
413
414 QNetworkRequest request(url);
415
416 m_imageAccessManager.get(request);
417}
418
419void OwsServiceManager::queryWmtsLevelZeroTile(const QString &url, const QString &style, const QString &tileMatrixSet)
420{
421 m_imageRequestResult.setResultType(LevelZeroTile);
422 queryWmtsTile(url, style, tileMatrixSet, "0", "0", QStringLiteral("0"));
423}
424
425void OwsServiceManager::queryWmtsPreviewImage(const QString &url, const QString &style, const QString &tileMatrixSet)
426{
427 m_imageRequestResult.setResultType(PreviewImage);
428 queryWmtsTile(url, style, tileMatrixSet, "0", "0", QStringLiteral("0"));
429}
430
431void OwsServiceManager::queryWmtsTile(const QString &url,
432 const QString &style,
433 const QString &tileMatrixSet,
434 const QString &tileMatrix,
435 const QString &tileRow,
436 const QString &tileCol)
437{
438 m_imageRequestResult.setResultImage(QImage());
439 m_imageRequestResult.setImageStatus(WmsImageNone);
440 m_imageRequestResult.setResultRaw("");
441 m_imageRequestResult.setResultFormat(QString());
442
443 QUrl downloadUrl;
444 QString baseUrl = url;
445 baseUrl.replace(baseUrl.indexOf(QLatin1StringView("{Time}")), 6, QStringLiteral("current"));
446 baseUrl.replace(baseUrl.indexOf(QLatin1StringView("{style}")), 7, style);
447 baseUrl.replace(baseUrl.indexOf(QLatin1StringView("{Style}")), 7, style);
448 baseUrl.replace(baseUrl.indexOf(QLatin1StringView("{TileMatrixSet}")), 15, tileMatrixSet);
449 baseUrl.replace(baseUrl.indexOf(QLatin1StringView("{TileMatrix}")), 12, tileMatrix);
450 baseUrl.replace(baseUrl.indexOf(QLatin1StringView("{TileRow}")), 9, tileRow);
451 baseUrl.replace(baseUrl.indexOf(QLatin1StringView("{TileCol}")), 9, tileCol);
452 downloadUrl.setUrl(baseUrl);
453
454 QNetworkRequest request(downloadUrl);
455 mDebug() << "requesting static map" << downloadUrl;
456 m_imageAccessManager.get(request);
457}
458
459void OwsServiceManager::queryXYZPreviewImage(const QString &urlString)
460{
461 m_imageRequestResult.setResultType(PreviewImage);
462 queryXYZImage(urlString);
463}
464
465void OwsServiceManager::queryXYZLevelZeroTile(const QString &urlString)
466{
467 m_imageRequestResult.setResultType(LevelZeroTile);
468 queryXYZImage(urlString);
469}
470
471void OwsServiceManager::queryXYZImage(const QString urlString)
472{
473 QUrl downloadUrl;
474 QString baseUrl = urlString;
475 baseUrl.replace(baseUrl.indexOf(QLatin1StringView("{x}")), 3, QString::number(0));
476 baseUrl.replace(baseUrl.indexOf(QLatin1StringView("{y}")), 3, QString::number(0));
477 baseUrl.replace(baseUrl.indexOf(QLatin1StringView("{zoomLevel}")), 11, QString::number(0));
478 baseUrl.replace(baseUrl.indexOf(QLatin1StringView("{z}")), 3, QString::number(0));
479 downloadUrl.setUrl(baseUrl);
480
481 QNetworkRequest request(downloadUrl);
482 mDebug() << "requesting static map" << downloadUrl;
483 m_imageAccessManager.get(request);
484}
485
486void OwsServiceManager::handleAuthentication(QNetworkReply *reply, QAuthenticator *authenticator)
487{
488 if (reply->url().host() == QStringLiteral("api.tileserver.org")) {
489 authenticator->setUser({});
490 authenticator->setPassword({});
491 }
492}
493
494void OwsServiceManager::setCapabilitiesStatus(OwsCapabilitiesStatus capabilitiesStatus)
495{
496 m_capabilitiesStatus = capabilitiesStatus;
497}
498
499OwsCapabilitiesStatus OwsServiceManager::capabilitiesStatus() const
500{
501 return m_capabilitiesStatus;
502}
503
504OwsServiceType OwsServiceManager::owsServiceType()
505{
506 return m_owsServiceType;
507}
508
509WmsCapabilities Marble::OwsServiceManager::wmsCapabilities()
510{
511 return m_wmsCapabilities;
512}
513
514WmtsCapabilities OwsServiceManager::wmtsCapabilities()
515{
516 return m_wmtsCapabilities;
517}
518
519ImageRequestResult OwsServiceManager::imageRequestResult()
520{
521 return m_imageRequestResult;
522}
523
524QImage OwsServiceManager::resultImage()
525{
526 return m_imageRequestResult.resultImage();
527}
528
529ImageResultType OwsServiceManager::resultType()
530{
531 return m_imageRequestResult.resultType();
532}
533
534QByteArray OwsServiceManager::resultRaw()
535{
536 return m_imageRequestResult.resultRaw();
537}
538
539QString OwsServiceManager::resultFormat()
540{
541 return m_imageRequestResult.resultFormat();
542}
543
544void OwsServiceManager::parseOwsCapabilities(QNetworkReply *reply)
545{
546 mDebug() << "received reply from" << reply->url();
547 QString result(reply->readAll());
548
549 m_wmsCapabilities = WmsCapabilities(); // clear()
550
551 if (!m_xml.setContent(result)) {
552 setCapabilitiesStatus(OwsCapabilitiesReplyUnreadable); // Wizard cannot parse server's response
553 Q_EMIT wmsCapabilitiesReady();
554 return;
555 }
556
557 if (m_xml.documentElement().firstChildElement().tagName().isNull() || !m_xml.documentElement().tagName().contains("Capabilities")) {
558 setCapabilitiesStatus(OwsCapabilitiesNoOwsServer); // Server is not a Ows Server.
559 Q_EMIT wmsCapabilitiesReady();
560 return;
561 }
562
563 if (m_xml.documentElement().tagName() == "WMS_Capabilities" || m_xml.documentElement().tagName() == "WMT_MS_Capabilities") { // WMTS server used for WMS?
564 m_owsServiceType = WmsType;
565 parseWmsCapabilities(reply);
566 } else if (m_xml.documentElement().tagName() == "Capabilities") {
567 m_owsServiceType = WmtsType;
568 parseWmtsCapabilities(reply);
569 } else if (m_xml.documentElement().tagName() == ("wfs:WFS_Capabilities")) {
570 m_owsServiceType = WfsType;
571 } else if (m_xml.documentElement().tagName() == ("wcs:Capabilities")) {
572 m_owsServiceType = WcsType;
573 } else {
574 m_owsServiceType = NoOwsType;
575 }
576}
577
578void OwsServiceManager::parseWmsCapabilities(QNetworkReply *reply)
579{
580 Q_UNUSED(reply)
581
582 m_wmsCapabilities.setVersion(m_xml.documentElement().attribute("version"));
583 m_wmsCapabilities.setReferenceSystemType(
584 (m_wmsCapabilities.version() == "1.0.0" || m_wmsCapabilities.version() == "1.1.0" || m_wmsCapabilities.version() == "1.1.1") ? "SRS" : "CRS");
585
586 QDomElement service = m_xml.documentElement().firstChildElement("Service");
587 QDomNodeList layers = m_xml.documentElement().firstChildElement("Capability").elementsByTagName("Layer");
588
589 m_wmsCapabilities.setTitle(service.firstChildElement("Title").text());
590 m_wmsCapabilities.setAbstract(service.firstChildElement("Abstract").text());
591
592 QDomElement contactElement = service.firstChildElement("ContactInformation");
593 QDomElement contactPersonPrimaryElement = contactElement.firstChildElement("ContactPersonPrimary");
594 QString contactPersonPrimary;
595 if (!contactPersonPrimaryElement.isNull()) {
596 QString contactPerson = contactPersonPrimaryElement.firstChildElement("ContactPerson").text();
597 QString contactOrganisation = contactPersonPrimaryElement.firstChildElement("ContactOrganization").text();
598 contactPersonPrimary = contactPerson + "<br>" + contactOrganisation + "<br>";
599 }
600 QString contactPosition = contactElement.firstChildElement("ContactPosition").text();
601 contactPersonPrimary += contactPosition;
602
603 QDomElement addressElement = contactElement.firstChildElement("ContactAddress");
604 QString postalAddress;
605 if (!addressElement.isNull() && addressElement.firstChildElement("AddressType").text() == "postal") {
606 QString address = addressElement.firstChildElement("Address").text();
607 QString city = addressElement.firstChildElement("City").text();
608 QString stateOrProvince = addressElement.firstChildElement("StateOrProvince").text();
609 QString postalCode = addressElement.firstChildElement("PostCode").text();
610 QString country = addressElement.firstChildElement("Country").text();
611 postalAddress = address + "<br>" + city + "<br>" + stateOrProvince + "<br>" + postalCode + "<br>" + country;
612 }
613 QString contactVoicePhone = contactElement.firstChildElement("ContactVoiceTelephone").text();
614 QString contactFacsimileTelephone = contactElement.firstChildElement("ContactFacsimileTelephone").text();
615 QString contactEmail = contactElement.firstChildElement("ContactElectronicMailAddress").text();
616 QString contactMedium = contactVoicePhone + "<br>" + contactFacsimileTelephone + "<br>" + contactEmail;
617
618 QString contactInformation = contactPersonPrimary + "<br><small><font color=\"darkgrey\">" + postalAddress + "<br>" + contactMedium + "</font></small>";
619
620 m_wmsCapabilities.setContactInformation(contactInformation);
621 QString fees = service.firstChildElement("Fees").text();
622 m_wmsCapabilities.setFees(fees);
623
624 QMap<QString, QStringList> wmsLayerMetaInfo;
625
626 for (int i = 0; i < layers.size(); ++i) {
627 QString name = layers.at(i).firstChildElement("Name").text();
628 QString title = layers.at(i).firstChildElement("Title").text();
629 QString abstract = layers.at(i).firstChildElement("Abstract").text();
630 QDomElement legendElement = layers.at(i).firstChildElement("Style").firstChildElement("LegendURL");
631 QString legendUrl;
632 if (!legendElement.isNull())
633 legendUrl = legendElement.firstChildElement("OnlineResource").attribute("xlink:href");
634 QString style = layers.at(i).firstChildElement("Style").firstChildElement("Name").text();
635 if (style.isEmpty())
636 style = "default";
637 /* QDomElement gbboxElement = layers.at(i).firstChildElement("EX_GeographicBoundingBox");
638 QStringList bbox;
639 if (!gbboxElement.isNull()) {
640 bbox << gbboxElement.firstChildElement("westBoundLongitude").text() << gbboxElement.firstChildElement("southBoundLatitude").text()
641 << gbboxElement.firstChildElement("eastBoundLongitude").text() << gbboxElement.firstChildElement("northBoundLatitude").text();
642 }
643 wmsLayerMetaInfo[ name ] << title << abstract << legendUrl << style << bbox.join(","); */
644 wmsLayerMetaInfo[name] << title << abstract << legendUrl << style;
645 }
646
647 m_wmsCapabilities.setOwsLayerMetaInfo(wmsLayerMetaInfo);
648
649 QMap<QString, QMap<QString, QString>> wmsLayerCoordinateSystems;
650 for (int i = 0; i < layers.size(); ++i) {
651 QString layerName = layers.at(i).firstChildElement("Name").text();
652 QDomNodeList projectionList = layers.at(i).toElement().elementsByTagName(m_wmsCapabilities.referenceSystemType());
653 QDomNodeList layerPreviewBBox = layers.at(i).toElement().elementsByTagName("BoundingBox");
654
655 for (int s = 0; s < projectionList.size(); ++s) {
656 QString projection = projectionList.at(s).toElement().text().toLower();
657 // SRS and CRS tags might contain a list of epsgs, so we need to use contains()
658 if (projection.contains("epsg:3857")) {
659 /* if (wmsLayerMetaInfo.value(layerName).at(4) != ",,,") { // EX_GeographicBoundingBox
660 QStringList coords = wmsLayerMetaInfo.value(layerName).at(4).split(",");
661 double west = (coords.at(0).toDouble() * 20037508.34) / 180;
662 double south = 20037508.34 / M_PI * log(tan(((90 + coords.at(1).toDouble()) * M_PI) / 360));
663 double east = (coords.at(2).toDouble() * 20037508.34) / 180;
664 double north = 20037508.34 / M_PI * log(tan(((90 + coords.at(3).toDouble()) * M_PI) / 360));
665 QString bbox = QStringLiteral("%1,%2,%3,%4").arg(QString::number( west, 'f', 6 )).arg(QString::number( south, 'f', 6 ))
666 .arg(QString::number( east, 'f', 6 )).arg(QString::number( north, 'f', 6 ));
667 wmsLayerCoordinateSystems[layerName]["epsg:3857"] = bbox;
668 }
669 else */
670 wmsLayerCoordinateSystems[layerName]["epsg:3857"] = QString();
671 }
672 if (projection.contains("epsg:4326")) {
673 /* if (wmsLayerMetaInfo.value(layerName).at(4) != ",,,") {
674 wmsLayerCoordinateSystems[layerName]["epsg:4326"] = wmsLayerMetaInfo.value(layerName).at(4); // Ignores flip
675 }
676 else */
677 wmsLayerCoordinateSystems[layerName]["epsg:4326"] = QString();
678 }
679 if (projection.contains("crs:84")) {
680 wmsLayerCoordinateSystems[layerName]["crs:84"] = QString();
681 }
682 }
683 for (int b = 0; b < layerPreviewBBox.size(); ++b) {
684 QDomElement bboxElement = layerPreviewBBox.at(b).toElement();
685 QString bboxProjection = bboxElement.attribute(m_wmsCapabilities.referenceSystemType());
686 if (bboxProjection != "epsg:3857" && bboxProjection != "epsg:4326" && bboxProjection != "crs:84")
687 continue;
688 int precision = bboxProjection == "epsg:3857" ? 6 : 12;
689 double west = bboxElement.attribute("minx").toDouble();
690 double south = bboxElement.attribute("miny").toDouble();
691 double east = bboxElement.attribute("maxx").toDouble();
692 double north = bboxElement.attribute("maxy").toDouble();
693 QString bboxString = QStringLiteral("%1,%2,%3,%4")
694 .arg(QString::number(west, 'f', precision),
695 QString::number(south, 'f', precision),
696 QString::number(east, 'f', precision),
697 QString::number(north, 'f', precision));
698 // TODO: convert bbox coordinates from UTM to 3857 (e.g. from epsg:25832/33)
699 wmsLayerCoordinateSystems[layerName][bboxProjection] = bboxString;
700 }
701 // FIXME: parse EX_GeographicBoundingBox if wmsLayerCoordinateSystems[layerName]["epsg:4326"/"epsg:3857"] == QString()
702 }
703
704 m_wmsCapabilities.setWmsLayerCoordinateSystems(wmsLayerCoordinateSystems);
705
706 QDomNodeList formatList =
707 m_xml.documentElement().firstChildElement("Capability").firstChildElement("Request").firstChildElement("GetMap").elementsByTagName("Format");
708
709 QStringList formats;
710 for (int f = 0; f < formatList.size(); ++f) {
711 QString format = formatList.at(f).toElement().text();
712 format = format.right(format.length() - format.indexOf(QLatin1Char('/')) - 1).toLower();
713 if (format == "jpeg" || format.contains("png") || format.contains("tif") || format.contains("gif") || format.contains("bmp")
714 || format.contains("jpg")) {
715 formats << format;
716 }
717 }
718
719 m_wmsCapabilities.setFormats(formats);
720
721 setCapabilitiesStatus(OwsCapabilitiesSuccess);
722 Q_EMIT wmsCapabilitiesReady();
723}
724
725void OwsServiceManager::parseWmtsCapabilities(QNetworkReply *reply)
726{
727 Q_UNUSED(reply)
728
729 m_wmsCapabilities.setVersion(m_xml.documentElement().firstChildElement("ows:ServiceIdentification").firstChildElement("ows:ServiceTypeVersion").text());
730
731 QDomElement service = m_xml.documentElement().firstChildElement("ows:ServiceIdentification");
732 QDomNodeList layers = m_xml.documentElement().firstChildElement("Contents").elementsByTagName("Layer");
733
734 m_wmtsCapabilities.setTitle(service.firstChildElement("ows:Title").text());
735 m_wmtsCapabilities.setAbstract(service.firstChildElement("ows:Abstract").text());
736
737 QMap<QString, QStringList> wmtsLayerMetaInfo;
738 QMap<QString, QStringList> wmtsTileMatrixSets;
739 QMap<QString, QMap<QString, QString>> wmtsTileResource;
740
741 for (int i = 0; i < layers.size(); ++i) {
742 QString name = layers.at(i).firstChildElement("ows:Identifier").text();
743 QString title = layers.at(i).firstChildElement("ows:Title").text();
744 QString abstract = layers.at(i).firstChildElement("ows:Abstract").text();
745 QDomElement legendElement = layers.at(i).firstChildElement("Style").firstChildElement("LegendURL");
746 QString legendUrl;
747 if (!legendElement.isNull())
748 legendUrl = legendElement.attribute("xlink:href");
749 QString style = layers.at(i).firstChildElement("Style").firstChildElement("ows:Identifier").text();
750
751 wmtsLayerMetaInfo[name] << title << abstract << legendUrl << style;
752
753 QDomNodeList resourceList = layers.at(i).toElement().elementsByTagName("ResourceURL");
754 for (int r = 0; r < resourceList.size(); ++r) {
755 if (resourceList.at(r).toElement().attribute("resourceType") == "tile") {
756 QString format = resourceList.at(r).toElement().attribute("format");
757 QString resultFormat;
758 format = format.right(format.length() - format.indexOf(QLatin1Char('/')) - 1).toLower();
759 if (format == "jpeg" || format.contains("png") || format.contains("tif") || format.contains("gif") || format.contains("bmp")
760 || format.contains("jpg")) {
761 resultFormat = format;
762 } else {
763 continue;
764 }
765 QString templ = resourceList.at(r).toElement().attribute(QStringLiteral("template"));
766 wmtsTileResource[name][resultFormat] = templ;
767 }
768 }
769
770 QStringList tileMatrixSets;
771 QDomNodeList tileMatrixLinkList = layers.at(i).toElement().elementsByTagName(QStringLiteral("TileMatrixSetLink"));
772 for (int t = 0; t < tileMatrixLinkList.size(); ++t) {
773 tileMatrixSets << tileMatrixLinkList.at(t).toElement().firstChildElement(QStringLiteral("TileMatrixSet")).text();
774 }
775 wmtsTileMatrixSets[name] = tileMatrixSets;
776 }
777
778 m_wmtsCapabilities.setWmtsTileMatrixSets(wmtsTileMatrixSets);
779 m_wmtsCapabilities.setWmtsTileResource(wmtsTileResource);
780
781 m_wmtsCapabilities.setOwsLayerMetaInfo(wmtsLayerMetaInfo);
782 setCapabilitiesStatus(OwsCapabilitiesSuccess);
783 Q_EMIT wmtsCapabilitiesReady();
784}
785
786void OwsServiceManager::parseImageResult(QNetworkReply *reply)
787{
788 // QString request = reply->request().url().toString();
789
790 // m_imageResult is already reset in the queryWmsMap
791
792 QImage testImage;
793 m_imageRequestResult.setResultRaw(reply->readAll());
794 testImage = QImage::fromData(m_imageRequestResult.resultRaw());
795
796 if (testImage.isNull()) {
797 m_imageRequestResult.setImageStatus(WmsImageFailedServerMessage); // Image could not be downloaded
798 } else if (m_imageRequestResult.resultRaw().isNull()) {
799 m_imageRequestResult.setImageStatus(WmsImageFailed); // Image could not be downloaded
800 } else {
801 m_imageRequestResult.setImageStatus(WmsImageSuccess);
802 }
803
804 m_imageRequestResult.setResultImage(testImage);
805
806 QByteArray resultRaw = m_imageRequestResult.resultRaw();
807 QBuffer testBuffer(&resultRaw);
808 m_imageRequestResult.setResultFormat(QImageReader(&testBuffer).format());
809
810 Q_EMIT imageRequestResultReady();
811}
812
813}
814
815#include "moc_OwsServiceManager.cpp"
KDB_EXPORT KDbVersionInfo version()
PostalAddress address(const QVariant &location)
QString name(StandardAction id)
KI18NLOCALEDATA_EXPORT KCountry country(const char *ianaId)
Binds a QML item to a specific geodetic location in screen coordinates.
void setPassword(const QString &password)
void setUser(const QString &user)
bool isNull() const const
QDomElement documentElement() const const
ParseResult setContent(QAnyStringView text, ParseOptions options)
QString attribute(const QString &name, const QString &defValue) const const
QDomNodeList elementsByTagName(const QString &tagname) const const
QString tagName() const const
QString text() const const
QDomElement firstChildElement(const QString &tagName, const QString &namespaceURI) const const
bool isNull() const const
QDomElement toElement() const const
QDomNode at(int index) const const
int size() const const
QImage fromData(QByteArrayView data, const char *format)
bool isNull() const const
QByteArray readAll()
const_reference at(qsizetype i) const const
T value(qsizetype i) const const
void authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
void finished(QNetworkReply *reply)
QUrl url() const const
void setAttribute(Attribute code, const QVariant &value)
void setUrl(const QUrl &url)
Q_EMITQ_EMIT
QString arg(Args &&... args) const const
const QChar at(qsizetype position) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
bool isNull() const const
qsizetype length() const const
QString number(double n, char format, int precision)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString right(qsizetype n) const const
QString section(QChar sep, qsizetype start, qsizetype end, SectionFlags flags) const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
double toDouble(bool *ok) const const
QString toLower() const const
qsizetype removeDuplicates()
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QString host(ComponentFormattingOptions options) const const
void setUrl(const QString &url, ParsingMode parsingMode)
void addQueryItem(const QString &key, const QString &value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:15:46 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.