Marble

OwsServiceManager.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2023 Torsten Rahn <[email protected]>
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 
19 namespace Marble
20 {
21 
22 OwsMappingCapabilities::OwsMappingCapabilities()
23 {
24 
25 }
26 
27 WmsCapabilities::WmsCapabilities()
28 {
29 }
30 
31 WmtsCapabilities::WmtsCapabilities()
32 {
33 
34 }
35 
36 void WmtsCapabilities::setWmtsTileMatrixSets(const QMap<QString, QStringList> &wmtsTileMatrixSets)
37 {
38  m_wmtsTileMatrixSets = wmtsTileMatrixSets;
39 }
40 
41 QMap<QString, QStringList> WmtsCapabilities::wmtsTileMatrixSets() const
42 {
43  return m_wmtsTileMatrixSets;
44 }
45 
46 void WmtsCapabilities::setWmtsTileResource(const QMap<QString, QMap<QString, QString> > &wmtsTileRessource)
47 {
48  m_wmtsTileResource = wmtsTileRessource;
49 }
50 
51 QMap<QString, QMap<QString, QString> > WmtsCapabilities::wmtsTileResource() const
52 {
53  return m_wmtsTileResource;
54 }
55 
56 ImageRequestResult::ImageRequestResult()
57  : m_imageStatus(WmsImageNone)
58 {
59 
60 }
61 
62 void ImageRequestResult::setImageStatus(WmsImageStatus imageStatus)
63 {
64  m_imageStatus = imageStatus;
65 }
66 
67 WmsImageStatus ImageRequestResult::imageStatus() const
68 {
69  return m_imageStatus;
70 }
71 
72 void ImageRequestResult::setResultImage(const QImage &image)
73 {
74  m_resultImage = image;
75 }
76 
77 QImage ImageRequestResult::resultImage() const
78 {
79  return m_resultImage;
80 }
81 
82 void ImageRequestResult::setResultRaw(const QByteArray &resultRaw)
83 {
84  m_resultRaw = resultRaw;
85 }
86 
87 QByteArray ImageRequestResult::resultRaw() const
88 {
89  return m_resultRaw;
90 }
91 
92 void ImageRequestResult::setResultFormat(const QString &resultFormat)
93 {
94  m_resultFormat = resultFormat;
95 }
96 
97 QString ImageRequestResult::resultFormat() const
98 {
99  return m_resultFormat;
100 }
101 
102 void ImageRequestResult::setResultType(const ImageResultType resultType)
103 {
104  m_resultType = resultType;
105 }
106 
107 ImageResultType ImageRequestResult::resultType() const
108 {
109  return m_resultType;
110 }
111 
112 void OwsMappingCapabilities::setVersion(const QString &version)
113 {
114  m_version = version;
115 }
116 
117 QString OwsMappingCapabilities::version() const
118 {
119  return m_version;
120 }
121 
122 void OwsMappingCapabilities::setTitle(const QString &title)
123 {
124  m_title = title;
125 }
126 
127 QString OwsMappingCapabilities::title() const
128 {
129  return m_title;
130 }
131 
132 void OwsMappingCapabilities::setAbstract(const QString &abstract)
133 {
134  m_abstract = abstract;
135 }
136 
137 QString OwsMappingCapabilities::abstract() const
138 {
139  return m_abstract;
140 }
141 
142 void WmsCapabilities::setContactInformation(const QString &info)
143 {
144  m_contactInformation = info;
145 }
146 
147 QString WmsCapabilities::contactInformation() const
148 {
149  return m_contactInformation;
150 }
151 
152 void WmsCapabilities::setFees(const QString &fees)
153 {
154  m_fees = fees;
155 }
156 
157 QString WmsCapabilities::fees() const
158 {
159  return m_fees;
160 }
161 
162 QStringList OwsMappingCapabilities::layers() const
163 {
164  return m_owsLayerMetaInfo.keys();
165 }
166 
167 QStringList 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 
178 QString 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 
188 QString OwsMappingCapabilities::title(const QString &layer)
189 {
190  return m_owsLayerMetaInfo.value(layer).at(0);
191 }
192 
193 QString OwsMappingCapabilities::abstract(const QString &layer)
194 {
195  return m_owsLayerMetaInfo.value(layer).at(1);
196 }
197 
198 QString WmsCapabilities::legendUrl(const QString &layer)
199 {
200  return m_owsLayerMetaInfo.value(layer).at(2);
201 }
202 
203 QString OwsMappingCapabilities::style(const QString &layer)
204 {
205  return m_owsLayerMetaInfo.value(layer).at(3);
206 }
207 
208 QStringList WmsCapabilities::styles(const QStringList &layers)
209 {
210  QStringList retVal;
211  for (auto layer : layers) {
212  retVal << style(layer);
213  }
214  return retVal;
215 }
216 
217 QString 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 
257 void WmsCapabilities::setReferenceSystemType(const QString &refSystem)
258 {
259  m_referenceSystemType = refSystem;
260 }
261 
262 QString WmsCapabilities::referenceSystemType() const
263 {
264  return m_referenceSystemType;
265 }
266 
267 void OwsMappingCapabilities::setOwsLayerMetaInfo(const QMap<QString, QStringList> &wmsLayerMetaInfo)
268 {
269  m_owsLayerMetaInfo = wmsLayerMetaInfo;
270 }
271 
272 QMap<QString, QStringList> OwsMappingCapabilities::owsLayerMetaInfo() const
273 {
274  return m_owsLayerMetaInfo;
275 }
276 
277 void WmsCapabilities::setWmsLayerCoordinateSystems(const QMap<QString, QMap<QString, QString> > &wmsLayerCoordinateSystems)
278 {
279  m_wmsLayerCoordinateSystems = wmsLayerCoordinateSystems;
280 }
281 
282 QMap<QString, QMap<QString, QString> > WmsCapabilities::wmsLayerCoordinateSystems() const
283 {
284  return m_wmsLayerCoordinateSystems;
285 }
286 
287 void WmsCapabilities::setFormats(const QStringList &formats)
288 {
289  m_formats = formats;
290 }
291 
292 QStringList WmsCapabilities::formats()
293 {
294  return m_formats;
295 }
296 
297 
298 OwsServiceManager::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 
309 void 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 );
321 
322  mDebug() << "for url" << url;
323  m_capabilitiesAccessManager.get( request );
324 }
325 
326 void 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 
384 void 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 
404 void 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 
416 void 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 
431 void 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 
437 void 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 
443 void 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 
466 void OwsServiceManager::queryXYZPreviewImage(const QString &urlString)
467 {
468  m_imageRequestResult.setResultType(PreviewImage);
469  queryXYZImage(urlString);
470 }
471 
472 void OwsServiceManager::queryXYZLevelZeroTile(const QString &urlString)
473 {
474  m_imageRequestResult.setResultType(LevelZeroTile);
475  queryXYZImage(urlString);
476 }
477 
478 void 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 
493 void 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 
501 void OwsServiceManager::setCapabilitiesStatus(OwsCapabilitiesStatus capabilitiesStatus)
502 {
503  m_capabilitiesStatus = capabilitiesStatus;
504 }
505 
506 OwsCapabilitiesStatus OwsServiceManager::capabilitiesStatus() const
507 {
508  return m_capabilitiesStatus;
509 }
510 
511 OwsServiceType OwsServiceManager::owsServiceType()
512 {
513  return m_owsServiceType;
514 }
515 
516 WmsCapabilities Marble::OwsServiceManager::wmsCapabilities()
517 {
518  return m_wmsCapabilities;
519 }
520 
521 WmtsCapabilities OwsServiceManager::wmtsCapabilities()
522 {
523  return m_wmtsCapabilities;
524 }
525 
526 ImageRequestResult OwsServiceManager::imageRequestResult()
527 {
528  return m_imageRequestResult;
529 }
530 
531 QImage OwsServiceManager::resultImage()
532 {
533  return m_imageRequestResult.resultImage();
534 }
535 
536 ImageResultType OwsServiceManager::resultType()
537 {
538  return m_imageRequestResult.resultType();
539 }
540 
541 QByteArray OwsServiceManager::resultRaw()
542 {
543  return m_imageRequestResult.resultRaw();
544 }
545 
546 QString OwsServiceManager::resultFormat()
547 {
548  return m_imageRequestResult.resultFormat();
549 }
550 
551 void 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 
565  if( m_xml.documentElement().firstChildElement().tagName().isNull()
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 
593 void 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");
610  QString contactPersonPrimary;
611  if (!contactPersonPrimaryElement.isNull()) {
612  QString contactPerson = contactPersonPrimaryElement.firstChildElement("ContactPerson").text();
613  QString contactOrganisation = contactPersonPrimaryElement.firstChildElement("ContactOrganization").text();
614  contactPersonPrimary = contactPerson + "<br>" + contactOrganisation + "<br>";
615  }
616  QString contactPosition = contactElement.firstChildElement("ContactPosition").text();
617  contactPersonPrimary += contactPosition;
618 
619  QDomElement addressElement = contactElement.firstChildElement("ContactAddress");
620  QString postalAddress;
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 
640  QMap<QString, QStringList> wmsLayerMetaInfo;
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 
741 void 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 
753  QMap<QString, QStringList> wmtsLayerMetaInfo;
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 
788  QStringList tileMatrixSets;
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 
804 void OwsServiceManager::parseImageResult(QNetworkReply *reply)
805 {
806  // QString request = reply->request().url().toString();
807 
808  // m_imageResult is already reset in the queryWmsMap
809 
810  QImage testImage;
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"
836 #include "OwsServiceManager.moc" // needed for Q_OBJECT here in source
QString section(QChar sep, int start, int end, QString::SectionFlags flags) const const
QString text() const const
QDomElement toElement() const const
QString number(int n, int base)
void setPassword(const QString &password)
void setUser(const QString &user)
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
void addQueryItem(const QString &key, const QString &value)
QDomNodeList elementsByTagName(const QString &tagname) const const
bool isNull() const const
KI18NLOCALEDATA_EXPORT KCountry country(const char *ianaId)
void authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
QDomNode at(int index) const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
int size() const const
QUrl url() const const
void finished(QNetworkReply *reply)
bool isEmpty() const const
int length() const const
bool isNull() const const
const T & at(int i) const const
Binds a QML item to a specific geodetic location in screen coordinates.
PostalAddress address(const QVariant &location)
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
QDomElement firstChildElement(const QString &tagName) const const
double toDouble(bool *ok) const const
QString & replace(int position, int n, QChar after)
int removeDuplicates()
QString toLower() const const
QString host(QUrl::ComponentFormattingOptions options) const const
void setUrl(const QString &url, QUrl::ParsingMode parsingMode)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
unsigned int version()
QString right(int n) const const
QString name(StandardShortcut id)
const QChar at(int position) const const
void setUrl(const QUrl &url)
QByteArray readAll()
QString attribute(const QString &name, const QString &defValue) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QImage fromData(const uchar *data, int size, const char *format)
T value(int i) const const
void setAttribute(QNetworkRequest::Attribute code, const QVariant &value)
QDebug mDebug()
a function to replace qDebug() in Marble library code
Definition: MarbleDebug.cpp:31
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Oct 2 2023 03:52:09 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.