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