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