QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
qgswfsgetfeature.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgswfsgetfeature.cpp
3  -------------------------
4  begin : December 20 , 2016
5  copyright : (C) 2007 by Marco Hugentobler (original code)
6  (C) 2012 by René-Luc D'Hont (original code)
7  (C) 2014 by Alessandro Pasotti (original code)
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 #include "qgswfsutils.h"
23 #include "qgsserverprojectutils.h"
24 #include "qgsserverfeatureid.h"
25 #include "qgsfields.h"
27 #include "qgsexpression.h"
28 #include "qgsgeometry.h"
29 #include "qgsmaplayer.h"
30 #include "qgsfeatureiterator.h"
32 #include "qgsvectorlayer.h"
33 #include "qgsfilterrestorer.h"
34 #include "qgsproject.h"
35 #include "qgsogcutils.h"
36 #include "qgsjsonutils.h"
38 
39 #include "qgswfsgetfeature.h"
40 
41 namespace QgsWfs
42 {
43 
44  namespace
45  {
46  struct createFeatureParams
47  {
48  int precision;
49 
51 
53 
54  const QString &typeName;
55 
56  bool withGeom;
57 
58  const QString &geometryName;
59 
61  };
62 
63  QString createFeatureGeoJSON( const QgsFeature &feature, const createFeatureParams &params, const QgsAttributeList &pkAttributes );
64 
65  QString encodeValueToText( const QVariant &value, const QgsEditorWidgetSetup &setup );
66 
67  QDomElement createFeatureGML2( const QgsFeature &feature, QDomDocument &doc, const createFeatureParams &params, const QgsProject *project, const QgsAttributeList &pkAttributes );
68 
69  QDomElement createFeatureGML3( const QgsFeature &feature, QDomDocument &doc, const createFeatureParams &params, const QgsProject *project, const QgsAttributeList &pkAttributes );
70 
71  void hitGetFeature( const QgsServerRequest &request, QgsServerResponse &response, const QgsProject *project,
72  QgsWfsParameters::Format format, int numberOfFeatures, const QStringList &typeNames );
73 
74  void startGetFeature( const QgsServerRequest &request, QgsServerResponse &response, const QgsProject *project,
76  QgsRectangle *rect, const QStringList &typeNames );
77 
78  void setGetFeature( QgsServerResponse &response, QgsWfsParameters::Format format, const QgsFeature &feature, int featIdx,
79  const createFeatureParams &params, const QgsProject *project, const QgsAttributeList &pkAttributes = QgsAttributeList() );
80 
81  void endGetFeature( QgsServerResponse &response, QgsWfsParameters::Format format );
82 
83  QgsServerRequest::Parameters mRequestParameters;
84  QgsWfsParameters mWfsParameters;
85  /* GeoJSON Exporter */
86  QgsJsonExporter mJsonExporter;
87  }
88 
89  void writeGetFeature( QgsServerInterface *serverIface, const QgsProject *project,
90  const QString &version, const QgsServerRequest &request,
91  QgsServerResponse &response )
92  {
93  Q_UNUSED( version )
94 
95  mRequestParameters = request.parameters();
96  mWfsParameters = QgsWfsParameters( QUrlQuery( request.url() ) );
97  mWfsParameters.dump();
98  getFeatureRequest aRequest;
99 
100  QDomDocument doc;
101  QString errorMsg;
102 
103  if ( doc.setContent( mRequestParameters.value( QStringLiteral( "REQUEST_BODY" ) ), true, &errorMsg ) )
104  {
105  QDomElement docElem = doc.documentElement();
106  aRequest = parseGetFeatureRequestBody( docElem, project );
107  }
108  else
109  {
110  aRequest = parseGetFeatureParameters( project );
111  }
112 
113  // store typeName
114  QStringList typeNameList;
115 
116  // Request metadata
117  bool onlyOneLayer = ( aRequest.queries.size() == 1 );
118  QgsRectangle requestRect;
119  QgsCoordinateReferenceSystem requestCrs;
120  int requestPrecision = 6;
121  if ( !onlyOneLayer )
123 
124  QList<getFeatureQuery>::iterator qIt = aRequest.queries.begin();
125  for ( ; qIt != aRequest.queries.end(); ++qIt )
126  {
127  typeNameList << ( *qIt ).typeName;
128  }
129 
130  // get layers and
131  // update the request metadata
132  QStringList wfsLayerIds = QgsServerProjectUtils::wfsLayerIds( *project );
133  QMap<QString, QgsMapLayer *> mapLayerMap;
134  for ( int i = 0; i < wfsLayerIds.size(); ++i )
135  {
136  QgsMapLayer *layer = project->mapLayer( wfsLayerIds.at( i ) );
137  if ( !layer )
138  {
139  continue;
140  }
141  if ( layer->type() != QgsMapLayerType::VectorLayer )
142  {
143  continue;
144  }
145 
146  QString name = layerTypeName( layer );
147 
148  if ( typeNameList.contains( name ) )
149  {
150  // store layers
151  mapLayerMap[name] = layer;
152  // update request metadata
153  if ( onlyOneLayer )
154  {
155  requestRect = layer->extent();
156  requestCrs = layer->crs();
157  }
158  else
159  {
160  QgsCoordinateTransform transform( layer->crs(), requestCrs, project );
161  try
162  {
163  if ( requestRect.isEmpty() )
164  {
165  requestRect = transform.transform( layer->extent() );
166  }
167  else
168  {
169  requestRect.combineExtentWith( transform.transform( layer->extent() ) );
170  }
171  }
172  catch ( QgsException &cse )
173  {
174  Q_UNUSED( cse )
175  requestRect = QgsRectangle( -180.0, -90.0, 180.0, 90.0 );
176  }
177  }
178  }
179  }
180 
181 #ifdef HAVE_SERVER_PYTHON_PLUGINS
182  QgsAccessControl *accessControl = serverIface->accessControls();
183  //scoped pointer to restore all original layer filters (subsetStrings) when pointer goes out of scope
184  //there's LOTS of potential exit paths here, so we avoid having to restore the filters manually
185  std::unique_ptr< QgsOWSServerFilterRestorer > filterRestorer( new QgsOWSServerFilterRestorer() );
186 #else
187  ( void )serverIface;
188 #endif
189 
190  // features counters
191  long sentFeatures = 0;
192  long iteratedFeatures = 0;
193  // sent features
194  QgsFeature feature;
195  qIt = aRequest.queries.begin();
196  for ( ; qIt != aRequest.queries.end(); ++qIt )
197  {
198  getFeatureQuery &query = *qIt;
199  QString typeName = query.typeName;
200 
201  if ( !mapLayerMap.keys().contains( typeName ) )
202  {
203  throw QgsRequestNotWellFormedException( QStringLiteral( "TypeName '%1' unknown" ).arg( typeName ) );
204  }
205 
206  QgsMapLayer *layer = mapLayerMap[typeName];
207 #ifdef HAVE_SERVER_PYTHON_PLUGINS
208  if ( accessControl && !accessControl->layerReadPermission( layer ) )
209  {
210  throw QgsSecurityAccessException( QStringLiteral( "Feature access permission denied" ) );
211  }
212 #endif
213  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
214  if ( !vlayer )
215  {
216  throw QgsRequestNotWellFormedException( QStringLiteral( "TypeName '%1' layer error" ).arg( typeName ) );
217  }
218 
219  //test provider
220  QgsVectorDataProvider *provider = vlayer->dataProvider();
221  if ( !provider )
222  {
223  throw QgsRequestNotWellFormedException( QStringLiteral( "TypeName '%1' layer's provider error" ).arg( typeName ) );
224  }
225 #ifdef HAVE_SERVER_PYTHON_PLUGINS
226  if ( accessControl )
227  {
228  QgsOWSServerFilterRestorer::applyAccessControlLayerFilters( accessControl, vlayer, filterRestorer->originalFilters() );
229  }
230 #endif
231  //is there alias info for this vector layer?
232  QMap< int, QString > layerAliasInfo;
233  QgsStringMap aliasMap = vlayer->attributeAliases();
234  QgsStringMap::const_iterator aliasIt = aliasMap.constBegin();
235  for ( ; aliasIt != aliasMap.constEnd(); ++aliasIt )
236  {
237  int attrIndex = vlayer->fields().lookupField( aliasIt.key() );
238  if ( attrIndex != -1 )
239  {
240  layerAliasInfo.insert( attrIndex, aliasIt.value() );
241  }
242  }
243 
244  // get propertyList from query
245  const QStringList propertyList = query.propertyList;
246 
247  //Using pending attributes and pending fields
248  QgsAttributeList attrIndexes = vlayer->attributeList();
249  QgsFields fields = vlayer->fields();
250  bool withGeom = true;
251  if ( !propertyList.isEmpty() && propertyList.first() != QStringLiteral( "*" ) )
252  {
253  withGeom = false;
254  QStringList::const_iterator plstIt;
255  QList<int> idxList;
256  // build corresponding propertyname
257  QList<QString> propertynames;
258  QList<QString> fieldnames;
259  for ( int idx = 0; idx < fields.count(); ++idx )
260  {
261  fieldnames.append( fields[idx].name() );
262  propertynames.append( fields.field( idx ).name().replace( ' ', '_' ).replace( cleanTagNameRegExp, QString() ) );
263  }
264  QString fieldName;
265  for ( plstIt = propertyList.constBegin(); plstIt != propertyList.constEnd(); ++plstIt )
266  {
267  fieldName = *plstIt;
268  int fieldNameIdx = propertynames.indexOf( fieldName );
269  if ( fieldNameIdx == -1 )
270  {
271  fieldNameIdx = fieldnames.indexOf( fieldName );
272  }
273  if ( fieldNameIdx > -1 )
274  {
275  idxList.append( fieldNameIdx );
276  }
277  else if ( fieldName == QStringLiteral( "geometry" ) )
278  {
279  withGeom = true;
280  }
281  }
282  if ( !idxList.isEmpty() )
283  {
284  attrIndexes = idxList;
285  }
286  }
287 
288  //excluded attributes for this layer
289  const QSet<QString> &layerExcludedAttributes = vlayer->excludeAttributesWfs();
290  if ( !attrIndexes.isEmpty() && !layerExcludedAttributes.isEmpty() )
291  {
292  foreach ( const QString &excludedAttribute, layerExcludedAttributes )
293  {
294  int fieldNameIdx = fields.indexOf( excludedAttribute );
295  if ( fieldNameIdx > -1 && attrIndexes.contains( fieldNameIdx ) )
296  {
297  attrIndexes.removeOne( fieldNameIdx );
298  }
299  }
300  }
301 
302 
303  // update request
304  QgsFeatureRequest featureRequest = query.featureRequest;
305 
306  // expression context
307  QgsExpressionContext expressionContext;
308  expressionContext << QgsExpressionContextUtils::globalScope()
311  featureRequest.setExpressionContext( expressionContext );
312 
313  if ( !query.serverFids.isEmpty() )
314  {
315  QgsServerFeatureId::updateFeatureRequestFromServerFids( featureRequest, query.serverFids, provider );
316  }
317 
318  // geometry flags
319  if ( vlayer->wkbType() == QgsWkbTypes::NoGeometry )
320  featureRequest.setFlags( featureRequest.flags() | QgsFeatureRequest::NoGeometry );
321  else
322  featureRequest.setFlags( featureRequest.flags() | ( withGeom ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry ) );
323  // subset of attributes
324  featureRequest.setSubsetOfAttributes( attrIndexes );
325 #ifdef HAVE_SERVER_PYTHON_PLUGINS
326  if ( accessControl )
327  {
328  accessControl->filterFeatures( vlayer, featureRequest );
329 
330  QStringList attributes = QStringList();
331  for ( int idx : attrIndexes )
332  {
333  attributes.append( vlayer->fields().field( idx ).name() );
334  }
335  featureRequest.setSubsetOfAttributes(
336  accessControl->layerAttributes( vlayer, attributes ),
337  vlayer->fields() );
338  }
339 #endif
340  if ( onlyOneLayer )
341  {
342  requestPrecision = QgsServerProjectUtils::wfsLayerPrecision( *project, vlayer->id() );
343  }
344 
345  if ( aRequest.maxFeatures > 0 )
346  {
347  featureRequest.setLimit( aRequest.maxFeatures + aRequest.startIndex - sentFeatures );
348  }
349  // specific layer precision
350  int layerPrecision = QgsServerProjectUtils::wfsLayerPrecision( *project, vlayer->id() );
351  // specific layer crs
352  QgsCoordinateReferenceSystem layerCrs = vlayer->crs();
353 
354  // Geometry name
355  QString geometryName = aRequest.geometryName;
356  if ( !withGeom )
357  {
358  geometryName = QLatin1String( "NONE" );
359  }
360  // outputCrs
362  if ( !query.srsName.isEmpty() )
363  {
365  }
366 
367  if ( !featureRequest.filterRect().isEmpty() )
368  {
369  QgsCoordinateTransform transform( outputCrs, vlayer->crs(), project );
370  try
371  {
372  featureRequest.setFilterRect( transform.transform( featureRequest.filterRect() ) );
373  }
374  catch ( QgsException &cse )
375  {
376  Q_UNUSED( cse )
377  }
378  if ( onlyOneLayer )
379  {
380  requestRect = featureRequest.filterRect();
381  }
382  }
383 
384  // Iterate through features
385  QgsFeatureIterator fit = vlayer->getFeatures( featureRequest );
386 
387  if ( mWfsParameters.resultType() == QgsWfsParameters::ResultType::HITS )
388  {
389  while ( fit.nextFeature( feature ) && ( aRequest.maxFeatures == -1 || sentFeatures < aRequest.maxFeatures ) )
390  {
391  if ( iteratedFeatures >= aRequest.startIndex )
392  {
393  ++sentFeatures;
394  }
395  ++iteratedFeatures;
396  }
397  }
398  else
399  {
400  const createFeatureParams cfp = { layerPrecision,
401  layerCrs,
402  attrIndexes,
403  typeName,
404  withGeom,
405  geometryName,
406  outputCrs
407  };
408  while ( fit.nextFeature( feature ) && ( aRequest.maxFeatures == -1 || sentFeatures < aRequest.maxFeatures ) )
409  {
410  if ( iteratedFeatures == aRequest.startIndex )
411  startGetFeature( request, response, project, aRequest.outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList );
412 
413  if ( iteratedFeatures >= aRequest.startIndex )
414  {
415  setGetFeature( response, aRequest.outputFormat, feature, sentFeatures, cfp, project, provider->pkAttributeIndexes() );
416  ++sentFeatures;
417  }
418  ++iteratedFeatures;
419  }
420  }
421  }
422 
423 #ifdef HAVE_SERVER_PYTHON_PLUGINS
424  //force restoration of original layer filters
425  filterRestorer.reset();
426 #endif
427 
428  if ( mWfsParameters.resultType() == QgsWfsParameters::ResultType::HITS )
429  {
430  hitGetFeature( request, response, project, aRequest.outputFormat, sentFeatures, typeNameList );
431  }
432  else
433  {
434  // End of GetFeature
435  if ( iteratedFeatures <= aRequest.startIndex )
436  startGetFeature( request, response, project, aRequest.outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList );
437  endGetFeature( response, aRequest.outputFormat );
438  }
439 
440  }
441 
443  {
444  getFeatureRequest request;
445  request.maxFeatures = mWfsParameters.maxFeaturesAsInt();;
446  request.startIndex = mWfsParameters.startIndexAsInt();
447  request.outputFormat = mWfsParameters.outputFormat();
448 
449  // Verifying parameters mutually exclusive
450  QStringList fidList = mWfsParameters.featureIds();
451  bool paramContainsFeatureIds = !fidList.isEmpty();
452  QStringList filterList = mWfsParameters.filters();
453  bool paramContainsFilters = !filterList.isEmpty();
454  QString bbox = mWfsParameters.bbox();
455  bool paramContainsBbox = !bbox.isEmpty();
456  if ( ( paramContainsFeatureIds
457  && ( paramContainsFilters || paramContainsBbox ) )
458  || ( paramContainsFilters
459  && ( paramContainsFeatureIds || paramContainsBbox ) )
460  || ( paramContainsBbox
461  && ( paramContainsFeatureIds || paramContainsFilters ) )
462  )
463  {
464  throw QgsRequestNotWellFormedException( QStringLiteral( "FEATUREID FILTER and BBOX parameters are mutually exclusive" ) );
465  }
466 
467  // Get and split PROPERTYNAME parameter
468  QStringList propertyNameList = mWfsParameters.propertyNames();
469 
470  // Manage extra parameter GeometryName
471  request.geometryName = mWfsParameters.geometryNameAsString().toUpper();
472 
473  QStringList typeNameList;
474  // parse FEATUREID
475  if ( paramContainsFeatureIds )
476  {
477  // Verifying the 1:1 mapping between FEATUREID and PROPERTYNAME
478  if ( !propertyNameList.isEmpty() && propertyNameList.size() != fidList.size() )
479  {
480  throw QgsRequestNotWellFormedException( QStringLiteral( "There has to be a 1:1 mapping between each element in a FEATUREID and the PROPERTYNAME list" ) );
481  }
482  if ( propertyNameList.isEmpty() )
483  {
484  for ( int i = 0; i < fidList.size(); ++i )
485  {
486  propertyNameList << QStringLiteral( "*" );
487  }
488  }
489 
490  QMap<QString, QStringList> fidsMap;
491 
492  QStringList::const_iterator fidIt = fidList.constBegin();
493  QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
494  for ( ; fidIt != fidList.constEnd(); ++fidIt )
495  {
496  // Get FeatureID
497  QString fid = *fidIt;
498  fid = fid.trimmed();
499  // Get PropertyName for this FeatureID
500  QString propertyName;
501  if ( propertyNameIt != propertyNameList.constEnd() )
502  {
503  propertyName = *propertyNameIt;
504  }
505  // testing typename in the WFS featureID
506  if ( !fid.contains( '.' ) )
507  {
508  throw QgsRequestNotWellFormedException( QStringLiteral( "FEATUREID has to have TYPENAME in the values" ) );
509  }
510 
511  QString typeName = fid.section( '.', 0, 0 );
512  fid = fid.section( '.', 1, 1 );
513  if ( !typeNameList.contains( typeName ) )
514  {
515  typeNameList << typeName;
516  }
517 
518  // each Feature requested by FEATUREID can have each own property list
519  QString key = QStringLiteral( "%1(%2)" ).arg( typeName ).arg( propertyName );
520  QStringList fids;
521  if ( fidsMap.contains( key ) )
522  {
523  fids = fidsMap.value( key );
524  }
525  fids.append( fid );
526  fidsMap.insert( key, fids );
527 
528  if ( propertyNameIt != propertyNameList.constEnd() )
529  {
530  ++propertyNameIt;
531  }
532  }
533 
534  QMap<QString, QStringList>::const_iterator fidsMapIt = fidsMap.constBegin();
535  while ( fidsMapIt != fidsMap.constEnd() )
536  {
537  QString key = fidsMapIt.key();
538 
539  //Extract TypeName and PropertyName from key
540  QRegExp rx( "([^()]+)\\(([^()]+)\\)" );
541  if ( rx.indexIn( key, 0 ) == -1 )
542  {
543  throw QgsRequestNotWellFormedException( QStringLiteral( "Error getting properties for FEATUREID" ) );
544  }
545  QString typeName = rx.cap( 1 );
546  QString propertyName = rx.cap( 2 );
547 
548  getFeatureQuery query;
549  query.typeName = typeName;
550  query.srsName = mWfsParameters.srsName();
551 
552  // Parse PropertyName
553  if ( propertyName != QStringLiteral( "*" ) )
554  {
555  QStringList propertyList;
556 
557  const QStringList attrList = propertyName.split( ',' );
558  QStringList::const_iterator alstIt;
559  for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
560  {
561  QString fieldName = *alstIt;
562  fieldName = fieldName.trimmed();
563  if ( fieldName.contains( ':' ) )
564  {
565  fieldName = fieldName.section( ':', 1, 1 );
566  }
567  if ( fieldName.contains( '/' ) )
568  {
569  if ( fieldName.section( '/', 0, 0 ) != typeName )
570  {
571  throw QgsRequestNotWellFormedException( QStringLiteral( "PropertyName text '%1' has to contain TypeName '%2'" ).arg( fieldName ).arg( typeName ) );
572  }
573  fieldName = fieldName.section( '/', 1, 1 );
574  }
575  propertyList.append( fieldName );
576  }
577  query.propertyList = propertyList;
578  }
579 
580  query.serverFids = fidsMapIt.value();
581  QgsFeatureRequest featureRequest;
582 
583  query.featureRequest = featureRequest;
584  request.queries.append( query );
585  fidsMapIt++;
586  }
587  return request;
588  }
589 
590  if ( !mRequestParameters.contains( QStringLiteral( "TYPENAME" ) ) )
591  {
592  throw QgsRequestNotWellFormedException( QStringLiteral( "TYPENAME is mandatory except if FEATUREID is used" ) );
593  }
594 
595  typeNameList = mWfsParameters.typeNames();
596  // Verifying the 1:1 mapping between TYPENAME and PROPERTYNAME
597  if ( !propertyNameList.isEmpty() && typeNameList.size() != propertyNameList.size() )
598  {
599  throw QgsRequestNotWellFormedException( QStringLiteral( "There has to be a 1:1 mapping between each element in a TYPENAME and the PROPERTYNAME list" ) );
600  }
601  if ( propertyNameList.isEmpty() )
602  {
603  for ( int i = 0; i < typeNameList.size(); ++i )
604  {
605  propertyNameList << QStringLiteral( "*" );
606  }
607  }
608 
609  // Create queries based on TypeName and propertyName
610  QStringList::const_iterator typeNameIt = typeNameList.constBegin();
611  QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
612  for ( ; typeNameIt != typeNameList.constEnd(); ++typeNameIt )
613  {
614  QString typeName = *typeNameIt;
615  typeName = typeName.trimmed();
616  // Get PropertyName for this typeName
617  QString propertyName;
618  if ( propertyNameIt != propertyNameList.constEnd() )
619  {
620  propertyName = *propertyNameIt;
621  }
622 
623  getFeatureQuery query;
624  query.typeName = typeName;
625  query.srsName = mWfsParameters.srsName();
626 
627  // Parse PropertyName
628  if ( propertyName != QStringLiteral( "*" ) )
629  {
630  QStringList propertyList;
631 
632  const QStringList attrList = propertyName.split( ',' );
633  QStringList::const_iterator alstIt;
634  for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
635  {
636  QString fieldName = *alstIt;
637  fieldName = fieldName.trimmed();
638  if ( fieldName.contains( ':' ) )
639  {
640  fieldName = fieldName.section( ':', 1, 1 );
641  }
642  if ( fieldName.contains( '/' ) )
643  {
644  if ( fieldName.section( '/', 0, 0 ) != typeName )
645  {
646  throw QgsRequestNotWellFormedException( QStringLiteral( "PropertyName text '%1' has to contain TypeName '%2'" ).arg( fieldName ).arg( typeName ) );
647  }
648  fieldName = fieldName.section( '/', 1, 1 );
649  }
650  propertyList.append( fieldName );
651  }
652  query.propertyList = propertyList;
653  }
654 
655  request.queries.append( query );
656 
657  if ( propertyNameIt != propertyNameList.constEnd() )
658  {
659  ++propertyNameIt;
660  }
661  }
662 
663  // Manage extra parameter exp_filter
664  QStringList expFilterList = mWfsParameters.expFilters();
665  if ( !expFilterList.isEmpty() )
666  {
667  // Verifying the 1:1 mapping between TYPENAME and EXP_FILTER but without exception
668  if ( request.queries.size() == expFilterList.size() )
669  {
670  // set feature request filter expression based on filter element
671  QList<getFeatureQuery>::iterator qIt = request.queries.begin();
672  QStringList::const_iterator expFilterIt = expFilterList.constBegin();
673  for ( ; qIt != request.queries.end(); ++qIt )
674  {
675  getFeatureQuery &query = *qIt;
676  // Get Filter for this typeName
677  QString expFilter;
678  if ( expFilterIt != expFilterList.constEnd() )
679  {
680  expFilter = *expFilterIt;
681  }
682  std::shared_ptr<QgsExpression> filter( new QgsExpression( expFilter ) );
683  if ( filter )
684  {
685  if ( filter->hasParserError() )
686  {
687  throw QgsRequestNotWellFormedException( QStringLiteral( "The EXP_FILTER expression has errors: %1" ).arg( filter->parserErrorString() ) );
688  }
689  if ( filter->needsGeometry() )
690  {
692  }
693  query.featureRequest.setFilterExpression( filter->expression() );
694  }
695  }
696  }
697  else
698  {
699  QgsMessageLog::logMessage( "There has to be a 1:1 mapping between each element in a TYPENAME and the EXP_FILTER list" );
700  }
701  }
702 
703  if ( paramContainsBbox )
704  {
705 
706  // get bbox extent
707  QgsRectangle extent = mWfsParameters.bboxAsRectangle();
708 
709  // handle WFS 1.1.0 optional CRS
710  if ( mWfsParameters.bbox().split( ',' ).size() == 5 && ! mWfsParameters.srsName().isEmpty() )
711  {
712  QString crs( mWfsParameters.bbox().split( ',' )[4] );
713  if ( crs != mWfsParameters.srsName() )
714  {
715  QgsCoordinateReferenceSystem sourceCrs( crs );
716  QgsCoordinateReferenceSystem destinationCrs( mWfsParameters.srsName() );
717  if ( sourceCrs.isValid() && destinationCrs.isValid( ) )
718  {
719  QgsGeometry extentGeom = QgsGeometry::fromRect( extent );
720  QgsCoordinateTransform transform;
721  transform.setSourceCrs( sourceCrs );
722  transform.setDestinationCrs( destinationCrs );
723  try
724  {
725  if ( extentGeom.transform( transform ) == 0 )
726  {
727  extent = QgsRectangle( extentGeom.boundingBox() );
728  }
729  }
730  catch ( QgsException &cse )
731  {
732  Q_UNUSED( cse )
733  }
734  }
735  }
736  }
737 
738  // set feature request filter rectangle
739  QList<getFeatureQuery>::iterator qIt = request.queries.begin();
740  for ( ; qIt != request.queries.end(); ++qIt )
741  {
742  getFeatureQuery &query = *qIt;
743  query.featureRequest.setFilterRect( extent );
744  }
745  return request;
746  }
747  else if ( paramContainsFilters )
748  {
749  // Verifying the 1:1 mapping between TYPENAME and FILTER
750  if ( request.queries.size() != filterList.size() )
751  {
752  throw QgsRequestNotWellFormedException( QStringLiteral( "There has to be a 1:1 mapping between each element in a TYPENAME and the FILTER list" ) );
753  }
754 
755  // set feature request filter expression based on filter element
756  QList<getFeatureQuery>::iterator qIt = request.queries.begin();
757  QStringList::const_iterator filterIt = filterList.constBegin();
758  for ( ; qIt != request.queries.end(); ++qIt )
759  {
760  getFeatureQuery &query = *qIt;
761  // Get Filter for this typeName
762  QDomDocument filter;
763  if ( filterIt != filterList.constEnd() )
764  {
765  QString errorMsg;
766  if ( !filter.setContent( *filterIt, true, &errorMsg ) )
767  {
768  throw QgsRequestNotWellFormedException( QStringLiteral( "error message: %1. The XML string was: %2" ).arg( errorMsg, *filterIt ) );
769  }
770  }
771 
772  QDomElement filterElem = filter.firstChildElement();
773  QStringList serverFids;
774  query.featureRequest = parseFilterElement( query.typeName, filterElem, serverFids, project );
775  query.serverFids = serverFids;
776 
777  if ( filterIt != filterList.constEnd() )
778  {
779  ++filterIt;
780  }
781  }
782  return request;
783  }
784 
785  QStringList sortByList = mWfsParameters.sortBy();
786  if ( !sortByList.isEmpty() && request.queries.size() == sortByList.size() )
787  {
788  // add order by to feature request
789  QList<getFeatureQuery>::iterator qIt = request.queries.begin();
790  QStringList::const_iterator sortByIt = sortByList.constBegin();
791  for ( ; qIt != request.queries.end(); ++qIt )
792  {
793  getFeatureQuery &query = *qIt;
794  // Get sortBy for this typeName
795  QString sortBy;
796  if ( sortByIt != sortByList.constEnd() )
797  {
798  sortBy = *sortByIt;
799  }
800  for ( const QString &attribute : sortBy.split( ',' ) )
801  {
802  if ( attribute.endsWith( QLatin1String( " D" ) ) || attribute.endsWith( QLatin1String( "+D" ) ) )
803  {
804  query.featureRequest.addOrderBy( attribute.left( attribute.size() - 2 ), false );
805  }
806  else if ( attribute.endsWith( QLatin1String( " DESC" ) ) || attribute.endsWith( QLatin1String( "+DESC" ) ) )
807  {
808  query.featureRequest.addOrderBy( attribute.left( attribute.size() - 5 ), false );
809  }
810  else if ( attribute.endsWith( QLatin1String( " A" ) ) || attribute.endsWith( QLatin1String( "+A" ) ) )
811  {
812  query.featureRequest.addOrderBy( attribute.left( attribute.size() - 2 ) );
813  }
814  else if ( attribute.endsWith( QLatin1String( " ASC" ) ) || attribute.endsWith( QLatin1String( "+ASC" ) ) )
815  {
816  query.featureRequest.addOrderBy( attribute.left( attribute.size() - 4 ) );
817  }
818  else
819  {
820  query.featureRequest.addOrderBy( attribute );
821  }
822  }
823  }
824  }
825 
826  return request;
827  }
828 
829  getFeatureRequest parseGetFeatureRequestBody( QDomElement &docElem, const QgsProject *project )
830  {
831  getFeatureRequest request;
832  request.maxFeatures = mWfsParameters.maxFeaturesAsInt();;
833  request.startIndex = mWfsParameters.startIndexAsInt();
834  request.outputFormat = mWfsParameters.outputFormat();
835 
836  QDomNodeList queryNodes = docElem.elementsByTagName( QStringLiteral( "Query" ) );
837  QDomElement queryElem;
838  for ( int i = 0; i < queryNodes.size(); i++ )
839  {
840  queryElem = queryNodes.at( i ).toElement();
841  getFeatureQuery query = parseQueryElement( queryElem, project );
842  request.queries.append( query );
843  }
844  return request;
845  }
846 
847  void parseSortByElement( QDomElement &sortByElem, QgsFeatureRequest &featureRequest, const QString &typeName )
848  {
849  QDomNodeList sortByNodes = sortByElem.childNodes();
850  if ( sortByNodes.size() )
851  {
852  for ( int i = 0; i < sortByNodes.size(); i++ )
853  {
854  QDomElement sortPropElem = sortByNodes.at( i ).toElement();
855  QDomNodeList sortPropChildNodes = sortPropElem.childNodes();
856  if ( sortPropChildNodes.size() )
857  {
858  QString fieldName;
859  bool ascending = true;
860  for ( int j = 0; j < sortPropChildNodes.size(); j++ )
861  {
862  QDomElement sortPropChildElem = sortPropChildNodes.at( j ).toElement();
863  if ( sortPropChildElem.tagName() == QLatin1String( "PropertyName" ) )
864  {
865  fieldName = sortPropChildElem.text().trimmed();
866  }
867  else if ( sortPropChildElem.tagName() == QLatin1String( "SortOrder" ) )
868  {
869  QString sortOrder = sortPropChildElem.text().trimmed().toUpper();
870  if ( sortOrder == QLatin1String( "DESC" ) || sortOrder == QLatin1String( "D" ) )
871  ascending = false;
872  }
873  }
874  // clean fieldName
875  if ( fieldName.contains( ':' ) )
876  {
877  fieldName = fieldName.section( ':', 1, 1 );
878  }
879  if ( fieldName.contains( '/' ) )
880  {
881  if ( fieldName.section( '/', 0, 0 ) != typeName )
882  {
883  throw QgsRequestNotWellFormedException( QStringLiteral( "PropertyName text '%1' has to contain TypeName '%2'" ).arg( fieldName ).arg( typeName ) );
884  }
885  fieldName = fieldName.section( '/', 1, 1 );
886  }
887  // addOrderBy
888  if ( !fieldName.isEmpty() )
889  featureRequest.addOrderBy( fieldName, ascending );
890  }
891  }
892  }
893  }
894 
895  getFeatureQuery parseQueryElement( QDomElement &queryElem, const QgsProject *project )
896  {
897  QString typeName = queryElem.attribute( QStringLiteral( "typeName" ), QString() );
898  if ( typeName.contains( ':' ) )
899  {
900  typeName = typeName.section( ':', 1, 1 );
901  }
902 
903  QgsFeatureRequest featureRequest;
904  QStringList serverFids;
905  QStringList propertyList;
906  QDomNodeList queryChildNodes = queryElem.childNodes();
907  if ( queryChildNodes.size() )
908  {
909  QDomElement sortByElem;
910  for ( int q = 0; q < queryChildNodes.size(); q++ )
911  {
912  QDomElement queryChildElem = queryChildNodes.at( q ).toElement();
913  if ( queryChildElem.tagName() == QLatin1String( "PropertyName" ) )
914  {
915  QString fieldName = queryChildElem.text().trimmed();
916  if ( fieldName.contains( ':' ) )
917  {
918  fieldName = fieldName.section( ':', 1, 1 );
919  }
920  if ( fieldName.contains( '/' ) )
921  {
922  if ( fieldName.section( '/', 0, 0 ) != typeName )
923  {
924  throw QgsRequestNotWellFormedException( QStringLiteral( "PropertyName text '%1' has to contain TypeName '%2'" ).arg( fieldName ).arg( typeName ) );
925  }
926  fieldName = fieldName.section( '/', 1, 1 );
927  }
928  propertyList.append( fieldName );
929  }
930  else if ( queryChildElem.tagName() == QLatin1String( "Filter" ) )
931  {
932  featureRequest = parseFilterElement( typeName, queryChildElem, serverFids, project );
933  }
934  else if ( queryChildElem.tagName() == QLatin1String( "SortBy" ) )
935  {
936  sortByElem = queryChildElem;
937  }
938  }
939  parseSortByElement( sortByElem, featureRequest, typeName );
940  }
941 
942  // srsName attribute
943  QString srsName = queryElem.attribute( QStringLiteral( "srsName" ), QString() );
944 
945  getFeatureQuery query;
946  query.typeName = typeName;
947  query.srsName = srsName;
948  query.featureRequest = featureRequest;
949  query.serverFids = serverFids;
950  query.propertyList = propertyList;
951  return query;
952  }
953 
954  namespace
955  {
956  static QSet< QString > sParamFilter
957  {
958  QStringLiteral( "REQUEST" ),
959  QStringLiteral( "FORMAT" ),
960  QStringLiteral( "OUTPUTFORMAT" ),
961  QStringLiteral( "BBOX" ),
962  QStringLiteral( "FEATUREID" ),
963  QStringLiteral( "TYPENAME" ),
964  QStringLiteral( "FILTER" ),
965  QStringLiteral( "EXP_FILTER" ),
966  QStringLiteral( "MAXFEATURES" ),
967  QStringLiteral( "STARTINDEX" ),
968  QStringLiteral( "PROPERTYNAME" ),
969  QStringLiteral( "_DC" )
970  };
971 
972 
973  void hitGetFeature( const QgsServerRequest &request, QgsServerResponse &response, const QgsProject *project, QgsWfsParameters::Format format,
974  int numberOfFeatures, const QStringList &typeNames )
975  {
976  QDateTime now = QDateTime::currentDateTime();
977  QString fcString;
978 
979  if ( format == QgsWfsParameters::Format::GeoJSON )
980  {
981  response.setHeader( "Content-Type", "application/vnd.geo+json; charset=utf-8" );
982  fcString = QStringLiteral( "{\"type\": \"FeatureCollection\",\n" );
983  fcString += QStringLiteral( " \"timeStamp\": \"%1\"\n" ).arg( now.toString( Qt::ISODate ) );
984  fcString += QStringLiteral( " \"numberOfFeatures\": %1\n" ).arg( QString::number( numberOfFeatures ) );
985  fcString += QLatin1String( "}" );
986  }
987  else
988  {
989  if ( format == QgsWfsParameters::Format::GML2 )
990  response.setHeader( "Content-Type", "text/xml; subtype=gml/2.1.2; charset=utf-8" );
991  else
992  response.setHeader( "Content-Type", "text/xml; subtype=gml/3.1.1; charset=utf-8" );
993 
994  //Prepare url
995  QString hrefString = serviceUrl( request, project );
996 
997  QUrl mapUrl( hrefString );
998 
999  QUrlQuery query( mapUrl );
1000  query.addQueryItem( QStringLiteral( "SERVICE" ), QStringLiteral( "WFS" ) );
1001  //Set version
1002  if ( mWfsParameters.version().isEmpty() )
1003  query.addQueryItem( QStringLiteral( "VERSION" ), implementationVersion() );
1004  else if ( mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) )
1005  query.addQueryItem( QStringLiteral( "VERSION" ), QStringLiteral( "1.1.0" ) );
1006  else
1007  query.addQueryItem( QStringLiteral( "VERSION" ), QStringLiteral( "1.0.0" ) );
1008 
1009  for ( auto param : query.queryItems() )
1010  {
1011  if ( sParamFilter.contains( param.first.toUpper() ) )
1012  query.removeAllQueryItems( param.first );
1013  }
1014 
1015  query.addQueryItem( QStringLiteral( "REQUEST" ), QStringLiteral( "DescribeFeatureType" ) );
1016  query.addQueryItem( QStringLiteral( "TYPENAME" ), typeNames.join( ',' ) );
1017  if ( mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) )
1018  {
1019  if ( format == QgsWfsParameters::Format::GML2 )
1020  query.addQueryItem( QStringLiteral( "OUTPUTFORMAT" ), QStringLiteral( "text/xml; subtype=gml/2.1.2" ) );
1021  else
1022  query.addQueryItem( QStringLiteral( "OUTPUTFORMAT" ), QStringLiteral( "text/xml; subtype=gml/3.1.1" ) );
1023  }
1024  else
1025  query.addQueryItem( QStringLiteral( "OUTPUTFORMAT" ), QStringLiteral( "XMLSCHEMA" ) );
1026 
1027  mapUrl.setQuery( query );
1028 
1029  hrefString = mapUrl.toString();
1030 
1031  //wfs:FeatureCollection valid
1032  fcString = QStringLiteral( "<wfs:FeatureCollection" );
1033  fcString += " xmlns:wfs=\"" + WFS_NAMESPACE + "\"";
1034  fcString += " xmlns:ogc=\"" + OGC_NAMESPACE + "\"";
1035  fcString += " xmlns:gml=\"" + GML_NAMESPACE + "\"";
1036  fcString += QLatin1String( " xmlns:ows=\"http://www.opengis.net/ows\"" );
1037  fcString += QLatin1String( " xmlns:xlink=\"http://www.w3.org/1999/xlink\"" );
1038  fcString += " xmlns:qgs=\"" + QGS_NAMESPACE + "\"";
1039  fcString += QLatin1String( " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" );
1040  fcString += " xsi:schemaLocation=\"" + WFS_NAMESPACE + " http://schemas.opengis.net/wfs/1.0.0/wfs.xsd " + QGS_NAMESPACE + " " + hrefString.replace( QLatin1String( "&" ), QLatin1String( "&amp;" ) ) + "\"";
1041  fcString += "\n timeStamp=\"" + now.toString( Qt::ISODate ) + "\"";
1042  fcString += "\n numberOfFeatures=\"" + QString::number( numberOfFeatures ) + "\"";
1043  fcString += QLatin1String( ">\n" );
1044  fcString += QStringLiteral( "</wfs:FeatureCollection>" );
1045  }
1046 
1047  response.write( fcString.toUtf8() );
1048  response.flush();
1049  }
1050 
1051  void startGetFeature( const QgsServerRequest &request, QgsServerResponse &response, const QgsProject *project, QgsWfsParameters::Format format,
1052  int prec, QgsCoordinateReferenceSystem &crs, QgsRectangle *rect, const QStringList &typeNames )
1053  {
1054  QString fcString;
1055 
1056  std::unique_ptr< QgsRectangle > transformedRect;
1057 
1058  if ( format == QgsWfsParameters::Format::GeoJSON )
1059  {
1060  response.setHeader( "Content-Type", "application/vnd.geo+json; charset=utf-8" );
1061 
1062  if ( crs.isValid() && !rect->isEmpty() )
1063  {
1064  QgsGeometry exportGeom = QgsGeometry::fromRect( *rect );
1065  QgsCoordinateTransform transform;
1066  transform.setSourceCrs( crs );
1068  try
1069  {
1070  if ( exportGeom.transform( transform ) == 0 )
1071  {
1072  transformedRect.reset( new QgsRectangle( exportGeom.boundingBox() ) );
1073  rect = transformedRect.get();
1074  }
1075  }
1076  catch ( QgsException &cse )
1077  {
1078  Q_UNUSED( cse )
1079  }
1080  }
1081  // EPSG:4326 max extent is -180, -90, 180, 90
1082  rect = new QgsRectangle( rect->intersect( QgsRectangle( -180.0, -90.0, 180.0, 90.0 ) ) );
1083 
1084  fcString = QStringLiteral( "{\"type\": \"FeatureCollection\",\n" );
1085  fcString += " \"bbox\": [ " + qgsDoubleToString( rect->xMinimum(), prec ) + ", " + qgsDoubleToString( rect->yMinimum(), prec ) + ", " + qgsDoubleToString( rect->xMaximum(), prec ) + ", " + qgsDoubleToString( rect->yMaximum(), prec ) + "],\n";
1086  fcString += QLatin1String( " \"features\": [\n" );
1087  response.write( fcString.toUtf8() );
1088  }
1089  else
1090  {
1091  if ( format == QgsWfsParameters::Format::GML2 )
1092  response.setHeader( "Content-Type", "text/xml; subtype=gml/2.1.2; charset=utf-8" );
1093  else
1094  response.setHeader( "Content-Type", "text/xml; subtype=gml/3.1.1; charset=utf-8" );
1095 
1096  //Prepare url
1097  QString hrefString = serviceUrl( request, project );
1098 
1099  QUrl mapUrl( hrefString );
1100 
1101  QUrlQuery query( mapUrl );
1102  query.addQueryItem( QStringLiteral( "SERVICE" ), QStringLiteral( "WFS" ) );
1103  //Set version
1104  if ( mWfsParameters.version().isEmpty() )
1105  query.addQueryItem( QStringLiteral( "VERSION" ), implementationVersion() );
1106  else if ( mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) )
1107  query.addQueryItem( QStringLiteral( "VERSION" ), QStringLiteral( "1.1.0" ) );
1108  else
1109  query.addQueryItem( QStringLiteral( "VERSION" ), QStringLiteral( "1.0.0" ) );
1110 
1111  for ( auto param : query.queryItems() )
1112  {
1113  if ( sParamFilter.contains( param.first.toUpper() ) )
1114  query.removeAllQueryItems( param.first );
1115  }
1116 
1117  query.addQueryItem( QStringLiteral( "REQUEST" ), QStringLiteral( "DescribeFeatureType" ) );
1118  query.addQueryItem( QStringLiteral( "TYPENAME" ), typeNames.join( ',' ) );
1119  if ( mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) )
1120  {
1121  if ( format == QgsWfsParameters::Format::GML2 )
1122  query.addQueryItem( QStringLiteral( "OUTPUTFORMAT" ), QStringLiteral( "text/xml; subtype=gml/2.1.2" ) );
1123  else
1124  query.addQueryItem( QStringLiteral( "OUTPUTFORMAT" ), QStringLiteral( "text/xml; subtype=gml/3.1.1" ) );
1125  }
1126  else
1127  query.addQueryItem( QStringLiteral( "OUTPUTFORMAT" ), QStringLiteral( "XMLSCHEMA" ) );
1128 
1129  mapUrl.setQuery( query );
1130 
1131  hrefString = mapUrl.toString();
1132 
1133  //wfs:FeatureCollection valid
1134  fcString = QStringLiteral( "<wfs:FeatureCollection" );
1135  fcString += " xmlns:wfs=\"" + WFS_NAMESPACE + "\"";
1136  fcString += " xmlns:ogc=\"" + OGC_NAMESPACE + "\"";
1137  fcString += " xmlns:gml=\"" + GML_NAMESPACE + "\"";
1138  fcString += QLatin1String( " xmlns:ows=\"http://www.opengis.net/ows\"" );
1139  fcString += QLatin1String( " xmlns:xlink=\"http://www.w3.org/1999/xlink\"" );
1140  fcString += " xmlns:qgs=\"" + QGS_NAMESPACE + "\"";
1141  fcString += QLatin1String( " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" );
1142  fcString += " xsi:schemaLocation=\"" + WFS_NAMESPACE + " http://schemas.opengis.net/wfs/1.0.0/wfs.xsd " + QGS_NAMESPACE + " " + hrefString.replace( QLatin1String( "&" ), QLatin1String( "&amp;" ) ) + "\"";
1143  fcString += QLatin1String( ">\n" );
1144 
1145  response.write( fcString.toUtf8() );
1146  response.flush();
1147 
1148  QDomDocument doc;
1149  QDomElement bbElem = doc.createElement( QStringLiteral( "gml:boundedBy" ) );
1150  if ( format == QgsWfsParameters::Format::GML3 )
1151  {
1152  QDomElement envElem = QgsOgcUtils::rectangleToGMLEnvelope( rect, doc, prec );
1153  if ( !envElem.isNull() )
1154  {
1155  if ( crs.isValid() )
1156  {
1157  envElem.setAttribute( QStringLiteral( "srsName" ), crs.authid() );
1158  }
1159  bbElem.appendChild( envElem );
1160  doc.appendChild( bbElem );
1161  }
1162  }
1163  else
1164  {
1165  QDomElement boxElem = QgsOgcUtils::rectangleToGMLBox( rect, doc, prec );
1166  if ( !boxElem.isNull() )
1167  {
1168  if ( crs.isValid() )
1169  {
1170  boxElem.setAttribute( QStringLiteral( "srsName" ), crs.authid() );
1171  }
1172  bbElem.appendChild( boxElem );
1173  doc.appendChild( bbElem );
1174  }
1175  }
1176  response.write( doc.toByteArray() );
1177  response.flush();
1178  }
1179  }
1180 
1181  void setGetFeature( QgsServerResponse &response, QgsWfsParameters::Format format, const QgsFeature &feature, int featIdx,
1182  const createFeatureParams &params, const QgsProject *project, const QgsAttributeList &pkAttributes )
1183  {
1184  if ( !feature.isValid() )
1185  return;
1186 
1187  if ( format == QgsWfsParameters::Format::GeoJSON )
1188  {
1189  QString fcString;
1190  if ( featIdx == 0 )
1191  fcString += QLatin1String( " " );
1192  else
1193  fcString += QLatin1String( " ," );
1194  mJsonExporter.setSourceCrs( params.crs );
1195  mJsonExporter.setIncludeGeometry( false );
1196  mJsonExporter.setIncludeAttributes( !params.attributeIndexes.isEmpty() );
1197  mJsonExporter.setAttributes( params.attributeIndexes );
1198  fcString += createFeatureGeoJSON( feature, params, pkAttributes );
1199  fcString += QLatin1String( "\n" );
1200 
1201  response.write( fcString.toUtf8() );
1202  }
1203  else
1204  {
1205  QDomDocument gmlDoc;
1206  QDomElement featureElement;
1207  if ( format == QgsWfsParameters::Format::GML3 )
1208  {
1209  featureElement = createFeatureGML3( feature, gmlDoc, params, project, pkAttributes );
1210  gmlDoc.appendChild( featureElement );
1211  }
1212  else
1213  {
1214  featureElement = createFeatureGML2( feature, gmlDoc, params, project, pkAttributes );
1215  gmlDoc.appendChild( featureElement );
1216  }
1217  response.write( gmlDoc.toByteArray() );
1218  }
1219 
1220  // Stream partial content
1221  response.flush();
1222  }
1223 
1224  void endGetFeature( QgsServerResponse &response, QgsWfsParameters::Format format )
1225  {
1226  QString fcString;
1227  if ( format == QgsWfsParameters::Format::GeoJSON )
1228  {
1229  fcString += QLatin1String( " ]\n" );
1230  fcString += QLatin1String( "}" );
1231  }
1232  else
1233  {
1234  fcString = QStringLiteral( "</wfs:FeatureCollection>\n" );
1235  }
1236  response.write( fcString.toUtf8() );
1237  }
1238 
1239 
1240  QString createFeatureGeoJSON( const QgsFeature &feature, const createFeatureParams &params, const QgsAttributeList &pkAttributes )
1241  {
1242  QString id = QStringLiteral( "%1.%2" ).arg( params.typeName, QgsServerFeatureId::getServerFid( feature, pkAttributes ) );
1243  //QgsJsonExporter force transform geometry to ESPG:4326
1244  //and the RFC 7946 GeoJSON specification recommends limiting coordinate precision to 6
1245  //Q_UNUSED( prec )
1246 
1247  //copy feature so we can modify its geometry as required
1248  QgsFeature f( feature );
1249  QgsGeometry geom = feature.geometry();
1250  if ( !geom.isNull() && params.withGeom && params.geometryName != QLatin1String( "NONE" ) )
1251  {
1252  mJsonExporter.setIncludeGeometry( true );
1253  if ( params.geometryName == QLatin1String( "EXTENT" ) )
1254  {
1255  QgsRectangle box = geom.boundingBox();
1256  f.setGeometry( QgsGeometry::fromRect( box ) );
1257  }
1258  else if ( params.geometryName == QLatin1String( "CENTROID" ) )
1259  {
1260  f.setGeometry( geom.centroid() );
1261  }
1262  }
1263 
1264  return mJsonExporter.exportFeature( f, QVariantMap(), id );
1265  }
1266 
1267 
1268  QDomElement createFeatureGML2( const QgsFeature &feature, QDomDocument &doc, const createFeatureParams &params, const QgsProject *project, const QgsAttributeList &pkAttributes )
1269  {
1270  //gml:FeatureMember
1271  QDomElement featureElement = doc.createElement( QStringLiteral( "gml:featureMember" )/*wfs:FeatureMember*/ );
1272 
1273  //qgs:%TYPENAME%
1274  QDomElement typeNameElement = doc.createElement( "qgs:" + params.typeName /*qgs:%TYPENAME%*/ );
1275  QString id = QStringLiteral( "%1.%2" ).arg( params.typeName, QgsServerFeatureId::getServerFid( feature, pkAttributes ) );
1276  typeNameElement.setAttribute( QStringLiteral( "fid" ), id );
1277  featureElement.appendChild( typeNameElement );
1278 
1279  //add geometry column (as gml)
1280  QgsGeometry geom = feature.geometry();
1281  if ( !geom.isNull() && params.withGeom && params.geometryName != QLatin1String( "NONE" ) )
1282  {
1283  int prec = params.precision;
1284  QgsCoordinateReferenceSystem crs = params.crs;
1285  QgsCoordinateTransform mTransform( crs, params.outputCrs, project );
1286  try
1287  {
1288  QgsGeometry transformed = geom;
1289  if ( transformed.transform( mTransform ) == 0 )
1290  {
1291  geom = transformed;
1292  crs = params.outputCrs;
1293  if ( crs.isGeographic() && !params.crs.isGeographic() )
1294  prec = std::min( params.precision + 3, 6 );
1295  }
1296  }
1297  catch ( QgsCsException &cse )
1298  {
1299  Q_UNUSED( cse )
1300  }
1301 
1302  QDomElement geomElem = doc.createElement( QStringLiteral( "qgs:geometry" ) );
1303  QDomElement gmlElem;
1304  if ( params.geometryName == QLatin1String( "EXTENT" ) )
1305  {
1307  gmlElem = QgsOgcUtils::geometryToGML( bbox, doc, prec );
1308  }
1309  else if ( params.geometryName == QLatin1String( "CENTROID" ) )
1310  {
1311  QgsGeometry centroid = geom.centroid();
1312  gmlElem = QgsOgcUtils::geometryToGML( centroid, doc, prec );
1313  }
1314  else
1315  {
1316  const QgsAbstractGeometry *abstractGeom = geom.constGet();
1317  if ( abstractGeom )
1318  {
1319  gmlElem = abstractGeom->asGml2( doc, prec, "http://www.opengis.net/gml" );
1320  }
1321  }
1322 
1323  if ( !gmlElem.isNull() )
1324  {
1325  QgsRectangle box = geom.boundingBox();
1326  QDomElement bbElem = doc.createElement( QStringLiteral( "gml:boundedBy" ) );
1327  QDomElement boxElem = QgsOgcUtils::rectangleToGMLBox( &box, doc, prec );
1328 
1329  if ( crs.isValid() )
1330  {
1331  boxElem.setAttribute( QStringLiteral( "srsName" ), crs.authid() );
1332  gmlElem.setAttribute( QStringLiteral( "srsName" ), crs.authid() );
1333  }
1334 
1335  bbElem.appendChild( boxElem );
1336  typeNameElement.appendChild( bbElem );
1337 
1338  geomElem.appendChild( gmlElem );
1339  typeNameElement.appendChild( geomElem );
1340  }
1341  }
1342 
1343  //read all attribute values from the feature
1344  QgsAttributes featureAttributes = feature.attributes();
1345  QgsFields fields = feature.fields();
1346  for ( int i = 0; i < params.attributeIndexes.count(); ++i )
1347  {
1348  int idx = params.attributeIndexes[i];
1349  if ( idx >= fields.count() )
1350  {
1351  continue;
1352  }
1353  const QgsField field = fields.at( idx );
1354  const QgsEditorWidgetSetup setup = field.editorWidgetSetup();
1355  QString attributeName = field.name();
1356 
1357  QDomElement fieldElem = doc.createElement( "qgs:" + attributeName.replace( ' ', '_' ).replace( cleanTagNameRegExp, QString() ) );
1358  QDomText fieldText = doc.createTextNode( encodeValueToText( featureAttributes[idx], setup ) );
1359  if ( featureAttributes[idx].isNull() )
1360  {
1361  fieldElem.setAttribute( QStringLiteral( "xsi:nil" ), QStringLiteral( "true" ) );
1362  }
1363  fieldElem.appendChild( fieldText );
1364  typeNameElement.appendChild( fieldElem );
1365  }
1366 
1367  return featureElement;
1368  }
1369 
1370  QDomElement createFeatureGML3( const QgsFeature &feature, QDomDocument &doc, const createFeatureParams &params, const QgsProject *project, const QgsAttributeList &pkAttributes )
1371  {
1372  //gml:FeatureMember
1373  QDomElement featureElement = doc.createElement( QStringLiteral( "gml:featureMember" )/*wfs:FeatureMember*/ );
1374 
1375  //qgs:%TYPENAME%
1376  QDomElement typeNameElement = doc.createElement( "qgs:" + params.typeName /*qgs:%TYPENAME%*/ );
1377  QString id = QStringLiteral( "%1.%2" ).arg( params.typeName, QgsServerFeatureId::getServerFid( feature, pkAttributes ) );
1378  typeNameElement.setAttribute( QStringLiteral( "gml:id" ), id );
1379  featureElement.appendChild( typeNameElement );
1380 
1381  //add geometry column (as gml)
1382  QgsGeometry geom = feature.geometry();
1383  if ( !geom.isNull() && params.withGeom && params.geometryName != QLatin1String( "NONE" ) )
1384  {
1385  int prec = params.precision;
1386  QgsCoordinateReferenceSystem crs = params.crs;
1387  QgsCoordinateTransform mTransform( crs, params.outputCrs, project );
1388  try
1389  {
1390  QgsGeometry transformed = geom;
1391  if ( transformed.transform( mTransform ) == 0 )
1392  {
1393  geom = transformed;
1394  crs = params.outputCrs;
1395  if ( crs.isGeographic() && !params.crs.isGeographic() )
1396  prec = std::min( params.precision + 3, 6 );
1397  }
1398  }
1399  catch ( QgsCsException &cse )
1400  {
1401  Q_UNUSED( cse )
1402  }
1403 
1404  QDomElement geomElem = doc.createElement( QStringLiteral( "qgs:geometry" ) );
1405  QDomElement gmlElem;
1406  if ( params.geometryName == QLatin1String( "EXTENT" ) )
1407  {
1409  gmlElem = QgsOgcUtils::geometryToGML( bbox, doc, QStringLiteral( "GML3" ), prec );
1410  }
1411  else if ( params.geometryName == QLatin1String( "CENTROID" ) )
1412  {
1413  QgsGeometry centroid = geom.centroid();
1414  gmlElem = QgsOgcUtils::geometryToGML( centroid, doc, QStringLiteral( "GML3" ), prec );
1415  }
1416  else
1417  {
1418  const QgsAbstractGeometry *abstractGeom = geom.constGet();
1419  if ( abstractGeom )
1420  {
1421  gmlElem = abstractGeom->asGml3( doc, prec, "http://www.opengis.net/gml" );
1422  }
1423  }
1424 
1425  if ( !gmlElem.isNull() )
1426  {
1427  QgsRectangle box = geom.boundingBox();
1428  QDomElement bbElem = doc.createElement( QStringLiteral( "gml:boundedBy" ) );
1429  QDomElement boxElem = QgsOgcUtils::rectangleToGMLEnvelope( &box, doc, prec );
1430 
1431  if ( crs.isValid() )
1432  {
1433  boxElem.setAttribute( QStringLiteral( "srsName" ), crs.authid() );
1434  gmlElem.setAttribute( QStringLiteral( "srsName" ), crs.authid() );
1435  }
1436 
1437  bbElem.appendChild( boxElem );
1438  typeNameElement.appendChild( bbElem );
1439 
1440  geomElem.appendChild( gmlElem );
1441  typeNameElement.appendChild( geomElem );
1442  }
1443  }
1444 
1445  //read all attribute values from the feature
1446  QgsAttributes featureAttributes = feature.attributes();
1447  QgsFields fields = feature.fields();
1448  for ( int i = 0; i < params.attributeIndexes.count(); ++i )
1449  {
1450  int idx = params.attributeIndexes[i];
1451  if ( idx >= fields.count() )
1452  {
1453  continue;
1454  }
1455 
1456  const QgsField field = fields.at( idx );
1457  const QgsEditorWidgetSetup setup = field.editorWidgetSetup();
1458 
1459  QString attributeName = field.name();
1460 
1461  QDomElement fieldElem = doc.createElement( "qgs:" + attributeName.replace( ' ', '_' ).replace( cleanTagNameRegExp, QString() ) );
1462  QDomText fieldText = doc.createTextNode( encodeValueToText( featureAttributes[idx], setup ) );
1463  if ( featureAttributes[idx].isNull() )
1464  {
1465  fieldElem.setAttribute( QStringLiteral( "xsi:nil" ), QStringLiteral( "true" ) );
1466  }
1467  fieldElem.appendChild( fieldText );
1468  typeNameElement.appendChild( fieldElem );
1469  }
1470 
1471  return featureElement;
1472  }
1473 
1474  QString encodeValueToText( const QVariant &value, const QgsEditorWidgetSetup &setup )
1475  {
1476  if ( value.isNull() )
1477  return QString();
1478 
1479  if ( setup.type() == QStringLiteral( "DateTime" ) )
1480  {
1481  QgsDateTimeFieldFormatter fieldFormatter;
1482  const QVariantMap config = setup.config();
1483  const QString fieldFormat = config.value( QStringLiteral( "field_format" ), fieldFormatter.defaultFormat( value.type() ) ).toString();
1484  QDateTime date = value.toDateTime();
1485 
1486  if ( date.isValid() )
1487  {
1488  return date.toString( fieldFormat );
1489  }
1490  }
1491  else if ( setup.type() == QStringLiteral( "Range" ) )
1492  {
1493  const QVariantMap config = setup.config();
1494  if ( config.contains( QStringLiteral( "Precision" ) ) )
1495  {
1496  // if precision is defined, use it
1497  bool ok;
1498  int precision( config[ QStringLiteral( "Precision" ) ].toInt( &ok ) );
1499  if ( ok )
1500  return QString::number( value.toDouble(), 'f', precision );
1501  }
1502  }
1503 
1504  switch ( value.type() )
1505  {
1506  case QVariant::Int:
1507  case QVariant::UInt:
1508  case QVariant::LongLong:
1509  case QVariant::ULongLong:
1510  case QVariant::Double:
1511  return value.toString();
1512 
1513  case QVariant::Bool:
1514  return value.toBool() ? QStringLiteral( "true" ) : QStringLiteral( "false" );
1515 
1516  case QVariant::StringList:
1517  case QVariant::List:
1518  case QVariant::Map:
1519  {
1520  QString v = QgsJsonUtils::encodeValue( value );
1521 
1522  //do we need CDATA
1523  if ( v.indexOf( '<' ) != -1 || v.indexOf( '&' ) != -1 )
1524  v.prepend( QStringLiteral( "<![CDATA[" ) ).append( QStringLiteral( "]]>" ) );
1525 
1526  return v;
1527  }
1528 
1529  default:
1530  case QVariant::String:
1531  {
1532  QString v = value.toString();
1533 
1534  //do we need CDATA
1535  if ( v.indexOf( '<' ) != -1 || v.indexOf( '&' ) != -1 )
1536  v.prepend( QStringLiteral( "<![CDATA[" ) ).append( QStringLiteral( "]]>" ) );
1537 
1538  return v;
1539  }
1540  }
1541  }
1542 
1543 
1544  } // namespace
1545 
1546 } // namespace QgsWfs
1547 
1548 
1549 
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:324
void writeGetFeature(QgsServerInterface *serverIface, const QgsProject *project, const QString &version, const QgsServerRequest &request, QgsServerResponse &response)
Output WFS GetFeature response.
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:183
Class for parsing and evaluation of expressions (formerly called "search strings").
QgsStringMap attributeAliases() const
Returns a map of field name to attribute alias.
Wrapper for iterator of features from vector data provider or vector layer.
virtual void setHeader(const QString &key, const QString &value)=0
Set Header entry Add Header entry to the response Note that it is usually an error to set Header afte...
int precision
A rectangle specified with double values.
Definition: qgsrectangle.h:41
Base class for all map layer types.
Definition: qgsmaplayer.h:79
QgsFeatureRequest featureRequest
QVariantMap config() const
QgsMapLayerType type() const
Returns the type of the layer.
const Flags & flags() const
QString name
Definition: qgsfield.h:58
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
getFeatureRequest parseGetFeatureRequestBody(QDomElement &docElem, const QgsProject *project)
Transform RequestBody root element to getFeatureRequest.
const QString QGS_NAMESPACE
Definition: qgswfsutils.h:74
QSet< QString > excludeAttributesWfs() const
A set of attributes that are not advertised in WFS requests with QGIS server.
SERVER_EXPORT QString getServerFid(const QgsFeature &feature, const QgsAttributeList &pkAttributes)
Returns the feature id based on primary keys.
QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
virtual QgsAttributeList pkAttributeIndexes() const
Returns list of indexes of fields that make up the primary key.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
Container of fields for a vector layer.
Definition: qgsfields.h:42
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:122
QgsFeatureRequest & addOrderBy(const QString &expression, bool ascending=true)
Adds a new OrderByClause, appending it as the least important one.
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:438
QgsGeometry centroid() const
Returns the center of mass of a geometry.
static QDomElement rectangleToGMLBox(QgsRectangle *box, QDomDocument &doc, int precision=17)
Exports the rectangle to GML2 Box.
virtual QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml", AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const =0
Returns a GML2 representation of the geometry.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
const QgsCoordinateReferenceSystem & crs
QgsFields fields
Definition: qgsfeature.h:66
const QgsAttributeList & attributeIndexes
void parseSortByElement(QDomElement &sortByElem, QgsFeatureRequest &featureRequest, const QString &typeName)
Add SortBy element to featureRequest.
virtual void write(const QString &data)
Write string This is a convenient method that will write directly to the underlying I/O device...
const QgsRectangle & filterRect() const
Returns the rectangle from which features will be taken.
bool withGeom
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QMap< QString, QString > QgsStringMap
Definition: qgis.h:612
QString layerTypeName(const QgsMapLayer *layer)
Returns typename from vector layer.
Definition: qgswfsutils.cpp:71
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
static QString defaultFormat(QVariant::Type type)
Gets the default format in function of the type.
static void applyAccessControlLayerFilters(const QgsAccessControl *accessControl, QgsMapLayer *mapLayer, QHash< QgsMapLayer *, QString > &originalLayerFilters)
Apply filter from AccessControl.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
QString serviceUrl(const QgsServerRequest &request, const QgsProject *project)
Service URL string.
Definition: qgswfsutils.cpp:37
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination coordinate reference system.
static QString encodeValue(const QVariant &value)
Encodes a value to a JSON string representation, adding appropriate quotations and escaping where req...
Exception thrown in case of malformed request.
const QString & geometryName
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:426
getFeatureQuery parseQueryElement(QDomElement &queryElem, const QgsProject *project)
Transform Query element to getFeatureQuery.
const QString & typeName
WMS implementation.
Definition: qgswfs.cpp:35
QgsServerRequest::Parameters parameters() const
Returns a map of query parameters with keys converted to uppercase.
A class to describe the version of a project.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:240
QgsWfsParameters::Format outputFormat
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
Definition: qgsrectangle.h:312
const QString GML_NAMESPACE
Definition: qgswfsutils.h:72
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QList< getFeatureQuery > queries
const QString WFS_NAMESPACE
Definition: qgswfsutils.h:71
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.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc.
Definition: qgsproject.h:89
QString implementationVersion()
Returns the highest version supported by this implementation.
Definition: qgswfsutils.cpp:32
QgsAttributeList attributeList() const
Returns list of attribute indexes.
Format
Output format for the response.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
Abstract base class for all geometries.
SERVER_EXPORT QgsFeatureRequest updateFeatureRequestFromServerFids(QgsFeatureRequest &featureRequest, const QStringList &serverFids, const QgsVectorDataProvider *provider)
Returns the feature request based on feature ids build with primary keys.
SERVER_EXPORT int wfsLayerPrecision(const QgsProject &project, const QString &layerId)
Returns the Layer precision defined in a QGIS project for the WFS GetFeature.
Handles exporting QgsFeature features to GeoJSON features.
Definition: qgsjsonutils.h:45
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
const QString OGC_NAMESPACE
Definition: qgswfsutils.h:73
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle...
Definition: qgsrectangle.h:359
QgsServerRequest Class defining request interface passed to services QgsService::executeRequest() met...
const QRegExp cleanTagNameRegExp("(?![\\\-]).")
bool layerReadPermission(const QgsMapLayer *layer) const
Returns the layer read right.
QgsServerInterface Class defining interfaces exposed by QGIS Server and made available to plugins...
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:207
Holder for the widget type and its configuration for a field.
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.
This class represents a coordinate reference system (CRS).
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
Field formatter for a date time field.
static QDomElement geometryToGML(const QgsGeometry &geometry, QDomDocument &doc, QgsOgcUtils::GMLVersion gmlVersion, const QString &srsName, bool invertAxisOrientation, const QString &gmlIdBase, int precision=17)
Exports the geometry to GML.
QgsFeatureRequest & setLimit(long limit)
Set the maximum number of features to request.
Class for doing transforms between two map coordinate systems.
static QDomElement rectangleToGMLEnvelope(QgsRectangle *env, QDomDocument &doc, int precision=17)
Exports the rectangle to GML3 Envelope.
const QgsCoordinateReferenceSystem & outputCrs
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
RAII class to restore layer filters on destruction.
A helper class that centralizes restrictions given by all the access control filter plugins...
void filterFeatures(const QgsVectorLayer *layer, QgsFeatureRequest &filterFeatures) const override
Filter the features of the layer.
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
Exception thrown when data access violates access controls.
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsServerResponse Class defining response interface passed to services QgsService::executeRequest() m...
QgsGeometry geometry
Definition: qgsfeature.h:67
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
virtual QgsAccessControl * accessControls() const =0
Gets the registered access control filters.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer&#39;s data provider, it may be nullptr.
QList< int > QgsAttributeList
Definition: qgsfield.h:27
getFeatureRequest parseGetFeatureParameters(const QgsProject *project)
Transform parameters to getFeatureRequest.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool nextFeature(QgsFeature &f)
This is the base class for vector data providers.
Geometry is not required. It may still be returned if e.g. required for a filter condition.
A vector of attributes.
Definition: qgsattributes.h:57
Represents a vector layer which manages a vector based data sets.
QStringList layerAttributes(const QgsVectorLayer *layer, const QStringList &attributes) const
Returns the authorized layer attributes.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs)
Sets the source coordinate reference system.
Defines a QGIS exception class.
Definition: qgsexception.h:34
QgsField field(int fieldIdx) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:168
Provides an interface to retrieve and manipulate WFS parameters received from the client...
QString authid() const
Returns the authority identifier for the CRS.
QgsAttributes attributes
Definition: qgsfeature.h:65
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:86
virtual QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml", AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const =0
Returns a GML3 representation of the geometry.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QMap< QString, QString > Parameters
virtual void flush() SIP_THROW(QgsServerException)
Flushes the current output buffer to the network.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.