QGIS API Documentation  3.6.0-Noosa (5873452)
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() != QgsMapLayer::LayerType::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:64
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
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:258
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:106
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...
QgsMapLayer::LayerType type() const
Returns the type of the layer.
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 root node of the expression. Root node is null is parsing has failed.
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:68
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.