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

KDE's Doxygen guidelines are available online.