QGIS API Documentation 3.99.0-Master (2fe06baccd8)
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
30namespace QgsWfs
31{
33 {
34 return QStringLiteral( "1.1.0" );
35 }
36
37 QString serviceUrl( const QgsServerRequest &request, const QgsProject *project, const QgsServerSettings &settings )
38 {
39 QUrl href;
40 href.setUrl( QgsServerProjectUtils::wfsServiceUrl( project ? *project : *QgsProject::instance(), request, settings ) );
41
42 // Build default url
43 if ( href.isEmpty() )
44 {
45 static QSet<QString> sFilter {
46 QStringLiteral( "REQUEST" ),
47 QStringLiteral( "VERSION" ),
48 QStringLiteral( "SERVICE" ),
49 };
50
51 href = request.originalUrl();
52 QUrlQuery q( href );
53
54 const auto constQueryItems = q.queryItems();
55 for ( const auto &param : constQueryItems )
56 {
57 if ( sFilter.contains( param.first.toUpper() ) )
58 q.removeAllQueryItems( param.first );
59 }
60
61 href.setQuery( q );
62 }
63
64 return href.toString();
65 }
66
67 QgsVectorLayer *layerByTypeName( const QgsProject *project, const QString &typeName )
68 {
69 QStringList layerIds = QgsServerProjectUtils::wfsLayerIds( *project );
70 for ( const QString &layerId : std::as_const( layerIds ) )
71 {
72 QgsMapLayer *layer = project->mapLayer( layerId );
73 if ( !layer )
74 {
75 continue;
76 }
77 if ( layer->type() != Qgis::LayerType::Vector )
78 {
79 continue;
80 }
81
82 if ( layer->serverProperties()->wfsTypeName() == typeName )
83 {
84 return qobject_cast<QgsVectorLayer *>( layer );
85 }
86 }
87 return nullptr;
88 }
89
90 QgsFeatureRequest parseFilterElement( const QString &typeName, QDomElement &filterElem, QgsProject *project )
91 {
92 // Get the server feature ids in filter element
93 QStringList collectedServerFids;
94 return parseFilterElement( typeName, filterElem, collectedServerFids, project );
95 }
96
97 QgsFeatureRequest parseFilterElement( const QString &typeName, QDomElement &filterElem, QStringList &serverFids, const QgsProject *project, const QgsMapLayer *layer )
98 {
99 QgsFeatureRequest request;
100
101 QDomNodeList fidNodes = filterElem.elementsByTagName( QStringLiteral( "FeatureId" ) );
102 QDomNodeList goidNodes = filterElem.elementsByTagName( QStringLiteral( "GmlObjectId" ) );
103 if ( !fidNodes.isEmpty() )
104 {
105 // Get the server feature ids in filter element
106 QStringList collectedServerFids;
107 QDomElement fidElem;
108 for ( int f = 0; f < fidNodes.size(); f++ )
109 {
110 fidElem = fidNodes.at( f ).toElement();
111 if ( !fidElem.hasAttribute( QStringLiteral( "fid" ) ) )
112 {
113 throw QgsRequestNotWellFormedException( "FeatureId element without fid attribute" );
114 }
115
116 QString serverFid = fidElem.attribute( QStringLiteral( "fid" ) );
117 if ( serverFid.contains( QLatin1String( "." ) ) )
118 {
119 if ( serverFid.section( QStringLiteral( "." ), 0, 0 ) != typeName )
120 continue;
121 serverFid = serverFid.section( QStringLiteral( "." ), 1, 1 );
122 }
123 collectedServerFids << serverFid;
124 }
125 // No server feature ids found
126 if ( collectedServerFids.isEmpty() )
127 {
128 throw QgsRequestNotWellFormedException( QStringLiteral( "No FeatureId element correctly parse against typeName '%1'" ).arg( typeName ) );
129 }
130 // update server feature ids
131 serverFids.append( collectedServerFids );
133 return request;
134 }
135 else if ( !goidNodes.isEmpty() )
136 {
137 // Get the server feature ids in filter element
138 QStringList collectedServerFids;
139 QDomElement goidElem;
140 for ( int f = 0; f < goidNodes.size(); f++ )
141 {
142 goidElem = goidNodes.at( f ).toElement();
143 if ( !goidElem.hasAttribute( QStringLiteral( "id" ) ) && !goidElem.hasAttribute( QStringLiteral( "gml:id" ) ) )
144 {
145 throw QgsRequestNotWellFormedException( "GmlObjectId element without gml:id attribute" );
146 }
147
148 QString serverFid = goidElem.attribute( QStringLiteral( "id" ) );
149 if ( serverFid.isEmpty() )
150 serverFid = goidElem.attribute( QStringLiteral( "gml:id" ) );
151 if ( serverFid.contains( QLatin1String( "." ) ) )
152 {
153 if ( serverFid.section( QStringLiteral( "." ), 0, 0 ) != typeName )
154 continue;
155 serverFid = serverFid.section( QStringLiteral( "." ), 1, 1 );
156 }
157 collectedServerFids << serverFid;
158 }
159 // No server feature ids found
160 if ( collectedServerFids.isEmpty() )
161 {
162 throw QgsRequestNotWellFormedException( QStringLiteral( "No GmlObjectId element correctly parse against typeName '%1'" ).arg( typeName ) );
163 }
164 // update server feature ids
165 serverFids.append( collectedServerFids );
167 return request;
168 }
169 else if ( filterElem.firstChildElement().tagName() == QLatin1String( "BBOX" ) )
170 {
171 QDomElement bboxElem = filterElem.firstChildElement();
172 QDomElement childElem = bboxElem.firstChildElement();
173
174 while ( !childElem.isNull() )
175 {
176 if ( childElem.tagName() == QLatin1String( "Box" ) )
177 {
178 request.setFilterRect( QgsOgcUtils::rectangleFromGMLBox( childElem ) );
179 }
180 else if ( childElem.tagName() != QLatin1String( "PropertyName" ) )
181 {
182 QgsOgcUtils::Context ctx { layer, project ? project->transformContext() : QgsCoordinateTransformContext() };
183 QgsGeometry geom = QgsOgcUtils::geometryFromGML( childElem, ctx );
184 request.setFilterRect( geom.boundingBox() );
185 }
186 childElem = childElem.nextSiblingElement();
187 }
188
190 return request;
191 }
192 // Apply BBOX through filterRect even inside an And to use spatial index
193 else if ( filterElem.firstChildElement().tagName() == QLatin1String( "And" ) && !filterElem.firstChildElement().firstChildElement( QLatin1String( "BBOX" ) ).isNull() )
194 {
195 int nbChildElem = filterElem.firstChildElement().childNodes().size();
196
197 // Create a filter element to parse And child not BBOX
198 QDomElement childFilterElement = filterElem.ownerDocument().createElement( QLatin1String( "Filter" ) );
199 if ( nbChildElem > 2 )
200 {
201 QDomElement childAndElement = filterElem.ownerDocument().createElement( QLatin1String( "And" ) );
202 childFilterElement.appendChild( childAndElement );
203 }
204
205 // Create a filter element to parse BBOX
206 QDomElement bboxFilterElement = filterElem.ownerDocument().createElement( QLatin1String( "Filter" ) );
207
208 QDomElement childElem = filterElem.firstChildElement().firstChildElement();
209 while ( !childElem.isNull() )
210 {
211 // Update request based on BBOX
212 if ( childElem.tagName() == QLatin1String( "BBOX" ) )
213 {
214 // Clone BBOX
215 bboxFilterElement.appendChild( childElem.cloneNode( true ) );
216 }
217 else
218 {
219 // Clone And child
220 if ( nbChildElem > 2 )
221 {
222 childFilterElement.firstChildElement().appendChild( childElem.cloneNode( true ) );
223 }
224 else
225 {
226 childFilterElement.appendChild( childElem.cloneNode( true ) );
227 }
228 }
229 childElem = childElem.nextSiblingElement();
230 }
231
232 // Parse the filter element with the cloned BBOX
233 QStringList collectedServerFids;
234 QgsFeatureRequest bboxRequest = parseFilterElement( typeName, bboxFilterElement, collectedServerFids, project );
235
236 // Update request based on BBOX
237 if ( request.filterRect().isEmpty() )
238 {
239 request.setFilterRect( bboxRequest.filterRect() );
240 }
241 else
242 {
243 request.setFilterRect( request.filterRect().intersect( bboxRequest.filterRect() ) );
244 }
245
246 // Parse the filter element with the cloned And child
247 QgsFeatureRequest childRequest = parseFilterElement( typeName, childFilterElement, collectedServerFids, project );
248
249 // Update server feature ids
250 if ( !collectedServerFids.isEmpty() )
251 {
252 serverFids.append( collectedServerFids );
253 }
254
255 // Update expression
256 request.setFilterExpression( childRequest.filterExpression()->expression() );
257
259 return request;
260 }
261 else
262 {
263 QgsVectorLayer *layer = nullptr;
264 if ( project )
265 {
266 layer = layerByTypeName( project, typeName );
267 }
268 std::shared_ptr<QgsExpression> filter( QgsOgcUtils::expressionFromOgcFilter( filterElem, layer ) );
269 if ( filter )
270 {
271 if ( filter->hasParserError() || !filter->parserErrorString().isEmpty() )
272 {
273 throw QgsRequestNotWellFormedException( filter->parserErrorString() );
274 }
275
276 if ( filter->needsGeometry() )
277 {
279 }
280 request.setFilterExpression( filter->expression() );
281 return request;
282 }
283 }
284 return request;
285 }
286
287} // namespace QgsWfs
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
Definition qgis.h:2198
@ NoFlags
No flags are set.
Definition qgis.h:2195
@ Vector
Vector layer.
Definition qgis.h:191
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:80
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
Qgis::LayerType type
Definition qgsmaplayer.h:90
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:109
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:116
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:36
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