QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
qgswfsutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgswfssutils.cpp
3 -------------------------
4 begin : December 20 , 2016
5 copyright : (C) 2007 by Marco Hugentobler ( parts from qgswmshandler)
6 (C) 2012 by René-Luc D'Hont ( parts from qgswmshandler)
7 (C) 2014 by Alessandro Pasotti ( parts from qgswmshandler)
8 (C) 2017 by David Marteau
9 email : marco dot hugentobler at karto dot baug dot ethz dot ch
10 a dot pasotti at itopen dot it
11 david dot marteau at 3liz dot com
12 ***************************************************************************/
13
14/***************************************************************************
15 * *
16 * This program is free software; you can redistribute it and/or modify *
17 * it under the terms of the GNU General Public License as published by *
18 * the Free Software Foundation; either version 2 of the License, or *
19 * (at your option) any later version. *
20 * *
21 ***************************************************************************/
22
23#include "qgswfsutils.h"
24
25#include "qgsogcutils.h"
26#include "qgsproject.h"
28#include "qgsvectorlayer.h"
29
30#include <QString>
31
32using namespace Qt::StringLiterals;
33
34namespace QgsWfs
35{
37 {
38 return u"1.1.0"_s;
39 }
40
41 QString serviceUrl( const QgsServerRequest &request, const QgsProject *project, const QgsServerSettings &settings )
42 {
43 QUrl href;
44 href.setUrl( QgsServerProjectUtils::wfsServiceUrl( project ? *project : *QgsProject::instance(), request, settings ) );
45
46 // Build default url
47 if ( href.isEmpty() )
48 {
49 static QSet<QString> sFilter {
50 u"REQUEST"_s,
51 u"VERSION"_s,
52 u"SERVICE"_s,
53 };
54
55 href = request.originalUrl();
56 QUrlQuery q( href );
57
58 const auto constQueryItems = q.queryItems();
59 for ( const auto &param : constQueryItems )
60 {
61 if ( sFilter.contains( param.first.toUpper() ) )
62 q.removeAllQueryItems( param.first );
63 }
64
65 href.setQuery( q );
66 }
67
68 return href.toString();
69 }
70
71 QgsVectorLayer *layerByTypeName( const QgsProject *project, const QString &typeName )
72 {
73 QStringList layerIds = QgsServerProjectUtils::wfsLayerIds( *project );
74 for ( const QString &layerId : std::as_const( layerIds ) )
75 {
76 QgsMapLayer *layer = project->mapLayer( layerId );
77 if ( !layer )
78 {
79 continue;
80 }
81 if ( layer->type() != Qgis::LayerType::Vector )
82 {
83 continue;
84 }
85
86 if ( layer->serverProperties()->wfsTypeName() == typeName )
87 {
88 return qobject_cast<QgsVectorLayer *>( layer );
89 }
90 }
91 return nullptr;
92 }
93
94 QgsFeatureRequest parseFilterElement( const QString &typeName, QDomElement &filterElem, QgsProject *project )
95 {
96 // Get the server feature ids in filter element
97 QStringList collectedServerFids;
98 return parseFilterElement( typeName, filterElem, collectedServerFids, project );
99 }
100
101 QgsFeatureRequest parseFilterElement( const QString &typeName, QDomElement &filterElem, QStringList &serverFids, const QgsProject *project, const QgsMapLayer *layer )
102 {
103 QgsFeatureRequest request;
104
105 QDomNodeList fidNodes = filterElem.elementsByTagName( u"FeatureId"_s );
106 QDomNodeList goidNodes = filterElem.elementsByTagName( u"GmlObjectId"_s );
107 if ( !fidNodes.isEmpty() )
108 {
109 // Get the server feature ids in filter element
110 QStringList collectedServerFids;
111 QDomElement fidElem;
112 for ( int f = 0; f < fidNodes.size(); f++ )
113 {
114 fidElem = fidNodes.at( f ).toElement();
115 if ( !fidElem.hasAttribute( u"fid"_s ) )
116 {
117 throw QgsRequestNotWellFormedException( "FeatureId element without fid attribute" );
118 }
119
120 QString serverFid = fidElem.attribute( u"fid"_s );
121 if ( serverFid.contains( "."_L1 ) )
122 {
123 if ( serverFid.section( u"."_s, 0, 0 ) != typeName )
124 continue;
125 serverFid = serverFid.section( u"."_s, 1, 1 );
126 }
127 collectedServerFids << serverFid;
128 }
129 // No server feature ids found
130 if ( collectedServerFids.isEmpty() )
131 {
132 throw QgsRequestNotWellFormedException( u"No FeatureId element correctly parse against typeName '%1'"_s.arg( typeName ) );
133 }
134 // update server feature ids
135 serverFids.append( collectedServerFids );
137 return request;
138 }
139 else if ( !goidNodes.isEmpty() )
140 {
141 // Get the server feature ids in filter element
142 QStringList collectedServerFids;
143 QDomElement goidElem;
144 for ( int f = 0; f < goidNodes.size(); f++ )
145 {
146 goidElem = goidNodes.at( f ).toElement();
147 if ( !goidElem.hasAttribute( u"id"_s ) && !goidElem.hasAttribute( u"gml:id"_s ) )
148 {
149 throw QgsRequestNotWellFormedException( "GmlObjectId element without gml:id attribute" );
150 }
151
152 QString serverFid = goidElem.attribute( u"id"_s );
153 if ( serverFid.isEmpty() )
154 serverFid = goidElem.attribute( u"gml:id"_s );
155 if ( serverFid.contains( "."_L1 ) )
156 {
157 if ( serverFid.section( u"."_s, 0, 0 ) != typeName )
158 continue;
159 serverFid = serverFid.section( u"."_s, 1, 1 );
160 }
161 collectedServerFids << serverFid;
162 }
163 // No server feature ids found
164 if ( collectedServerFids.isEmpty() )
165 {
166 throw QgsRequestNotWellFormedException( u"No GmlObjectId element correctly parse against typeName '%1'"_s.arg( typeName ) );
167 }
168 // update server feature ids
169 serverFids.append( collectedServerFids );
171 return request;
172 }
173 else if ( filterElem.firstChildElement().tagName() == "BBOX"_L1 )
174 {
175 QDomElement bboxElem = filterElem.firstChildElement();
176 QDomElement childElem = bboxElem.firstChildElement();
177
178 while ( !childElem.isNull() )
179 {
180 if ( childElem.tagName() == "Box"_L1 )
181 {
182 request.setFilterRect( QgsOgcUtils::rectangleFromGMLBox( childElem ) );
183 }
184 else if ( childElem.tagName() != "PropertyName"_L1 )
185 {
186 QgsOgcUtils::Context ctx { layer, project ? project->transformContext() : QgsCoordinateTransformContext() };
187 QgsGeometry geom = QgsOgcUtils::geometryFromGML( childElem, ctx );
188 request.setFilterRect( geom.boundingBox() );
189 }
190 childElem = childElem.nextSiblingElement();
191 }
192
194 return request;
195 }
196 // Apply BBOX through filterRect even inside an And to use spatial index
197 else if ( filterElem.firstChildElement().tagName() == "And"_L1 && !filterElem.firstChildElement().firstChildElement( "BBOX"_L1 ).isNull() )
198 {
199 int nbChildElem = filterElem.firstChildElement().childNodes().size();
200
201 // Create a filter element to parse And child not BBOX
202 QDomElement childFilterElement = filterElem.ownerDocument().createElement( "Filter"_L1 );
203 if ( nbChildElem > 2 )
204 {
205 QDomElement childAndElement = filterElem.ownerDocument().createElement( "And"_L1 );
206 childFilterElement.appendChild( childAndElement );
207 }
208
209 // Create a filter element to parse BBOX
210 QDomElement bboxFilterElement = filterElem.ownerDocument().createElement( "Filter"_L1 );
211
212 QDomElement childElem = filterElem.firstChildElement().firstChildElement();
213 while ( !childElem.isNull() )
214 {
215 // Update request based on BBOX
216 if ( childElem.tagName() == "BBOX"_L1 )
217 {
218 // Clone BBOX
219 bboxFilterElement.appendChild( childElem.cloneNode( true ) );
220 }
221 else
222 {
223 // Clone And child
224 if ( nbChildElem > 2 )
225 {
226 childFilterElement.firstChildElement().appendChild( childElem.cloneNode( true ) );
227 }
228 else
229 {
230 childFilterElement.appendChild( childElem.cloneNode( true ) );
231 }
232 }
233 childElem = childElem.nextSiblingElement();
234 }
235
236 // Parse the filter element with the cloned BBOX
237 QStringList collectedServerFids;
238 QgsFeatureRequest bboxRequest = parseFilterElement( typeName, bboxFilterElement, collectedServerFids, project );
239
240 // Update request based on BBOX
241 if ( request.filterRect().isEmpty() )
242 {
243 request.setFilterRect( bboxRequest.filterRect() );
244 }
245 else
246 {
247 request.setFilterRect( request.filterRect().intersect( bboxRequest.filterRect() ) );
248 }
249
250 // Parse the filter element with the cloned And child
251 QgsFeatureRequest childRequest = parseFilterElement( typeName, childFilterElement, collectedServerFids, project );
252
253 // Update server feature ids
254 if ( !collectedServerFids.isEmpty() )
255 {
256 serverFids.append( collectedServerFids );
257 }
258
259 // Update expression
260 request.setFilterExpression( childRequest.filterExpression()->expression() );
261
263 return request;
264 }
265 else
266 {
267 QgsVectorLayer *layer = nullptr;
268 if ( project )
269 {
270 layer = layerByTypeName( project, typeName );
271 }
272 std::shared_ptr<QgsExpression> filter( QgsOgcUtils::expressionFromOgcFilter( filterElem, layer ) );
273 if ( filter )
274 {
275 if ( filter->hasParserError() || !filter->parserErrorString().isEmpty() )
276 {
277 throw QgsRequestNotWellFormedException( filter->parserErrorString() );
278 }
279
280 if ( filter->needsGeometry() )
281 {
283 }
284 request.setFilterExpression( filter->expression() );
285 return request;
286 }
287 }
288 return request;
289 }
290
291} // namespace QgsWfs
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
Definition qgis.h:2256
@ NoFlags
No flags are set.
Definition qgis.h:2253
@ Vector
Vector layer.
Definition qgis.h:194
Contains information about the context in which a coordinate transform is executed.
QString expression() const
Returns the original, unmodified expression string.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsRectangle filterRect() const
Returns the rectangle from which features will be taken.
QgsExpression * filterExpression() const
Returns the filter expression (if set).
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
A geometry is the spatial representation of a feature.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
QString wfsTypeName() const
Returns WFS typename for the layer.
Base class for all map layer types.
Definition qgsmaplayer.h:83
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
Qgis::LayerType type
Definition qgsmaplayer.h:93
static QgsRectangle rectangleFromGMLBox(const QDomNode &boxNode)
Read rectangle from GML2 Box.
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
static QgsExpression * expressionFromOgcFilter(const QDomElement &element, QgsVectorLayer *layer=nullptr)
Parse XML with OGC filter into QGIS expression.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:112
static QgsProject * instance()
Returns the QgsProject singleton instance.
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QgsCoordinateTransformContext transformContext
Definition qgsproject.h:119
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
static QStringList wfsLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published in WFS.
static QString wfsServiceUrl(const QgsProject &project, const QgsServerRequest &request=QgsServerRequest(), const QgsServerSettings &settings=QgsServerSettings())
Returns the WFS service url.
Defines requests passed to QgsService classes.
QUrl originalUrl() const
Returns the request url as seen by the web server, by default this is equal to the url seen by QGIS s...
Provides a way to retrieve settings by prioritizing according to environment variables,...
Represents a vector layer which manages a vector based dataset.
Exception thrown in case of malformed request.
WMS implementation.
Definition qgswfs.cpp:40
QString implementationVersion()
Returns the highest version supported by this implementation.
QgsVectorLayer * layerByTypeName(const QgsProject *project, const QString &typeName)
Retrieve a layer by typename.
QString serviceUrl(const QgsServerRequest &request, const QgsProject *project, const QgsServerSettings &settings)
Service URL string.
QgsFeatureRequest parseFilterElement(const QString &typeName, QDomElement &filterElem, QgsProject *project)
Transform a Filter element to a feature request.
The Context struct stores the current layer and coordinate transform context.
Definition qgsogcutils.h:64