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