1 /***************************************************************************
2     qgswmscapabilities.h
3     ---------------------
4     begin                : January 2014
5     copyright            : (C) 2014 by Martin Dobias
6     email                : wonder dot sk at gmail dot com
7  ***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 #ifndef QGSWMSCAPABILITIES_H
16 #define QGSWMSCAPABILITIES_H
17 
18 #include <QHash>
19 #include <QMap>
20 #include <QNetworkRequest>
21 #include <QSet>
22 #include <QStringList>
23 #include <QVector>
24 
25 #include "qgsauthmanager.h"
26 #include "qgsraster.h"
27 #include "qgsrectangle.h"
28 #include "qgsrasteriterator.h"
29 #include "qgsapplication.h"
30 #include "qgsdataprovider.h"
31 #include "qgsinterval.h"
32 
33 
34 class QNetworkReply;
35 
36 /*
37  * The following structs reflect the WMS XML schema,
38  * as illustrated in Appendix E of the Web Map Service standard, version 1.3, 2004-08-02.
39  */
40 
41 //! OnlineResource Attribute structure
42 // TODO: Fill to WMS specifications
43 struct QgsWmsOnlineResourceAttribute
44 {
45   QString xlinkHref;
46 };
47 
48 //! Gets Property structure
49 // TODO: Fill to WMS specifications
50 struct QgsWmsGetProperty
51 {
52   QgsWmsOnlineResourceAttribute onlineResource;
53 };
54 
55 //! Post Property structure
56 // TODO: Fill to WMS specifications
57 struct QgsWmsPostProperty
58 {
59   QgsWmsOnlineResourceAttribute onlineResource;
60 };
61 
62 //! HTTP Property structure
63 // TODO: Fill to WMS specifications
64 struct QgsWmsHttpProperty
65 {
66   QgsWmsGetProperty    get;
67   QgsWmsPostProperty   post;  // can be null
68 };
69 
70 //! DCP Type Property structure
71 // TODO: Fill to WMS specifications
72 struct QgsWmsDcpTypeProperty
73 {
74   QgsWmsHttpProperty http;
75 };
76 
77 //! Operation Type structure (for GetMap and GetFeatureInfo)
78 // TODO: Fill to WMS specifications
79 struct QgsWmsOperationType
80 {
81   QStringList                      format;
82   QVector<QgsWmsDcpTypeProperty>   dcpType;
83   QStringList                      allowedEncodings;
84 };
85 
86 //! Request Property structure
87 // TODO: Fill to WMS specifications
88 struct QgsWmsRequestProperty
89 {
90   // QgsWmsGetCapabilitiesProperty   ...
91   // -- don't include since if we can get the capabilities,
92   //    we already know what's in this part.
93   QgsWmsOperationType     getMap;
94   QgsWmsOperationType     getFeatureInfo;
95   QgsWmsOperationType     getTile;
96   QgsWmsOperationType     getLegendGraphic;
97 };
98 
99 //! Exception Property structure
100 // TODO: Fill to WMS specifications
101 struct QgsWmsExceptionProperty
102 {
103   QStringList        format;   // text formats supported.
104 };
105 
106 //! Primary Contact Person Property structure
107 struct QgsWmsContactPersonPrimaryProperty
108 {
109   QString            contactPerson;
110   QString            contactOrganization;
111 };
112 
113 //! Contact Address Property structure
114 struct QgsWmsContactAddressProperty
115 {
116   QString            addressType;
117   QString            address;
118   QString            city;
119   QString            stateOrProvince;
120   QString            postCode;
121   QString            country;
122 };
123 
124 //! Contact Information Property structure
125 struct QgsWmsContactInformationProperty
126 {
127   QgsWmsContactPersonPrimaryProperty contactPersonPrimary;
128   QString                            contactPosition;
129   QgsWmsContactAddressProperty       contactAddress;
130   QString                            contactVoiceTelephone;
131   QString                            contactFacsimileTelephone;
132   QString                            contactElectronicMailAddress;
133 };
134 
135 //! Service Property structure
136 // TODO: Fill to WMS specifications
137 struct QgsWmsServiceProperty
138 {
139   QString                            title;
140   QString                            abstract;
141   QStringList                        keywordList;
142   QgsWmsOnlineResourceAttribute      onlineResource;
143   QgsWmsContactInformationProperty   contactInformation;
144   QString                            fees;
145   QString                            accessConstraints;
146   uint                               layerLimit = 0;
147   uint                               maxWidth = 0;
148   uint                               maxHeight = 0;
149 };
150 
151 //! Bounding Box Property structure
152 // TODO: Fill to WMS specifications
153 struct QgsWmsBoundingBoxProperty
154 {
155   QString   crs;
156   QgsRectangle   box;    // consumes minx, miny, maxx, maxy.
157 };
158 
159 /**
160  * \brief Dimension Property structure.
161  *
162  *  Contains the optional dimension element,
163  *  the element can be present in Service or Layer metadata
164  */
165 struct QgsWmsDimensionProperty
166 {
167   //! Name of the dimensional axis eg. time
168   QString   name;
169 
170   //! Units of the dimensional axis, defined from UCUM. Can be null.
171   QString   units;
172 
173   //! Optional, unit symbol a 7-bit ASCII character string also defined from UCUM.
174   QString   unitSymbol;
175 
176   //! Optional, default value to be used in GetMap request
177   QString   defaultValue;   // plain "default" is a reserved word
178 
179   //! Text containing available value(s) for the dimension
180   QString   extent;
181 
182   //! Optional, determines whether multiple values of the dimension can be requested
183   bool      multipleValues = false;
184 
185   //! Optional, whether nearest value of the dimension will be returned, if requested.
186   bool      nearestValue = false;
187 
188   //! Optional, valid only for temporal exents, determines whether data are normally kept current.
189   bool      current = false;
190 
191   //! Parse the dimension extent to QgsDateTimeRange instance
parseExtentQgsWmsDimensionProperty192   QgsDateTimeRange parseExtent() const
193   {
194     if ( extent.contains( '/' ) )
195     {
196       QStringList extentContent = extent.split( '/' );
197       int extentSize = extentContent.size();
198 
199       QDateTime start = QDateTime::fromString( extentContent.at( 0 ), Qt::ISODateWithMs );
200       QDateTime end = QDateTime::fromString( extentContent.at( extentSize - 2 ), Qt::ISODateWithMs );
201 
202       if ( start.isValid() & end.isValid() )
203         return QgsDateTimeRange( start, end );
204     }
205 
206     return QgsDateTimeRange();
207   }
208 
209   bool operator== ( const QgsWmsDimensionProperty &other ) const
210   {
211     return name == other.name && units == other.units &&
212            unitSymbol == other.unitSymbol && defaultValue == other.defaultValue &&
213            extent == other.extent && multipleValues == other.multipleValues &&
214            nearestValue == other.nearestValue && current == other.current;
215   }
216 
217 };
218 
219 //! Logo URL Property structure
220 // TODO: Fill to WMS specifications
221 struct QgsWmsLogoUrlProperty
222 {
223   QString                         format;
224   QgsWmsOnlineResourceAttribute   onlineResource;
225 
226   int                             width;
227   int                             height;
228 };
229 
230 //! Attribution Property structure
231 // TODO: Fill to WMS specifications
232 struct QgsWmsAttributionProperty
233 {
234   QString                         title;
235   QgsWmsOnlineResourceAttribute   onlineResource;
236   QgsWmsLogoUrlProperty           logoUrl;
237 };
238 
239 //! Legend URL Property structure
240 // TODO: Fill to WMS specifications
241 struct QgsWmsLegendUrlProperty
242 {
243   QString                         format;
244   QgsWmsOnlineResourceAttribute   onlineResource;
245 
246   int                             width;
247   int                             height;
248 };
249 
250 //! StyleSheet URL Property structure
251 // TODO: Fill to WMS specifications
252 struct QgsWmsStyleSheetUrlProperty
253 {
254   QString                         format;
255   QgsWmsOnlineResourceAttribute   onlineResource;
256 };
257 
258 //! Style URL Property structure
259 // TODO: Fill to WMS specifications
260 struct QgsWmsStyleUrlProperty
261 {
262   QString                         format;
263   QgsWmsOnlineResourceAttribute   onlineResource;
264 };
265 
266 //! Style Property structure
267 // TODO: Fill to WMS specifications
268 struct QgsWmsStyleProperty
269 {
270   QString                           name;
271   QString                           title;
272   QString                           abstract;
273   QVector<QgsWmsLegendUrlProperty>  legendUrl;
274   QgsWmsStyleSheetUrlProperty       styleSheetUrl;
275   QgsWmsStyleUrlProperty            styleUrl;
276 };
277 
278 //! Authority URL Property structure
279 // TODO: Fill to WMS specifications
280 struct QgsWmsAuthorityUrlProperty
281 {
282   QgsWmsOnlineResourceAttribute   onlineResource;
283   QString                         name;             // XML "NMTOKEN" type
284 };
285 
286 //! Identifier Property structure
287 // TODO: Fill to WMS specifications
288 struct QgsWmsIdentifierProperty
289 {
290   QString   authority;
291 };
292 
293 //! Metadata URL Property structure
294 // TODO: Fill to WMS specifications
295 struct QgsWmsMetadataUrlProperty
296 {
297   QString                         format;
298   QgsWmsOnlineResourceAttribute   onlineResource;
299   QString                         type;             // XML "NMTOKEN" type
300 };
301 
302 //! Data List URL Property structure
303 // TODO: Fill to WMS specifications
304 struct QgsWmsDataListUrlProperty
305 {
306   QString                         format;
307   QgsWmsOnlineResourceAttribute   onlineResource;
308 };
309 
310 //! Feature List URL Property structure
311 // TODO: Fill to WMS specifications
312 struct QgsWmsFeatureListUrlProperty
313 {
314   QString                         format;
315   QgsWmsOnlineResourceAttribute   onlineResource;
316 };
317 
318 //! Layer Property structure
319 // TODO: Fill to WMS specifications
320 struct QgsWmsLayerProperty
321 {
322   // WMS layer properties
323   int                                     orderId;
324   QString                                 name;
325   QString                                 title;
326   QString                                 abstract;
327   QStringList                             keywordList;
328   QStringList                             crs;        // coord ref sys
329   QgsRectangle                            ex_GeographicBoundingBox;
330   QVector<QgsWmsBoundingBoxProperty>      boundingBoxes;
331   QgsWmsAttributionProperty               attribution;
332   QVector<QgsWmsAuthorityUrlProperty>     authorityUrl;
333   QVector<QgsWmsIdentifierProperty>       identifier;
334   QVector<QgsWmsDimensionProperty>        dimensions;
335   QVector<QgsWmsMetadataUrlProperty>      metadataUrl;
336   QVector<QgsWmsDataListUrlProperty>      dataListUrl;
337   QVector<QgsWmsFeatureListUrlProperty>   featureListUrl;
338   QVector<QgsWmsStyleProperty>            style;
339   double                                  minimumScaleDenominator;
340   double                                  maximumScaleDenominator;
341   QVector<QgsWmsLayerProperty>            layer;      // nested layers
342 
343   // WMS layer attributes
344   bool               queryable;
345   int                cascaded;
346   bool               opaque;
347   bool               noSubsets;
348   int                fixedWidth;
349   int                fixedHeight;
350 
351   // TODO need to expand this to cover more of layer properties
equalQgsWmsLayerProperty352   bool equal( const QgsWmsLayerProperty &layerProperty )
353   {
354     if ( !( name == layerProperty.name ) )
355       return false;
356     if ( !( title == layerProperty.title ) )
357       return false;
358     if ( !( abstract == layerProperty.abstract ) )
359       return false;
360     if ( !( dimensions == layerProperty.dimensions ) )
361       return false;
362 
363     return true;
364   }
365 
366   /**
367    * Returns true if it the struct has the dimension with the passed name
368    */
hasDimensionQgsWmsLayerProperty369   bool hasDimension( QString dimensionName ) const
370   {
371     if ( dimensions.isEmpty() )
372       return false;
373 
374     for ( const QgsWmsDimensionProperty &dimension : qgis::as_const( dimensions ) )
375     {
376       if ( dimension.name == dimensionName )
377         return true;
378     }
379 
380     return false;
381   }
382 
383   /**
384    * Attempts to return a preferred CRS from the list of available CRS definitions.
385    *
386    * Prioritizes the first listed CRS, unless it's a block listed value.
387    */
preferredAvailableCrsQgsWmsLayerProperty388   QString preferredAvailableCrs() const
389   {
390     static QSet< QString > sSkipList { QStringLiteral( "EPSG:900913" ) };
391     for ( const QString &candidate : crs )
392     {
393       if ( sSkipList.contains( candidate ) )
394         continue;
395 
396       return candidate;
397     }
398     return crs.value( 0 );
399   }
400 };
401 
402 /**
403  * Stores the dates parts from the WMS-T dimension extent.
404  *
405  */
406 struct QgsWmstDates
407 {
QgsWmstDatesQgsWmstDates408   QgsWmstDates( QList< QDateTime > dates )
409   {
410     dateTimes = dates;
411   }
QgsWmstDatesQgsWmstDates412   QgsWmstDates()
413   {
414 
415   }
416 
417   bool operator== ( const QgsWmstDates &other )
418   {
419     return dateTimes == other.dateTimes;
420   }
421 
422   QList< QDateTime > dateTimes;
423 };
424 
425 /**
426  * Stores resolution part of the WMS-T dimension extent.
427  *
428  * If resolution does not exist, active() will return false;
429  */
430 struct QgsWmstResolution
431 {
432   int year = -1;
433   int month = -1;
434   int day = -1;
435 
436   int hour = -1;
437   int minutes = -1;
438   int seconds = -1;
439 
intervalQgsWmstResolution440   long long interval() const
441   {
442     long long secs = 0.0;
443 
444     if ( year != -1 )
445       secs += year * QgsInterval::YEARS ;
446     if ( month != -1 )
447       secs += month * QgsInterval::MONTHS;
448     if ( day != -1 )
449       secs += day * QgsInterval::DAY;
450     if ( hour != -1 )
451       secs += hour * QgsInterval::HOUR;
452     if ( minutes != -1 )
453       secs += minutes * QgsInterval::MINUTE;
454     if ( seconds != -1 )
455       secs += seconds;
456 
457     return secs;
458   }
459 
activeQgsWmstResolution460   bool active() const
461   {
462     return year != -1 || month != -1 || day != -1 ||
463            hour != -1 || minutes != -1 || seconds != -1;
464   }
465 
textQgsWmstResolution466   QString text() const
467   {
468     QString text( "P" );
469 
470     if ( year != -1 )
471     {
472       text.append( QString::number( year ) );
473       text.append( 'Y' );
474     }
475     if ( month != -1 )
476     {
477       text.append( QString::number( month ) );
478       text.append( 'M' );
479     }
480     if ( day != -1 )
481     {
482       text.append( QString::number( day ) );
483       text.append( 'D' );
484     }
485 
486     if ( hour != -1 )
487     {
488       if ( !text.contains( 'T' ) )
489         text.append( 'T' );
490       text.append( QString::number( hour ) );
491       text.append( 'H' );
492     }
493     if ( minutes != -1 )
494     {
495       if ( !text.contains( 'T' ) )
496         text.append( 'T' );
497       text.append( QString::number( minutes ) );
498       text.append( 'M' );
499     }
500     if ( seconds != -1 )
501     {
502       if ( !text.contains( 'T' ) )
503         text.append( 'T' );
504       text.append( QString::number( seconds ) );
505       text.append( 'S' );
506     }
507     return text;
508   }
509 
510   bool operator==( const QgsWmstResolution &other ) const
511   {
512     return year == other.year && month == other.month &&
513            day == other.day && hour == other.hour &&
514            minutes == other.minutes && seconds == other.seconds;
515   }
516 
517 };
518 
519 
520 /**
521  * Stores dates and resolution structure pair.
522  */
523 struct QgsWmstExtentPair
524 {
QgsWmstExtentPairQgsWmstExtentPair525   QgsWmstExtentPair()
526   {
527   }
528 
QgsWmstExtentPairQgsWmstExtentPair529   QgsWmstExtentPair( QgsWmstDates otherDates, QgsWmstResolution otherResolution )
530   {
531     dates = otherDates;
532     resolution = otherResolution;
533   }
534 
535   bool operator ==( const QgsWmstExtentPair &other )
536   {
537     return dates == other.dates &&
538            resolution == other.resolution;
539   }
540 
541   QgsWmstDates dates;
542   QgsWmstResolution resolution;
543 };
544 
545 
546 /**
547  * Stores  the WMS-T dimension extent.
548  */
549 struct QgsWmstDimensionExtent
550 {
551   QList <QgsWmstExtentPair> datesResolutionList;
552 };
553 
554 struct QgsWmtsTheme
555 {
556   QString identifier;
557   QString title, abstract;
558   QStringList keywords;
559   QgsWmtsTheme *subTheme = nullptr;
560   QStringList layerRefs;
561 
562   QgsWmtsTheme() = default;
~QgsWmtsThemeQgsWmtsTheme563   ~QgsWmtsTheme() { delete subTheme; }
564 };
565 
566 struct QgsWmtsTileMatrixLimits;
567 
568 struct QgsWmtsTileMatrix
569 {
570   QString identifier;
571   QString title, abstract;
572   QStringList keywords;
573   double scaleDenom;
574   QgsPointXY topLeft;  //!< Top-left corner of the tile matrix in map units
575   int tileWidth;     //!< Width of a tile in pixels
576   int tileHeight;    //!< Height of a tile in pixels
577   int matrixWidth;   //!< Number of tiles horizontally
578   int matrixHeight;  //!< Number of tiles vertically
579   double tres;       //!< Pixel span in map units
580 
581   /**
582    * Returns extent of a tile in map coordinates.
583    * (same function as tileBBox() but returns QRectF instead of QgsRectangle)
584    */
585   QRectF tileRect( int col, int row ) const;
586 
587   /**
588    * Returns extent of a tile in map coordinates
589    * (same function as tileRect() but returns QgsRectangle instead of QRectF)
590    */
591   QgsRectangle tileBBox( int col, int row ) const;
592 
593   /**
594    * Returns range of tiles that intersects with the view extent
595    * (\a tml may be NULLPTR)
596    */
597   void viewExtentIntersection( const QgsRectangle &viewExtent, const QgsWmtsTileMatrixLimits *tml, int &col0, int &row0, int &col1, int &row1 ) const;
598 
599 };
600 
601 struct QgsWmtsTileMatrixSet
602 {
603   QString identifier;   //!< Tile matrix set identifier
604   QString title;        //!< Human readable tile matrix set name
605   QString abstract;     //!< Brief description of the tile matrix set
606   QStringList keywords; //!< List of words/phrases to describe the dataset
607   QString crs;          //!< CRS of the tile matrix set
608   QString wkScaleSet;   //!< Optional reference to a well-known scale set
609   //! available tile matrixes (key = pixel span in map units)
610   QMap<double, QgsWmtsTileMatrix> tileMatrices;
611 
612   //! Returns closest tile resolution to the requested one. (resolution = width [map units] / with [pixels])
613   const QgsWmtsTileMatrix *findNearestResolution( double vres ) const;
614 
615   //! Returns the tile matrix for other near resolution from given tres (positive offset = lower resolution tiles)
616   const QgsWmtsTileMatrix *findOtherResolution( double tres, int offset ) const;
617 };
618 
619 enum QgsTileMode { WMTS, WMSC, XYZ };
620 
621 struct QgsWmtsTileMatrixLimits
622 {
623   QString tileMatrix;
624   int minTileRow, maxTileRow;
625   int minTileCol, maxTileCol;
626 };
627 
628 struct QgsWmtsTileMatrixSetLink
629 {
630   QString tileMatrixSet;
631   QHash<QString, QgsWmtsTileMatrixLimits> limits;
632 };
633 
634 struct QgsWmtsLegendURL
635 {
636   QString format;
637   double minScale, maxScale;
638   QString href;
639   int width, height;
640 };
641 
642 struct QgsWmtsStyle
643 {
644   QString identifier;
645   QString title, abstract;
646   QStringList keywords;
647   bool isDefault;
648   QList<QgsWmtsLegendURL> legendURLs;
649 };
650 
651 /**
652  * In case of multi-dimensional data, the service metadata can describe their multi-
653  * dimensionality and tiles can be requested at specific values in these dimensions.
654  * Examples of dimensions are Time, Elevation and Band.
655  */
656 struct QgsWmtsDimension
657 {
658   QString identifier;   //!< Name of the dimensional axis
659   QString title;        //!< Human readable name
660   QString abstract;     //!< Brief description of the dimension
661   QStringList keywords; //!< List of words/phrases to describe the dataset
662   QString UOM;          //!< Units of measure of dimensional axis
663   QString unitSymbol;   //!< Symbol of the units
664   QString defaultValue; //!< Default value to be used if value is not specified in request
665   bool current;         //!< Indicates whether temporal data are normally kept current
666   QStringList values;   //!< Available values for this dimension
667 };
668 
669 struct QgsWmtsTileLayer
670 {
671   enum QgsTileMode tileMode;
672   QString identifier;
673   QString title, abstract;
674   QStringList keywords;
675   QVector<QgsWmsBoundingBoxProperty> boundingBoxes;
676   QStringList formats;
677   QStringList infoFormats;
678   QString defaultStyle;
679   int dpi = -1;   //!< DPI of the tile layer (-1 for unknown DPI)
680   //! available dimensions (optional, for multi-dimensional data)
681   QHash<QString, QgsWmtsDimension> dimensions;
682   QHash<QString, QgsWmtsStyle> styles;
683   QHash<QString, QgsWmtsTileMatrixSetLink> setLinks;
684 
685   QHash<QString, QString> getTileURLs;
686   QHash<QString, QString> getFeatureInfoURLs;
687 };
688 
689 //! Capability Property structure
690 // TODO: Fill to WMS specifications
691 struct QgsWmsCapabilityProperty
692 {
693   QgsWmsRequestProperty                request;
694   QgsWmsExceptionProperty              exception;
695 
696   // Top level layer should normally be present max once
697   // <element name="Capability">
698   //    <element ref="wms:Layer" minOccurs="0"/>  - default maxOccurs=1
699   // but there are a few non conformant capabilities around (#13762)
700   QList<QgsWmsLayerProperty>           layers;
701 
702   QList<QgsWmtsTileLayer>              tileLayers;
703   QHash<QString, QgsWmtsTileMatrixSet> tileMatrixSets;
704 };
705 
706 //! Capabilities Property structure
707 // TODO: Fill to WMS specifications
708 struct QgsWmsCapabilitiesProperty
709 {
710   QgsWmsServiceProperty         service;
711   QgsWmsCapabilityProperty      capability;
712   QString                       version;
713 };
714 
715 //! Formats supported by QImageReader
716 struct QgsWmsSupportedFormat
717 {
718   QString format;
719   QString label;
720 };
721 
722 enum QgsWmsTileAttribute
723 {
724   TileReqNo = QNetworkRequest::User + 0,
725   TileIndex = QNetworkRequest::User + 1,
726   TileRect  = QNetworkRequest::User + 2,
727   TileRetry = QNetworkRequest::User + 3,
728 };
729 
730 enum QgsWmsDpiMode
731 {
732   DpiNone = 0,
733   DpiQGIS = 1,
734   DpiUMN = 2,
735   DpiGeoServer = 4,
736   DpiAll = DpiQGIS | DpiUMN | DpiGeoServer,
737 };
738 
739 
740 
741 struct QgsWmsParserSettings
742 {
743   QgsWmsParserSettings( bool ignAxis = false, bool invAxis = false )
ignoreAxisOrientationQgsWmsParserSettings744     : ignoreAxisOrientation( ignAxis )
745     , invertAxisOrientation( invAxis )
746   {}
747   bool ignoreAxisOrientation;
748   bool invertAxisOrientation;
749 };
750 
751 struct QgsWmsAuthorization
752 {
753   QgsWmsAuthorization( const QString &userName = QString(), const QString &password = QString(), const QString &referer = QString(), const QString &authcfg = QString() )
mUserNameQgsWmsAuthorization754     : mUserName( userName )
755     , mPassword( password )
756     , mReferer( referer )
757     , mAuthCfg( authcfg )
758   {}
759 
setAuthorizationQgsWmsAuthorization760   bool setAuthorization( QNetworkRequest &request ) const
761   {
762     if ( !mAuthCfg.isEmpty() )
763     {
764       return QgsApplication::authManager()->updateNetworkRequest( request, mAuthCfg );
765     }
766     else if ( !mUserName.isEmpty() || !mPassword.isEmpty() )
767     {
768       request.setRawHeader( "Authorization", "Basic " + QStringLiteral( "%1:%2" ).arg( mUserName, mPassword ).toUtf8().toBase64() );
769     }
770 
771     if ( !mReferer.isEmpty() )
772     {
773       request.setRawHeader( "Referer", mReferer.toLatin1() );
774     }
775     return true;
776   }
777   //! Sets authorization reply
setAuthorizationReplyQgsWmsAuthorization778   bool setAuthorizationReply( QNetworkReply *reply ) const
779   {
780     if ( !mAuthCfg.isEmpty() )
781     {
782       return QgsApplication::authManager()->updateNetworkReply( reply, mAuthCfg );
783     }
784     return true;
785   }
786 
787   //! Username for basic http authentication
788   QString mUserName;
789 
790   //! Password for basic http authentication
791   QString mPassword;
792 
793   //! Referer for http requests
794   QString mReferer;
795 
796   //! Authentication configuration ID
797   QString mAuthCfg;
798 };
799 
800 
801 //! URI that gets passed to provider
802 class QgsWmsSettings
803 {
804   public:
805 
806     bool parseUri( const QString &uriString );
807 
baseUrl()808     QString baseUrl() const { return mBaseUrl; }
authorization()809     QgsWmsAuthorization authorization() const { return mAuth; }
810 
parserSettings()811     QgsWmsParserSettings parserSettings() const { return mParserSettings; }
812 
813     /**
814      * Parse the given string extent into a well defined dates and resolution structures.
815      * The string extent comes from WMS-T dimension capabilities.
816      *
817      * \since QGIS 3.14
818      */
819     QgsWmstDimensionExtent parseTemporalExtent( const QString &extent );
820 
821     /**
822      * Sets the dimension extent property
823      *
824      * \see timeDimensionExtent()
825      * \since QGIS 3.14
826      */
827     void setTimeDimensionExtent( const QgsWmstDimensionExtent &timeDimensionExtent );
828 
829     /**
830      * Returns the dimension extent property.
831      *
832      * \see setTimeDimensionExtent()
833      * \since QGIS 3.14
834      */
835     QgsWmstDimensionExtent timeDimensionExtent() const;
836 
837     /**
838      * Parse the given string item into a resolution structure.
839      *
840      * \since QGIS 3.14
841      */
842     QgsWmstResolution parseWmstResolution( const QString &item );
843 
844     /**
845      * Parse the given string item into QDateTime instant.
846      *
847      * \since QGIS 3.14
848      */
849     QDateTime parseWmstDateTimes( const QString &item );
850 
851     /**
852      * Returns the datetime with the sum of passed \a dateTime and the \a resolution time.
853      *
854      * \since QGIS 3.14
855      */
856     QDateTime addTime( const QDateTime &dateTime, const QgsWmstResolution &resolution );
857 
858     /**
859      * Finds the least closest datetime from list of available dimension temporal ranges
860      * with the given \a dateTime.
861      *
862      * \note It works with wms-t capabilities that provide time dimension with temporal ranges only.
863      *
864      * \since QGIS 3.14
865      */
866     QDateTime findLeastClosestDateTime( const QDateTime &dateTime, bool dateOnly = false ) const;
867 
868   protected:
869     QgsWmsParserSettings    mParserSettings;
870 
871     //! layer is tiled, tile layer and active matrix set
872     bool                    mTiled;
873     //! whether we actually work with XYZ tiles instead of WMS / WMTS
874     bool mXyz;
875 
876     //! Whether we are dealing with WMS-T
877     bool mIsTemporal = false;
878 
879     //! Whether we are dealing bi-temporal dimensional WMS-T
880     bool mIsBiTemporal = false;
881 
882     //! Temporal extent from dimension property in WMS-T
883     QString mTemporalExtent;
884 
885     //! Fixed temporal range for the data provider
886     QgsDateTimeRange mFixedRange;
887 
888     //! Fixed reference temporal range for the data provider
889     QgsDateTimeRange mFixedReferenceRange;
890 
891     //! Stores WMS-T time dimension extent dates
892     QgsWmstDimensionExtent mTimeDimensionExtent;
893 
894     //! Stores WMS-T reference dimension extent dates
895     QgsWmstDimensionExtent mReferenceTimeDimensionExtent;
896 
897     //! whether we are dealing with MBTiles file rather than using network-based tiles
898     bool mIsMBTiles = false;
899     //! chosen values for dimensions in case of multi-dimensional data (key=dim id, value=dim value)
900     QHash<QString, QString>  mTileDimensionValues;
901     //! name of the chosen tile matrix set
902     QString                 mTileMatrixSetId;
903 
904     /**
905      * Maximum width and height of getmap requests
906      */
907     int mMaxWidth;
908     int mMaxHeight;
909 
910     /**
911      * Step size when iterating the layer
912      */
913     int mStepWidth = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_WIDTH;
914     int mStepHeight = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_HEIGHT;
915 
916     //! Data source URI of the WMS for this layer
917     QString mHttpUri;
918 
919     //! URL part of URI (httpuri)
920     QString mBaseUrl;
921 
922     QgsWmsAuthorization mAuth;
923 
924     bool mIgnoreGetMapUrl;
925     bool mIgnoreGetFeatureInfoUrl;
926     bool mIgnoreReportedLayerExtents = false;
927     bool mSmoothPixmapTransform;
928     enum QgsWmsDpiMode mDpiMode;
929 
930     /**
931      * Active sublayers managed by this provider in a draw function, in order from bottom to top
932      * (some may not be visible in a draw function, cf. activeSubLayerVisibility)
933      */
934     QStringList mActiveSubLayers;
935     QStringList mActiveSubStyles;
936 
937     QStringList mOpacities;
938 
939     /**
940      * Visibility status of the given active sublayer
941      */
942     QMap<QString, bool> mActiveSubLayerVisibility;
943 
944     //! FEATURE_COUNT for GetFeatureInfo
945     int mFeatureCount;
946 
947     /**
948      * MIME type of the image encoding used from the WMS server
949      */
950     QString mImageMimeType;
951 
952     QString mCrsId;
953 
954     bool mEnableContextualLegend;
955 
956     friend class QgsWmsProvider;
957 };
958 
959 
960 //! Keeps information about capabilities of particular URI
961 class QgsWmsCapabilities
962 {
963   public:
964 
965     /**
966      * Constructs a QgsWmsCapabilities object with the given \a coordinateTransformContext
967      */
968     QgsWmsCapabilities( const QgsCoordinateTransformContext &coordinateTransformContext = QgsCoordinateTransformContext(), const QString &baseUrl = QString() );
969 
isValid()970     bool isValid() const { return mValid; }
971 
972     bool parseResponse( const QByteArray &response, QgsWmsParserSettings settings );
973 
lastError()974     QString lastError() const { return mError; }
lastErrorFormat()975     QString lastErrorFormat() const { return mErrorFormat; }
976 
capabilitiesProperty()977     QgsWmsCapabilitiesProperty capabilitiesProperty() { return mCapabilities; }
978 
979     /**
980      * \brief   Returns a list of the supported layers of the WMS server
981      *
982      * \returns The list of layers will be placed here.
983      *
984      * \todo Document this better
985      */
supportedLayers()986     QVector<QgsWmsLayerProperty> supportedLayers() const { return mLayersSupported; }
987 
988     //! Gets raster image encodings supported by the WMS, expressed as MIME types
supportedImageEncodings()989     QStringList supportedImageEncodings() const { return mCapabilities.capability.request.getMap.format; }
990 
991     /**
992      * \brief   Returns a map for the hierarchy of layers
993      */
layerParents(QMap<int,int> & parents,QMap<int,QStringList> & parentNames)994     void layerParents( QMap<int, int> &parents, QMap<int, QStringList> &parentNames ) const { parents = mLayerParents; parentNames = mLayerParentNames; }
995 
996     /**
997      * \brief   Returns a list of the supported tile layers of the WMS server
998      *
999      * \returns The list of tile sets will be placed here.
1000      */
supportedTileLayers()1001     QList<QgsWmtsTileLayer> supportedTileLayers() const { return mTileLayersSupported; }
1002 
1003     /**
1004      * \brief   Returns a list of the available tile matrix sets
1005      */
supportedTileMatrixSets()1006     QHash<QString, QgsWmtsTileMatrixSet> supportedTileMatrixSets() const { return mTileMatrixSets; }
1007 
1008     //! Find out whether to invert axis orientation when parsing/writing coordinates
1009     bool shouldInvertAxisOrientation( const QString &ogcCrs );
1010 
1011     //! Find out identify capabilities
1012     int identifyCapabilities() const;
1013 
1014   protected:
1015     bool parseCapabilitiesDom( const QByteArray &xml, QgsWmsCapabilitiesProperty &capabilitiesProperty );
1016 
1017     void parseService( const QDomElement &element, QgsWmsServiceProperty &serviceProperty );
1018     void parseOnlineResource( const QDomElement &element, QgsWmsOnlineResourceAttribute &onlineResourceAttribute );
1019     void parseKeywordList( const QDomElement &element, QStringList &keywordListProperty );
1020     void parseContactInformation( const QDomElement &element, QgsWmsContactInformationProperty &contactInformationProperty );
1021     void parseContactPersonPrimary( const QDomElement &element, QgsWmsContactPersonPrimaryProperty &contactPersonPrimaryProperty );
1022     void parseContactAddress( const QDomElement &element, QgsWmsContactAddressProperty &contactAddressProperty );
1023 
1024     void parseCapability( const QDomElement &element, QgsWmsCapabilityProperty &capabilityProperty );
1025     void parseRequest( const QDomElement &element, QgsWmsRequestProperty &requestProperty );
1026     void parseDimension( const QDomElement &element, QgsWmsDimensionProperty &dimensionProperty );
1027     void parseExtent( const QDomElement &element, QVector<QgsWmsDimensionProperty> &dimensionProperties );
1028     void parseLegendUrl( const QDomElement &element, QgsWmsLegendUrlProperty &legendUrlProperty );
1029     void parseMetadataUrl( const QDomElement &element, QgsWmsMetadataUrlProperty &metadataUrlProperty );
1030     void parseLayer( const QDomElement &element, QgsWmsLayerProperty &layerProperty, QgsWmsLayerProperty *parentProperty = nullptr );
1031     void parseStyle( const QDomElement &element, QgsWmsStyleProperty &styleProperty );
1032 
1033     void parseOperationType( const QDomElement &element, QgsWmsOperationType &operationType );
1034     void parseDcpType( const QDomElement &element, QgsWmsDcpTypeProperty &dcpType );
1035     void parseHttp( const QDomElement &element, QgsWmsHttpProperty &httpProperty );
1036     void parseGet( const QDomElement &element, QgsWmsGetProperty &getProperty );
1037     void parsePost( const QDomElement &element, QgsWmsPostProperty &postProperty );
1038 
1039     void parseTileSetProfile( const QDomElement &element );
1040     void parseWMTSContents( const QDomElement &element );
1041     void parseKeywords( const QDomNode &e, QStringList &keywords );
1042     void parseTheme( const QDomElement &e, QgsWmtsTheme &t );
1043 
1044     QString nodeAttribute( const QDomElement &element, const QString &name, const QString &defValue = QString() );
1045 
1046     /**
1047      * In case no bounding box is present in WMTS capabilities, try to estimate it from tile matrix sets.
1048      * Returns true if the detection went fine.
1049      */
1050     bool detectTileLayerBoundingBox( QgsWmtsTileLayer &l );
1051 
1052   protected:
1053     bool mValid = false;
1054 
1055     QString mError;
1056     QString mErrorCaption;
1057     QString mErrorFormat;
1058 
1059     QgsWmsParserSettings mParserSettings;
1060 
1061     //! number of layers and parents
1062     int mLayerCount = -1;
1063     QMap<int, int> mLayerParents;
1064     QMap<int, QStringList> mLayerParentNames;
1065 
1066     /**
1067      * WMS "queryable" per layer
1068      * Used in determining if the Identify map tool can be useful on the rendered WMS map layer.
1069      */
1070     QMap<QString, bool> mQueryableForLayer;
1071 
1072     /**
1073      * layers hosted by the WMS
1074      */
1075     QVector<QgsWmsLayerProperty> mLayersSupported;
1076 
1077     /**
1078      * tilesets hosted by the WMTS
1079      */
1080     QList<QgsWmtsTileLayer> mTileLayersSupported;
1081 
1082     /**
1083      * themes hosted by the WMTS
1084      */
1085     QList<QgsWmtsTheme> mTileThemes;
1086 
1087     /**
1088      * Parsed capabilities of the WMS
1089      */
1090     QgsWmsCapabilitiesProperty mCapabilities;
1091 
1092     //! Formats supported by server and provider
1093     QMap<QgsRaster::IdentifyFormat, QString> mIdentifyFormats;
1094 
1095 
1096     /**
1097      * tile matrix sets hosted by the WMS
1098      */
1099     QHash<QString, QgsWmtsTileMatrixSet> mTileMatrixSets;
1100 
1101     //temporarily caches invert axis setting for each crs
1102     QHash<QString, bool> mCrsInvertAxis;
1103 
1104   private:
1105 
1106     QgsCoordinateTransformContext mCoordinateTransformContext;
1107     QString mBaseUrl;
1108 
1109     friend class QgsWmsProvider;
1110     friend class TestQgsWmsCapabilities;
1111 };
1112 
1113 
1114 
1115 /**
1116  * Class that handles download of capabilities.
1117  */
1118 class QgsWmsCapabilitiesDownload : public QObject
1119 {
1120     Q_OBJECT
1121 
1122   public:
1123     explicit QgsWmsCapabilitiesDownload( bool forceRefresh, QObject *parent = nullptr );
1124 
1125     QgsWmsCapabilitiesDownload( const QString &baseUrl, const QgsWmsAuthorization &auth, bool forceRefresh, QObject *parent = nullptr );
1126 
1127     ~QgsWmsCapabilitiesDownload() override;
1128 
1129     bool downloadCapabilities();
1130 
1131     bool downloadCapabilities( const QString &baseUrl, const QgsWmsAuthorization &auth );
1132 
lastError()1133     QString lastError() const { return mError; }
1134 
response()1135     QByteArray response() const { return mHttpCapabilitiesResponse; }
1136 
1137     //! Abort network request immediately
1138     void abort();
1139 
1140   signals:
1141     //! \brief emit a signal to be caught by qgisapp and display a msg on status bar
1142     void statusChanged( QString const   &statusQString );
1143 
1144     //! \brief emit a signal once the download is finished
1145     void downloadFinished();
1146 
1147   protected slots:
1148     void capabilitiesReplyFinished();
1149     void capabilitiesReplyProgress( qint64, qint64 );
1150 
1151   protected:
1152     //! URL part of URI (httpuri)
1153     QString mBaseUrl;
1154 
1155     QgsWmsAuthorization mAuth;
1156 
1157     //! The reply to the capabilities request
1158     QNetworkReply *mCapabilitiesReply = nullptr;
1159 
1160     //! The error message associated with the last WMS error.
1161     QString mError;
1162 
1163     //! The mime type of the message
1164     QString mErrorFormat;
1165 
1166     //! Capabilities of the WMS (raw)
1167     QByteArray mHttpCapabilitiesResponse;
1168 
1169     bool mIsAborted;
1170     bool mForceRefresh;
1171 };
1172 
1173 
1174 
1175 #endif // QGSWMSCAPABILITIES_H
1176