1 /***************************************************************************
2     qgswfsshareddata.h
3     ---------------------
4     begin                : March 2016
5     copyright            : (C) 2016 by Even Rouault
6     email                : even.rouault at spatialys.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 QGSWFSSHAREDDATA_H
16 #define QGSWFSSHAREDDATA_H
17 
18 #include "qgswfsfeatureiterator.h"
19 #include "qgswfsrequest.h"
20 #include "qgswfscapabilities.h"
21 #include "qgsogcutils.h"
22 
23 #include "qgsbackgroundcachedshareddata.h"
24 #include "qgsbackgroundcachedfeatureiterator.h"
25 
26 //! Class shared between provider and feature source
27 class QgsWFSSharedData : public QObject, public QgsBackgroundCachedSharedData
28 {
29     Q_OBJECT
30   public:
31     explicit QgsWFSSharedData( const QString &uri );
32     ~QgsWFSSharedData() override;
33 
34     //! Compute WFS filter from the sql or filter in the URI
35     bool computeFilter( QString &errorMsg );
36 
37     //! Returns srsName
38     QString srsName() const;
39 
40     //! Return provider geometry attribute name
geometryAttribute()41     const QString &geometryAttribute() const { return mGeometryAttribute; }
42 
43     std::unique_ptr<QgsFeatureDownloaderImpl> newFeatureDownloaderImpl( QgsFeatureDownloader *, bool requestFromMainThread ) override;
44 
45     bool isRestrictedToRequestBBOX() const override;
46 
hasGeometry()47     bool hasGeometry() const override { return !mGeometryAttribute.isEmpty(); }
48 
49     //! Set a new filter and return the previous one. Only used to temporarily disable filtering when trying to get layer geometry type.
setWFSFilter(const QString & newFilter)50     QString setWFSFilter( const QString &newFilter ) { QString oldFilter = mWFSFilter; mWFSFilter = newFilter; return oldFilter; }
51 
52   signals:
53 
54     //! Raise error
55     void raiseError( const QString &errorMsg ) const;
56 
57     //! Extent has been updated
58     void extentUpdated();
59 
60   protected:
61     friend class QgsWFSFeatureDownloaderImpl;
62     friend class QgsWFSProvider;
63     friend class QgsWFSSingleFeatureRequest;
64 
65     //! Datasource URI
66     QgsWFSDataSourceURI mURI;
67 
68     //! WFS version to use. Comes from GetCapabilities response
69     QString mWFSVersion;
70 
71     //! Name of geometry attribute
72     QString mGeometryAttribute;
73 
74     //! Layer properties
75     QList< QgsOgcUtils::LayerProperties > mLayerPropertiesList;
76 
77     //! Map a field name to the pair (typename, fieldname) that describes its source field
78     QMap< QString, QPair<QString, QString> > mMapFieldNameToSrcLayerNameFieldName;
79 
80     //! Page size for WFS 2.0. 0 = disabled
81     int mPageSize = 0;
82 
83     //! Server capabilities
84     QgsWfsCapabilities::Capabilities mCaps;
85 
86     //! If we have already issued a warning about missing feature ids
87     bool mHasWarnedAboutMissingFeatureId = false;
88 
89     /**
90      * If the server (typically MapServer WFS 1.1) honours EPSG axis order, but returns
91      * EPSG:XXXX srsName and not EPSG urns
92     */
93     bool mGetFeatureEPSGDotHonoursEPSGOrder = false;
94 
95     /**
96      * If the server (typically ESRI with WFS-T 1.1 in 2020) does not like "pos" and "posList", and requires "coordinates" for WFS 1.1 transactions
97      */
98     bool mServerPrefersCoordinatesForTransactions_1_1 = false;
99 
100     //! Geometry type of the features in this layer
101     QgsWkbTypes::Type mWKBType = QgsWkbTypes::Unknown;
102 
103     //! Create GML parser
104     QgsGmlStreamingParser *createParser() const;
105 
106     //! Returns true if it is likely that the server doesn't properly honor axis order.
107     bool detectPotentialServerAxisOrderIssueFromSingleFeatureExtent() const override;
108 
109   private:
110 
111     //! WFS filter
112     QString mWFSFilter;
113 
114     //! WFS SORTBY
115     QString mSortBy;
116 
117     //! Log error to QgsMessageLog and raise it to the provider
118     void pushError( const QString &errorMsg ) const override;
119 
emitExtentUpdated()120     void emitExtentUpdated() override { emit extentUpdated(); }
121 
122     void invalidateCacheBaseUnderLock() override;
123 
supportsLimitedFeatureCountDownloads()124     bool supportsLimitedFeatureCountDownloads() const override { return  !( mWFSVersion.startsWith( QLatin1String( "1.0" ) ) ); }
125 
layerName()126     QString layerName() const override { return mURI.typeName(); }
127 
hasServerSideFilter()128     bool hasServerSideFilter() const override { return !mWFSFilter.isEmpty(); }
129 
supportsFastFeatureCount()130     bool supportsFastFeatureCount() const override { return mCaps.supportsHits; }
131 
132     QgsRectangle getExtentFromSingleFeatureRequest() const override;
133 
134     int getFeatureCountFromServer() const override;
135 };
136 
137 //! Utility class to issue a GetFeature resultType=hits request
138 class QgsWFSFeatureHitsRequest: public QgsWfsRequest
139 {
140     Q_OBJECT
141   public:
142     explicit QgsWFSFeatureHitsRequest( const QgsWFSDataSourceURI &uri );
143 
144     //! Returns the feature count, or -1 in case of error
145     int getFeatureCount( const QString &WFSVersion, const QString &filter, const QgsWfsCapabilities::Capabilities &caps );
146 
147   protected:
148     QString errorMessageWithReason( const QString &reason ) override;
149 };
150 
151 /**
152  * Utility class to issue a GetFeature requets with maxfeatures/count=1
153  * Used by QgsWFSSharedData::endOfDownload() when capabilities extent are likely wrong
154 */
155 class QgsWFSSingleFeatureRequest: public QgsWfsRequest
156 {
157     Q_OBJECT
158   public:
159     explicit QgsWFSSingleFeatureRequest( const QgsWFSSharedData *shared );
160 
161     //! Returns the feature  extent of the single feature requested
162     QgsRectangle getExtent();
163 
164   protected:
165     QString errorMessageWithReason( const QString &reason ) override;
166 
167   private:
168     const QgsWFSSharedData *mShared = nullptr;
169 };
170 
171 #endif // QGSWFSSHAREDDATA_H
172