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