QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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"
25 #include "qgsserverprojectutils.h"
26 #include "qgswfsparameters.h"
27 #include "qgsvectorlayer.h"
28 #include "qgsproject.h"
29 
30 namespace QgsWfs
31 {
33  {
34  return QStringLiteral( "1.1.0" );
35  }
36 
37  QString serviceUrl( const QgsServerRequest &request, const QgsProject *project )
38  {
39  QUrl href;
40  if ( project )
41  {
42  href.setUrl( QgsServerProjectUtils::wfsServiceUrl( *project ) );
43  }
44 
45  // Build default url
46  if ( href.isEmpty() )
47  {
48 
49  static QSet<QString> sFilter
50  {
51  QStringLiteral( "REQUEST" ),
52  QStringLiteral( "VERSION" ),
53  QStringLiteral( "SERVICE" ),
54  };
55 
56  href = request.originalUrl();
57  QUrlQuery q( href );
58 
59  for ( auto param : q.queryItems() )
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  QString layerTypeName( const QgsMapLayer *layer )
72  {
73  QString name = layer->name();
74  if ( !layer->shortName().isEmpty() )
75  name = layer->shortName();
76  name = name.replace( ' ', '_' );
77  return name;
78  }
79 
80  QgsVectorLayer *layerByTypeName( const QgsProject *project, const QString &typeName )
81  {
82  QStringList layerIds = QgsServerProjectUtils::wfsLayerIds( *project );
83  for ( const QString &layerId : layerIds )
84  {
85  QgsMapLayer *layer = project->mapLayer( layerId );
86  if ( !layer )
87  {
88  continue;
89  }
90  if ( layer->type() != QgsMapLayerType::VectorLayer )
91  {
92  continue;
93  }
94 
95  if ( layerTypeName( layer ) == typeName )
96  {
97  return qobject_cast<QgsVectorLayer *>( layer );
98  }
99  }
100  return nullptr;
101  }
102 
103  QgsFeatureRequest parseFilterElement( const QString &typeName, QDomElement &filterElem, const QgsProject *project )
104  {
105  QgsFeatureRequest request;
106 
107  QDomNodeList fidNodes = filterElem.elementsByTagName( QStringLiteral( "FeatureId" ) );
108  QDomNodeList goidNodes = filterElem.elementsByTagName( QStringLiteral( "GmlObjectId" ) );
109  if ( !fidNodes.isEmpty() )
110  {
111  QgsFeatureIds fids;
112  QDomElement fidElem;
113  for ( int f = 0; f < fidNodes.size(); f++ )
114  {
115  fidElem = fidNodes.at( f ).toElement();
116  if ( !fidElem.hasAttribute( QStringLiteral( "fid" ) ) )
117  {
118  throw QgsRequestNotWellFormedException( "FeatureId element without fid attribute" );
119  }
120 
121  QString fid = fidElem.attribute( QStringLiteral( "fid" ) );
122  if ( fid.contains( QLatin1String( "." ) ) )
123  {
124  if ( fid.section( QStringLiteral( "." ), 0, 0 ) != typeName )
125  continue;
126  fid = fid.section( QStringLiteral( "." ), 1, 1 );
127  }
128  fids.insert( fid.toInt() );
129  }
130 
131  if ( !fids.isEmpty() )
132  {
133  request.setFilterFids( fids );
134  }
135  else
136  {
137  throw QgsRequestNotWellFormedException( QStringLiteral( "No FeatureId element correctly parse against typeName '%1'" ).arg( typeName ) );
138  }
140  return request;
141  }
142  else if ( !goidNodes.isEmpty() )
143  {
144  QgsFeatureIds fids;
145  QDomElement goidElem;
146  for ( int f = 0; f < goidNodes.size(); f++ )
147  {
148  goidElem = goidNodes.at( f ).toElement();
149  if ( !goidElem.hasAttribute( QStringLiteral( "id" ) ) && !goidElem.hasAttribute( QStringLiteral( "gml:id" ) ) )
150  {
151  throw QgsRequestNotWellFormedException( "GmlObjectId element without gml:id attribute" );
152  }
153 
154  QString fid = goidElem.attribute( QStringLiteral( "id" ) );
155  if ( fid.isEmpty() )
156  fid = goidElem.attribute( QStringLiteral( "gml:id" ) );
157  if ( fid.contains( QLatin1String( "." ) ) )
158  {
159  if ( fid.section( QStringLiteral( "." ), 0, 0 ) != typeName )
160  continue;
161  fid = fid.section( QStringLiteral( "." ), 1, 1 );
162  }
163  fids.insert( fid.toInt() );
164  }
165 
166  if ( !fids.isEmpty() )
167  {
168  request.setFilterFids( fids );
169  }
170  else
171  {
172  throw QgsRequestNotWellFormedException( QStringLiteral( "No GmlObjectId element correctly parse against typeName '%1'" ).arg( typeName ) );
173  }
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  QgsGeometry geom = QgsOgcUtils::geometryFromGML( childElem );
191  request.setFilterRect( geom.boundingBox() );
192  }
193  childElem = childElem.nextSiblingElement();
194  }
196  return request;
197  }
198  // Apply BBOX through filterRect even inside an And to use spatial index
199  else if ( filterElem.firstChildElement().tagName() == QLatin1String( "And" ) &&
200  !filterElem.firstChildElement().firstChildElement( QLatin1String( "BBOX" ) ).isNull() )
201  {
202  QDomElement childElem = filterElem.firstChildElement().firstChildElement();
203  while ( !childElem.isNull() )
204  {
205  QDomElement childFilterElement = filterElem.ownerDocument().createElement( QLatin1String( "Filter" ) );
206  childFilterElement.appendChild( childElem.cloneNode( true ) );
207  QgsFeatureRequest childRequest = parseFilterElement( typeName, childFilterElement );
208  if ( childElem.tagName() == QLatin1String( "BBOX" ) )
209  {
210  if ( request.filterRect().isEmpty() )
211  {
212  request.setFilterRect( childRequest.filterRect() );
213  }
214  else
215  {
216  request.setFilterRect( request.filterRect().intersect( childRequest.filterRect() ) );
217  }
218  }
219  else
220  {
221  if ( !request.filterExpression() )
222  {
223  request.setFilterExpression( childRequest.filterExpression()->expression() );
224  }
225  else
226  {
227  QgsExpressionNode *opLeft = request.filterExpression()->rootNode()->clone();
228  QgsExpressionNode *opRight = childRequest.filterExpression()->rootNode()->clone();
229  std::unique_ptr<QgsExpressionNodeBinaryOperator> node = qgis::make_unique<QgsExpressionNodeBinaryOperator>( QgsExpressionNodeBinaryOperator::boAnd, opLeft, opRight );
230  QgsExpression expr( node->dump() );
231  request.setFilterExpression( expr );
232  }
233  }
234  childElem = childElem.nextSiblingElement();
235  }
237  return request;
238  }
239  else
240  {
241  QgsVectorLayer *layer = nullptr;
242  if ( project != nullptr )
243  {
244  layer = layerByTypeName( project, typeName );
245  }
246  std::shared_ptr<QgsExpression> filter( QgsOgcUtils::expressionFromOgcFilter( filterElem, layer ) );
247  if ( filter )
248  {
249  if ( filter->hasParserError() )
250  {
251  throw QgsRequestNotWellFormedException( filter->parserErrorString() );
252  }
253 
254  if ( filter->needsGeometry() )
255  {
257  }
258  request.setFilterExpression( filter->expression() );
259  return request;
260  }
261  }
262  return request;
263  }
264 
265 } // namespace QgsWfs
266 
267 
Class for parsing and evaluation of expressions (formerly called "search strings").
Base class for all map layer types.
Definition: qgsmaplayer.h:78
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
QgsMapLayerType type() const
Returns the type of the layer.
Use exact geometry intersection (slower) instead of bounding boxes.
QString shortName() const
Returns the short name of the layer used by QGIS Server to identify the layer.
Definition: qgsmaplayer.h:263
QgsVectorLayer * layerByTypeName(const QgsProject *project, const QString &typeName)
Retrieve a layer by typename.
Definition: qgswfsutils.cpp:80
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:111
virtual QgsExpressionNode * clone() const =0
Generate a clone of this node.
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...
const QgsRectangle & filterRect() const
Returns the rectangle from which features will be taken.
QString layerTypeName(const QgsMapLayer *layer)
Returns typename from vector layer.
Definition: qgswfsutils.cpp:71
static QgsRectangle rectangleFromGMLBox(const QDomNode &boxNode)
Read rectangle from GML2 Box.
QgsExpression * filterExpression() const
Returns the filter expression if set.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
QString serviceUrl(const QgsServerRequest &request, const QgsProject *project)
Service URL string.
Definition: qgswfsutils.cpp:37
Exception thrown in case of malformed request.
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:426
const QString & typeName
WMS implementation.
Definition: qgswfs.cpp:35
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
Definition: qgsrectangle.h:312
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
QgsFeatureRequest parseFilterElement(const QString &typeName, QDomElement &filterElem, const QgsProject *project)
Transform a Filter element to a feature request.
Reads and writes project states.
Definition: qgsproject.h:89
QString implementationVersion()
Returns the highest version supported by this implementation.
Definition: qgswfsutils.cpp:32
Abstract base class for all nodes that can appear in an expression.
QString expression() const
Returns the original, unmodified expression string.
QgsServerRequest Class defining request interface passed to services QgsService::executeRequest() met...
static QgsGeometry geometryFromGML(const QString &xmlString)
Static method that creates geometry from GML.
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets feature IDs that should be fetched.
const QgsExpressionNode * rootNode() const
Returns the root node of the expression.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
SERVER_EXPORT QStringList wfsLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published in WFS.
QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QString name
Definition: qgsmaplayer.h:82
static QgsExpression * expressionFromOgcFilter(const QDomElement &element, QgsVectorLayer *layer=nullptr)
Parse XML with OGC filter into QGIS expression.
Represents a vector layer which manages a vector based data sets.
SERVER_EXPORT QString wfsServiceUrl(const QgsProject &project)
Returns the WFS service url defined in a QGIS project.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.