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 #include "qgstemporalutils.h"
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 : std::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 dates and resolution structure pair.
427  */
428 struct QgsWmstExtentPair
429 {
QgsWmstExtentPairQgsWmstExtentPair430   QgsWmstExtentPair()
431   {
432   }
433 
QgsWmstExtentPairQgsWmstExtentPair434   QgsWmstExtentPair( QgsWmstDates dates, QgsTimeDuration resolution )
435     : dates( dates )
436     , resolution( resolution )
437   {
438   }
439 
440   bool operator ==( const QgsWmstExtentPair &other )
441   {
442     return dates == other.dates &&
443            resolution == other.resolution;
444   }
445 
446   QgsWmstDates dates;
447   QgsTimeDuration resolution;
448 
449 };
450 
451 
452 /**
453  * Stores  the WMS-T dimension extent.
454  */
455 struct QgsWmstDimensionExtent
456 {
457   QList <QgsWmstExtentPair> datesResolutionList;
458 };
459 
460 struct QgsWmtsTheme
461 {
462   QString identifier;
463   QString title, abstract;
464   QStringList keywords;
465   QgsWmtsTheme *subTheme = nullptr;
466   QStringList layerRefs;
467 
468   QgsWmtsTheme() = default;
~QgsWmtsThemeQgsWmtsTheme469   ~QgsWmtsTheme() { delete subTheme; }
470 };
471 
472 struct QgsWmtsTileMatrixLimits;
473 
474 struct QgsWmtsTileMatrix
475 {
476   QString identifier;
477   QString title, abstract;
478   QStringList keywords;
479   double scaleDenom = 0;
480   QgsPointXY topLeft;  //!< Top-left corner of the tile matrix in map units
481   int tileWidth;     //!< Width of a tile in pixels
482   int tileHeight;    //!< Height of a tile in pixels
483   int matrixWidth;   //!< Number of tiles horizontally
484   int matrixHeight;  //!< Number of tiles vertically
485   double tres;       //!< Pixel span in map units
486 
487   /**
488    * Returns extent of a tile in map coordinates.
489    * (same function as tileBBox() but returns QRectF instead of QgsRectangle)
490    */
491   QRectF tileRect( int col, int row ) const;
492 
493   /**
494    * Returns extent of a tile in map coordinates
495    * (same function as tileRect() but returns QgsRectangle instead of QRectF)
496    */
497   QgsRectangle tileBBox( int col, int row ) const;
498 
499   /**
500    * Returns range of tiles that intersects with the view extent
501    * (\a tml may be NULLPTR)
502    */
503   void viewExtentIntersection( const QgsRectangle &viewExtent, const QgsWmtsTileMatrixLimits *tml, int &col0, int &row0, int &col1, int &row1 ) const;
504 
505 };
506 
507 struct QgsWmtsTileMatrixSet
508 {
509   QString identifier;   //!< Tile matrix set identifier
510   QString title;        //!< Human readable tile matrix set name
511   QString abstract;     //!< Brief description of the tile matrix set
512   QStringList keywords; //!< List of words/phrases to describe the dataset
513   QString crs;          //!< CRS of the tile matrix set
514   QString wkScaleSet;   //!< Optional reference to a well-known scale set
515   //! available tile matrixes (key = pixel span in map units)
516   QMap<double, QgsWmtsTileMatrix> tileMatrices;
517 
518   //! Returns closest tile resolution to the requested one. (resolution = width [map units] / with [pixels])
519   const QgsWmtsTileMatrix *findNearestResolution( double vres ) const;
520 
521   //! Returns the tile matrix for other near resolution from given tres (positive offset = lower resolution tiles)
522   const QgsWmtsTileMatrix *findOtherResolution( double tres, int offset ) const;
523 };
524 
525 enum QgsTileMode { WMTS, WMSC, XYZ };
526 
527 struct QgsWmtsTileMatrixLimits
528 {
529   QString tileMatrix;
530   int minTileRow, maxTileRow;
531   int minTileCol, maxTileCol;
532 };
533 
534 struct QgsWmtsTileMatrixSetLink
535 {
536   QString tileMatrixSet;
537   QHash<QString, QgsWmtsTileMatrixLimits> limits;
538 };
539 
540 struct QgsWmtsLegendURL
541 {
542   QString format;
543   double minScale, maxScale;
544   QString href;
545   int width, height;
546 };
547 
548 struct QgsWmtsStyle
549 {
550   QString identifier;
551   QString title, abstract;
552   QStringList keywords;
553   bool isDefault = false;
554   QList<QgsWmtsLegendURL> legendURLs;
555 };
556 
557 /**
558  * In case of multi-dimensional data, the service metadata can describe their multi-
559  * dimensionality and tiles can be requested at specific values in these dimensions.
560  * Examples of dimensions are Time, Elevation and Band.
561  */
562 struct QgsWmtsDimension
563 {
564   QString identifier;   //!< Name of the dimensional axis
565   QString title;        //!< Human readable name
566   QString abstract;     //!< Brief description of the dimension
567   QStringList keywords; //!< List of words/phrases to describe the dataset
568   QString UOM;          //!< Units of measure of dimensional axis
569   QString unitSymbol;   //!< Symbol of the units
570   QString defaultValue; //!< Default value to be used if value is not specified in request
571   bool current;         //!< Indicates whether temporal data are normally kept current
572   QStringList values;   //!< Available values for this dimension
573 };
574 
575 struct QgsWmtsTileLayer
576 {
577   enum QgsTileMode tileMode;
578   QString identifier;
579   QString title, abstract;
580   QStringList keywords;
581   QVector<QgsWmsBoundingBoxProperty> boundingBoxes;
582   QStringList formats;
583   QStringList infoFormats;
584   QString defaultStyle;
585   int dpi = -1;   //!< DPI of the tile layer (-1 for unknown DPI)
586   //! available dimensions (optional, for multi-dimensional data)
587   QHash<QString, QgsWmtsDimension> dimensions;
588   QHash<QString, QgsWmtsStyle> styles;
589   QHash<QString, QgsWmtsTileMatrixSetLink> setLinks;
590 
591   QHash<QString, QString> getTileURLs;
592   QHash<QString, QString> getFeatureInfoURLs;
593 };
594 
595 //! Capability Property structure
596 // TODO: Fill to WMS specifications
597 struct QgsWmsCapabilityProperty
598 {
599   QgsWmsRequestProperty                request;
600   QgsWmsExceptionProperty              exception;
601 
602   // Top level layer should normally be present max once
603   // <element name="Capability">
604   //    <element ref="wms:Layer" minOccurs="0"/>  - default maxOccurs=1
605   // but there are a few non conformant capabilities around (#13762)
606   QList<QgsWmsLayerProperty>           layers;
607 
608   QList<QgsWmtsTileLayer>              tileLayers;
609   QHash<QString, QgsWmtsTileMatrixSet> tileMatrixSets;
610 };
611 
612 //! Capabilities Property structure
613 // TODO: Fill to WMS specifications
614 struct QgsWmsCapabilitiesProperty
615 {
616   QgsWmsServiceProperty         service;
617   QgsWmsCapabilityProperty      capability;
618   QString                       version;
619 };
620 
621 //! Formats supported by QImageReader
622 struct QgsWmsSupportedFormat
623 {
624   QString format;
625   QString label;
626 };
627 
628 enum QgsWmsTileAttribute
629 {
630   TileReqNo = QNetworkRequest::User + 0,
631   TileIndex = QNetworkRequest::User + 1,
632   TileRect  = QNetworkRequest::User + 2,
633   TileRetry = QNetworkRequest::User + 3,
634 };
635 
636 enum QgsWmsDpiMode
637 {
638   DpiNone = 0,
639   DpiQGIS = 1,
640   DpiUMN = 2,
641   DpiGeoServer = 4,
642   DpiAll = DpiQGIS | DpiUMN | DpiGeoServer,
643 };
644 
645 
646 
647 struct QgsWmsParserSettings
648 {
649   QgsWmsParserSettings( bool ignAxis = false, bool invAxis = false )
ignoreAxisOrientationQgsWmsParserSettings650     : ignoreAxisOrientation( ignAxis )
651     , invertAxisOrientation( invAxis )
652   {}
653   bool ignoreAxisOrientation;
654   bool invertAxisOrientation;
655 };
656 
657 struct QgsWmsAuthorization
658 {
659   QgsWmsAuthorization( const QString &userName = QString(), const QString &password = QString(), const QString &referer = QString(), const QString &authcfg = QString() )
mUserNameQgsWmsAuthorization660     : mUserName( userName )
661     , mPassword( password )
662     , mReferer( referer )
663     , mAuthCfg( authcfg )
664   {}
665 
setAuthorizationQgsWmsAuthorization666   bool setAuthorization( QNetworkRequest &request ) const
667   {
668     if ( !mAuthCfg.isEmpty() )
669     {
670       return QgsApplication::authManager()->updateNetworkRequest( request, mAuthCfg );
671     }
672     else if ( !mUserName.isEmpty() || !mPassword.isEmpty() )
673     {
674       request.setRawHeader( "Authorization", "Basic " + QStringLiteral( "%1:%2" ).arg( mUserName, mPassword ).toUtf8().toBase64() );
675     }
676 
677     if ( !mReferer.isEmpty() )
678     {
679       request.setRawHeader( "Referer", mReferer.toLatin1() );
680     }
681     return true;
682   }
683   //! Sets authorization reply
setAuthorizationReplyQgsWmsAuthorization684   bool setAuthorizationReply( QNetworkReply *reply ) const
685   {
686     if ( !mAuthCfg.isEmpty() )
687     {
688       return QgsApplication::authManager()->updateNetworkReply( reply, mAuthCfg );
689     }
690     return true;
691   }
692 
693   //! Username for basic http authentication
694   QString mUserName;
695 
696   //! Password for basic http authentication
697   QString mPassword;
698 
699   //! Referer for http requests
700   QString mReferer;
701 
702   //! Authentication configuration ID
703   QString mAuthCfg;
704 };
705 
706 
707 //! URI that gets passed to provider
708 class QgsWmsSettings
709 {
710   public:
711 
712     bool parseUri( const QString &uriString );
713 
baseUrl()714     QString baseUrl() const { return mBaseUrl; }
authorization()715     QgsWmsAuthorization authorization() const { return mAuth; }
716 
parserSettings()717     QgsWmsParserSettings parserSettings() const { return mParserSettings; }
718 
719     /**
720      * Parse the given string extent into a well defined dates and resolution structures.
721      * The string extent comes from WMS-T dimension capabilities.
722      *
723      * \since QGIS 3.14
724      */
725     QgsWmstDimensionExtent parseTemporalExtent( const QString &extent );
726 
727     /**
728      * Sets the dimension extent property
729      *
730      * \see timeDimensionExtent()
731      * \since QGIS 3.14
732      */
733     void setTimeDimensionExtent( const QgsWmstDimensionExtent &timeDimensionExtent );
734 
735     /**
736      * Returns the dimension extent property.
737      *
738      * \see setTimeDimensionExtent()
739      * \since QGIS 3.14
740      */
741     QgsWmstDimensionExtent timeDimensionExtent() const;
742 
743     /**
744      * Parse the given string item into a resolution structure.
745      *
746      * \since QGIS 3.14
747      */
748     QgsTimeDuration parseWmstResolution( const QString &item );
749 
750     /**
751      * Parse the given string item into QDateTime instant.
752      *
753      * \since QGIS 3.14
754      */
755     QDateTime parseWmstDateTimes( const QString &item );
756 
757     /**
758      * Finds the least closest datetime from list of available dimension temporal ranges
759      * with the given \a dateTime.
760      *
761      * \note It works with wms-t capabilities that provide time dimension with temporal ranges only.
762      *
763      * \since QGIS 3.14
764      */
765     QDateTime findLeastClosestDateTime( const QDateTime &dateTime, bool dateOnly = false ) const;
766 
767   protected:
768     QgsWmsParserSettings    mParserSettings;
769 
770     //! layer is tiled, tile layer and active matrix set
771     bool                    mTiled;
772     //! whether we actually work with XYZ tiles instead of WMS / WMTS
773     bool mXyz;
774 
775     //! Whether we are dealing with WMS-T
776     bool mIsTemporal = false;
777 
778     //! Whether we are dealing bi-temporal dimensional WMS-T
779     bool mIsBiTemporal = false;
780 
781     //! Temporal extent from dimension property in WMS-T
782     QString mTemporalExtent;
783 
784     //! Fixed temporal range for the data provider
785     QgsDateTimeRange mFixedRange;
786 
787     //! All available temporal ranges
788     QList< QgsDateTimeRange > mAllRanges;
789 
790     QgsInterval mDefaultInterval;
791 
792     //! Fixed reference temporal range for the data provider
793     QgsDateTimeRange mFixedReferenceRange;
794 
795     //! Stores WMS-T time dimension extent dates
796     QgsWmstDimensionExtent mTimeDimensionExtent;
797 
798     //! Stores WMS-T reference dimension extent dates
799     QgsWmstDimensionExtent mReferenceTimeDimensionExtent;
800 
801     //! whether we are dealing with MBTiles file rather than using network-based tiles
802     bool mIsMBTiles = false;
803     //! chosen values for dimensions in case of multi-dimensional data (key=dim id, value=dim value)
804     QHash<QString, QString>  mTileDimensionValues;
805     //! name of the chosen tile matrix set
806     QString                 mTileMatrixSetId;
807 
808     /**
809      * Maximum width and height of getmap requests
810      */
811     int mMaxWidth;
812     int mMaxHeight;
813 
814     /**
815      * Step size when iterating the layer
816      */
817     int mStepWidth = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_WIDTH;
818     int mStepHeight = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_HEIGHT;
819 
820     //! Data source URI of the WMS for this layer
821     QString mHttpUri;
822 
823     //! URL part of URI (httpuri)
824     QString mBaseUrl;
825 
826     QgsWmsAuthorization mAuth;
827 
828     bool mIgnoreGetMapUrl;
829     bool mIgnoreGetFeatureInfoUrl;
830     bool mIgnoreReportedLayerExtents = false;
831     bool mSmoothPixmapTransform;
832     enum QgsWmsDpiMode mDpiMode;
833 
834     /**
835      * Active sublayers managed by this provider in a draw function, in order from bottom to top
836      * (some may not be visible in a draw function, cf. activeSubLayerVisibility)
837      */
838     QStringList mActiveSubLayers;
839     QStringList mActiveSubStyles;
840 
841     //! Opacities for wms layers. Same ordering as mActiveSubLayers/mActiveSubStyles
842     QStringList mOpacities;
843 
844     /**
845      * Visibility status of the given active sublayer
846      */
847     QMap<QString, bool> mActiveSubLayerVisibility;
848 
849     //! FEATURE_COUNT for GetFeatureInfo
850     int mFeatureCount;
851 
852     /**
853      * MIME type of the image encoding used from the WMS server
854      */
855     QString mImageMimeType;
856 
857     QString mCrsId;
858 
859     bool mEnableContextualLegend;
860 
861     friend class QgsWmsProvider;
862 };
863 
864 
865 //! Keeps information about capabilities of particular URI
866 class QgsWmsCapabilities
867 {
868   public:
869 
870     /**
871      * Constructs a QgsWmsCapabilities object with the given \a coordinateTransformContext
872      */
873     QgsWmsCapabilities( const QgsCoordinateTransformContext &coordinateTransformContext = QgsCoordinateTransformContext(), const QString &baseUrl = QString() );
874 
isValid()875     bool isValid() const { return mValid; }
876 
877     bool parseResponse( const QByteArray &response, QgsWmsParserSettings settings );
878 
lastError()879     QString lastError() const { return mError; }
lastErrorFormat()880     QString lastErrorFormat() const { return mErrorFormat; }
881 
capabilitiesProperty()882     QgsWmsCapabilitiesProperty capabilitiesProperty() { return mCapabilities; }
883 
884     /**
885      * \brief   Returns a list of the supported layers of the WMS server
886      *
887      * \returns The list of layers will be placed here.
888      *
889      * \todo Document this better
890      */
supportedLayers()891     QVector<QgsWmsLayerProperty> supportedLayers() const { return mLayersSupported; }
892 
893     //! Gets raster image encodings supported by the WMS, expressed as MIME types
supportedImageEncodings()894     QStringList supportedImageEncodings() const { return mCapabilities.capability.request.getMap.format; }
895 
896     /**
897      * \brief   Returns a map for the hierarchy of layers
898      */
layerParents(QMap<int,int> & parents,QMap<int,QStringList> & parentNames)899     void layerParents( QMap<int, int> &parents, QMap<int, QStringList> &parentNames ) const { parents = mLayerParents; parentNames = mLayerParentNames; }
900 
901     /**
902      * \brief   Returns a list of the supported tile layers of the WMS server
903      *
904      * \returns The list of tile sets will be placed here.
905      */
supportedTileLayers()906     QList<QgsWmtsTileLayer> supportedTileLayers() const { return mTileLayersSupported; }
907 
908     /**
909      * \brief   Returns a list of the available tile matrix sets
910      */
supportedTileMatrixSets()911     QHash<QString, QgsWmtsTileMatrixSet> supportedTileMatrixSets() const { return mTileMatrixSets; }
912 
913     //! Find out whether to invert axis orientation when parsing/writing coordinates
914     bool shouldInvertAxisOrientation( const QString &ogcCrs );
915 
916     //! Find out identify capabilities
917     int identifyCapabilities() const;
918 
919   protected:
920     bool parseCapabilitiesDom( const QByteArray &xml, QgsWmsCapabilitiesProperty &capabilitiesProperty );
921 
922     void parseService( const QDomElement &element, QgsWmsServiceProperty &serviceProperty );
923     void parseOnlineResource( const QDomElement &element, QgsWmsOnlineResourceAttribute &onlineResourceAttribute );
924     void parseKeywordList( const QDomElement &element, QStringList &keywordListProperty );
925     void parseContactInformation( const QDomElement &element, QgsWmsContactInformationProperty &contactInformationProperty );
926     void parseContactPersonPrimary( const QDomElement &element, QgsWmsContactPersonPrimaryProperty &contactPersonPrimaryProperty );
927     void parseContactAddress( const QDomElement &element, QgsWmsContactAddressProperty &contactAddressProperty );
928 
929     void parseCapability( const QDomElement &element, QgsWmsCapabilityProperty &capabilityProperty );
930     void parseRequest( const QDomElement &element, QgsWmsRequestProperty &requestProperty );
931     void parseDimension( const QDomElement &element, QgsWmsDimensionProperty &dimensionProperty );
932     void parseExtent( const QDomElement &element, QVector<QgsWmsDimensionProperty> &dimensionProperties );
933     void parseLegendUrl( const QDomElement &element, QgsWmsLegendUrlProperty &legendUrlProperty );
934     void parseMetadataUrl( const QDomElement &element, QgsWmsMetadataUrlProperty &metadataUrlProperty );
935     void parseLayer( const QDomElement &element, QgsWmsLayerProperty &layerProperty, QgsWmsLayerProperty *parentProperty = nullptr );
936     void parseStyle( const QDomElement &element, QgsWmsStyleProperty &styleProperty );
937 
938     void parseOperationType( const QDomElement &element, QgsWmsOperationType &operationType );
939     void parseDcpType( const QDomElement &element, QgsWmsDcpTypeProperty &dcpType );
940     void parseHttp( const QDomElement &element, QgsWmsHttpProperty &httpProperty );
941     void parseGet( const QDomElement &element, QgsWmsGetProperty &getProperty );
942     void parsePost( const QDomElement &element, QgsWmsPostProperty &postProperty );
943 
944     void parseTileSetProfile( const QDomElement &element );
945     void parseWMTSContents( const QDomElement &element );
946     void parseKeywords( const QDomNode &e, QStringList &keywords );
947     void parseTheme( const QDomElement &e, QgsWmtsTheme &t );
948 
949     QString nodeAttribute( const QDomElement &element, const QString &name, const QString &defValue = QString() );
950 
951     /**
952      * In case no bounding box is present in WMTS capabilities, try to estimate it from tile matrix sets.
953      * Returns true if the detection went fine.
954      */
955     bool detectTileLayerBoundingBox( QgsWmtsTileLayer &l );
956 
957   protected:
958     bool mValid = false;
959 
960     QString mError;
961     QString mErrorCaption;
962     QString mErrorFormat;
963 
964     QgsWmsParserSettings mParserSettings;
965 
966     //! number of layers and parents
967     int mLayerCount = -1;
968     QMap<int, int> mLayerParents;
969     QMap<int, QStringList> mLayerParentNames;
970 
971     /**
972      * WMS "queryable" per layer
973      * Used in determining if the Identify map tool can be useful on the rendered WMS map layer.
974      */
975     QMap<QString, bool> mQueryableForLayer;
976 
977     /**
978      * layers hosted by the WMS
979      */
980     QVector<QgsWmsLayerProperty> mLayersSupported;
981 
982     /**
983      * tilesets hosted by the WMTS
984      */
985     QList<QgsWmtsTileLayer> mTileLayersSupported;
986 
987     /**
988      * themes hosted by the WMTS
989      */
990     QList<QgsWmtsTheme> mTileThemes;
991 
992     /**
993      * Parsed capabilities of the WMS
994      */
995     QgsWmsCapabilitiesProperty mCapabilities;
996 
997     //! Formats supported by server and provider
998     QMap<QgsRaster::IdentifyFormat, QString> mIdentifyFormats;
999 
1000 
1001     /**
1002      * tile matrix sets hosted by the WMS
1003      */
1004     QHash<QString, QgsWmtsTileMatrixSet> mTileMatrixSets;
1005 
1006     //temporarily caches invert axis setting for each crs
1007     QHash<QString, bool> mCrsInvertAxis;
1008 
1009   private:
1010 
1011     QgsCoordinateTransformContext mCoordinateTransformContext;
1012     QString mBaseUrl;
1013 
1014     friend class QgsWmsProvider;
1015     friend class TestQgsWmsCapabilities;
1016 };
1017 
1018 
1019 
1020 /**
1021  * Class that handles download of capabilities.
1022  */
1023 class QgsWmsCapabilitiesDownload : public QObject
1024 {
1025     Q_OBJECT
1026 
1027   public:
1028     explicit QgsWmsCapabilitiesDownload( bool forceRefresh, QObject *parent = nullptr );
1029 
1030     QgsWmsCapabilitiesDownload( const QString &baseUrl, const QgsWmsAuthorization &auth, bool forceRefresh, QObject *parent = nullptr );
1031 
1032     ~QgsWmsCapabilitiesDownload() override;
1033 
1034     bool downloadCapabilities();
1035 
1036     bool downloadCapabilities( const QString &baseUrl, const QgsWmsAuthorization &auth );
1037 
1038     /**
1039      * Returns the download refresh state.
1040      * \see setForceRefresh()
1041      *
1042      * \since QGIS 3.22
1043      */
1044     bool forceRefresh();
1045 
1046     /**
1047      * Sets the download refresh state.
1048      * \see forceRefresh()
1049      *
1050      * \since QGIS 3.22
1051      */
1052     void setForceRefresh( bool forceRefresh );
1053 
lastError()1054     QString lastError() const { return mError; }
1055 
response()1056     QByteArray response() const { return mHttpCapabilitiesResponse; }
1057 
1058     //! Abort network request immediately
1059     void abort();
1060 
1061   signals:
1062     //! \brief emit a signal to be caught by qgisapp and display a msg on status bar
1063     void statusChanged( QString const   &statusQString );
1064 
1065     //! \brief emit a signal once the download is finished
1066     void downloadFinished();
1067 
1068   protected slots:
1069     void capabilitiesReplyFinished();
1070     void capabilitiesReplyProgress( qint64, qint64 );
1071 
1072   protected:
1073     //! URL part of URI (httpuri)
1074     QString mBaseUrl;
1075 
1076     QgsWmsAuthorization mAuth;
1077 
1078     //! The reply to the capabilities request
1079     QNetworkReply *mCapabilitiesReply = nullptr;
1080 
1081     //! The error message associated with the last WMS error.
1082     QString mError;
1083 
1084     //! The mime type of the message
1085     QString mErrorFormat;
1086 
1087     //! Capabilities of the WMS (raw)
1088     QByteArray mHttpCapabilitiesResponse;
1089 
1090     bool mIsAborted;
1091     bool mForceRefresh;
1092 };
1093 
1094 
1095 
1096 #endif // QGSWMSCAPABILITIES_H
1097