QGIS API Documentation  3.27.0-Master (e113457133)
qgswfsdescribefeaturetype.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgswfsdescribefeaturetype.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"
25 #include "qgswfsparameters.h"
26 
27 #include "qgsproject.h"
28 #include "qgsvectorlayer.h"
30 
31 namespace QgsWfs
32 {
33 
34  void writeDescribeFeatureType( QgsServerInterface *serverIface, const QgsProject *project, const QString &version,
35  const QgsServerRequest &request, QgsServerResponse &response )
36  {
37 #ifdef HAVE_SERVER_PYTHON_PLUGINS
38  QgsAccessControl *accessControl = serverIface->accessControls();
39 #endif
40  QDomDocument doc;
41  const QDomDocument *describeDocument = nullptr;
42 
43 #ifdef HAVE_SERVER_PYTHON_PLUGINS
44  QgsServerCacheManager *cacheManager = serverIface->cacheManager();
45  if ( cacheManager && cacheManager->getCachedDocument( &doc, project, request, accessControl ) )
46  {
47  describeDocument = &doc;
48  }
49  else //describe feature xml not in cache. Create a new one
50  {
51  doc = createDescribeFeatureTypeDocument( serverIface, project, version, request );
52 
53  if ( cacheManager )
54  {
55  cacheManager->setCachedDocument( &doc, project, request, accessControl );
56  }
57  describeDocument = &doc;
58  }
59 #else
60  doc = createDescribeFeatureTypeDocument( serverIface, project, version, request );
61  describeDocument = &doc;
62 #endif
63  response.setHeader( "Content-Type", "text/xml; charset=utf-8" );
64  response.write( describeDocument->toByteArray() );
65  }
66 
67 
68  QDomDocument createDescribeFeatureTypeDocument( QgsServerInterface *serverIface, const QgsProject *project, const QString &version,
69  const QgsServerRequest &request )
70  {
71  Q_UNUSED( version )
72 
73  QDomDocument doc;
74 
75  const QgsWfsParameters wfsParameters( QUrlQuery( request.url() ) );
76  const QgsWfsParameters::Format oFormat = wfsParameters.outputFormat();
77 
78  // test oFormat
79  if ( oFormat == QgsWfsParameters::Format::NONE )
80  throw QgsBadRequestException( QStringLiteral( "Invalid WFS Parameter" ),
81  QStringLiteral( "OUTPUTFORMAT %1 is not supported" ).arg( wfsParameters.outputFormatAsString() ) );
82 
83 #ifdef HAVE_SERVER_PYTHON_PLUGINS
84  QgsAccessControl *accessControl = serverIface->accessControls();
85 #else
86  ( void )serverIface;
87 #endif
88 
89  //xsd:schema
90  QDomElement schemaElement = doc.createElement( QStringLiteral( "schema" )/*xsd:schema*/ );
91  schemaElement.setAttribute( QStringLiteral( "xmlns" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema" ) );
92  schemaElement.setAttribute( QStringLiteral( "xmlns:xsd" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema" ) );
93  schemaElement.setAttribute( QStringLiteral( "xmlns:ogc" ), OGC_NAMESPACE );
94  schemaElement.setAttribute( QStringLiteral( "xmlns:gml" ), GML_NAMESPACE );
95  schemaElement.setAttribute( QStringLiteral( "xmlns:qgs" ), QGS_NAMESPACE );
96  schemaElement.setAttribute( QStringLiteral( "targetNamespace" ), QGS_NAMESPACE );
97  schemaElement.setAttribute( QStringLiteral( "elementFormDefault" ), QStringLiteral( "qualified" ) );
98  schemaElement.setAttribute( QStringLiteral( "version" ), QStringLiteral( "1.0" ) );
99  doc.appendChild( schemaElement );
100 
101  //xsd:import
102  QDomElement importElement = doc.createElement( QStringLiteral( "import" )/*xsd:import*/ );
103  importElement.setAttribute( QStringLiteral( "namespace" ), GML_NAMESPACE );
104  if ( oFormat == QgsWfsParameters::Format::GML2 )
105  importElement.setAttribute( QStringLiteral( "schemaLocation" ), QStringLiteral( "http://schemas.opengis.net/gml/2.1.2/feature.xsd" ) );
106  else if ( oFormat == QgsWfsParameters::Format::GML3 )
107  importElement.setAttribute( QStringLiteral( "schemaLocation" ), QStringLiteral( "http://schemas.opengis.net/gml/3.1.1/base/gml.xsd" ) );
108  schemaElement.appendChild( importElement );
109 
110  QStringList typeNameList;
111  QDomDocument queryDoc;
112  QString errorMsg;
113  if ( queryDoc.setContent( request.data(), true, &errorMsg ) )
114  {
115  //read doc
116  const QDomElement queryDocElem = queryDoc.documentElement();
117  const QDomNodeList docChildNodes = queryDocElem.childNodes();
118  if ( docChildNodes.size() )
119  {
120  for ( int i = 0; i < docChildNodes.size(); i++ )
121  {
122  const QDomElement docChildElem = docChildNodes.at( i ).toElement();
123  if ( docChildElem.tagName() == QLatin1String( "TypeName" ) )
124  {
125  const QString typeName = docChildElem.text().trimmed();
126  if ( typeName.contains( ':' ) )
127  typeNameList << typeName.section( ':', 1, 1 );
128  else
129  typeNameList << typeName;
130  }
131  }
132  }
133  }
134  else
135  {
136  typeNameList = wfsParameters.typeNames();
137  }
138 
139  const QStringList wfsLayerIds = QgsServerProjectUtils::wfsLayerIds( *project );
140  for ( int i = 0; i < wfsLayerIds.size(); ++i )
141  {
142  QgsMapLayer *layer = project->mapLayer( wfsLayerIds.at( i ) );
143  if ( !layer )
144  {
145  continue;
146  }
147 
148  const QString name = layerTypeName( layer );
149 
150  if ( !typeNameList.isEmpty() && !typeNameList.contains( name ) )
151  {
152  continue;
153  }
154 #ifdef HAVE_SERVER_PYTHON_PLUGINS
155  if ( accessControl && !accessControl->layerReadPermission( layer ) )
156  {
157  if ( !typeNameList.isEmpty() )
158  {
159  throw QgsSecurityAccessException( QStringLiteral( "Feature access permission denied" ) );
160  }
161  else
162  {
163  continue;
164  }
165  }
166 #endif
167  QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( layer );
168  QgsVectorDataProvider *provider = vLayer->dataProvider();
169  if ( !provider )
170  {
171  continue;
172  }
173  setSchemaLayer( schemaElement, doc, const_cast<QgsVectorLayer *>( vLayer ), oFormat );
174  }
175  return doc;
176  }
177 
178  void setSchemaLayer( QDomElement &parentElement, QDomDocument &doc, const QgsVectorLayer *layer, QgsWfsParameters::Format format )
179  {
180  const QgsVectorDataProvider *provider = layer->dataProvider();
181  if ( !provider )
182  {
183  return;
184  }
185 
186  const QString typeName = layerTypeName( layer );
187 
188  //xsd:element
189  QDomElement elementElem = doc.createElement( QStringLiteral( "element" )/*xsd:element*/ );
190  elementElem.setAttribute( QStringLiteral( "name" ), typeName );
191  elementElem.setAttribute( QStringLiteral( "type" ), "qgs:" + typeName + "Type" );
192  elementElem.setAttribute( QStringLiteral( "substitutionGroup" ), QStringLiteral( "gml:_Feature" ) );
193  parentElement.appendChild( elementElem );
194 
195  //xsd:complexType
196  QDomElement complexTypeElem = doc.createElement( QStringLiteral( "complexType" )/*xsd:complexType*/ );
197  complexTypeElem.setAttribute( QStringLiteral( "name" ), typeName + "Type" );
198  parentElement.appendChild( complexTypeElem );
199 
200  //xsd:complexType
201  QDomElement complexContentElem = doc.createElement( QStringLiteral( "complexContent" )/*xsd:complexContent*/ );
202  complexTypeElem.appendChild( complexContentElem );
203 
204  //xsd:extension
205  QDomElement extensionElem = doc.createElement( QStringLiteral( "extension" )/*xsd:extension*/ );
206  extensionElem.setAttribute( QStringLiteral( "base" ), QStringLiteral( "gml:AbstractFeatureType" ) );
207  complexContentElem.appendChild( extensionElem );
208 
209  //xsd:sequence
210  QDomElement sequenceElem = doc.createElement( QStringLiteral( "sequence" )/*xsd:sequence*/ );
211  extensionElem.appendChild( sequenceElem );
212 
213  //xsd:element
214  if ( layer->isSpatial() )
215  {
216  QDomElement geomElem = doc.createElement( QStringLiteral( "element" )/*xsd:element*/ );
217  geomElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "geometry" ) );
218  geomElem.setAttribute( QStringLiteral( "type" ), getGmlGeometryType( layer, format ) );
219  geomElem.setAttribute( QStringLiteral( "minOccurs" ), QStringLiteral( "0" ) );
220  geomElem.setAttribute( QStringLiteral( "maxOccurs" ), QStringLiteral( "1" ) );
221  sequenceElem.appendChild( geomElem );
222  }
223 
224  //Attributes
225  const QgsFields fields = layer->fields();
226  //hidden attributes for this layer
227  for ( int idx = 0; idx < fields.count(); ++idx )
228  {
229  const QgsField field = fields.at( idx );
230  QString attributeName = field.name();
231  //skip attribute if excluded from WFS publication
233  {
234  continue;
235  }
236 
237  //xsd:element
238  QDomElement attElem = doc.createElement( QStringLiteral( "element" )/*xsd:element*/ );
239  attElem.setAttribute( QStringLiteral( "name" ), attributeName.replace( ' ', '_' ).replace( cleanTagNameRegExp, QString() ) );
240  const QVariant::Type attributeType = field.type();
241  if ( attributeType == QVariant::Int )
242  {
243  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "int" ) );
244  }
245  else if ( attributeType == QVariant::UInt )
246  {
247  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "unsignedInt" ) );
248  }
249  else if ( attributeType == QVariant::LongLong )
250  {
251  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "long" ) );
252  }
253  else if ( attributeType == QVariant::ULongLong )
254  {
255  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "unsignedLong" ) );
256  }
257  else if ( attributeType == QVariant::Double )
258  {
259  if ( field.length() > 0 && field.precision() == 0 )
260  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "integer" ) );
261  else
262  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "decimal" ) );
263  }
264  else if ( attributeType == QVariant::Bool )
265  {
266  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "boolean" ) );
267  }
268  else if ( attributeType == QVariant::Date )
269  {
270  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "date" ) );
271  }
272  else if ( attributeType == QVariant::Time )
273  {
274  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "time" ) );
275  }
276  else if ( attributeType == QVariant::DateTime )
277  {
278  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "dateTime" ) );
279  }
280  else
281  {
282  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "string" ) );
283  }
284 
286  if ( setup.type() == QStringLiteral( "DateTime" ) )
287  {
288  const QVariantMap config = setup.config();
289  const QString fieldFormat = config.value( QStringLiteral( "field_format" ), QgsDateTimeFieldFormatter::defaultFormat( field.type() ) ).toString();
290  if ( fieldFormat == QLatin1String( "yyyy-MM-dd" ) )
291  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "date" ) );
292  else if ( fieldFormat == QLatin1String( "HH:mm:ss" ) )
293  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "time" ) );
294  else
295  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "dateTime" ) );
296  }
297  else if ( setup.type() == QStringLiteral( "Range" ) )
298  {
299  const QVariantMap config = setup.config();
300  if ( config.contains( QStringLiteral( "Precision" ) ) )
301  {
302  // if precision in range config is not the same as the attributePrec
303  // we need to update type
304  bool ok;
305  const int configPrec( config[ QStringLiteral( "Precision" ) ].toInt( &ok ) );
306  if ( ok && configPrec != field.precision() )
307  {
308  if ( configPrec == 0 )
309  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "integer" ) );
310  else
311  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "decimal" ) );
312  }
313  }
314  }
315 
316  if ( !( field.constraints().constraints() & QgsFieldConstraints::Constraint::ConstraintNotNull ) )
317  {
318  attElem.setAttribute( QStringLiteral( "nillable" ), QStringLiteral( "true" ) );
319  }
320 
321  sequenceElem.appendChild( attElem );
322 
323  const QString alias = field.alias();
324  if ( !alias.isEmpty() )
325  {
326  attElem.setAttribute( QStringLiteral( "alias" ), alias );
327  }
328  }
329  }
330 
332  {
333  const QgsWkbTypes::Type wkbType = layer->wkbType();
334  switch ( format )
335  {
336  case QgsWfsParameters::Format::GML2:
337  switch ( wkbType )
338  {
340  case QgsWkbTypes::Point:
341  return QStringLiteral( "gml:PointPropertyType" );
342 
345  return QStringLiteral( "gml:LineStringPropertyType" );
346 
349  return QStringLiteral( "gml:PolygonPropertyType" );
350 
353  return QStringLiteral( "gml:MultiPointPropertyType" );
354 
358  return QStringLiteral( "gml:MultiLineStringPropertyType" );
359 
363  return QStringLiteral( "gml:MultiPolygonPropertyType" );
364 
365  default:
366  return QStringLiteral( "gml:GeometryPropertyType" );
367 
368  }
369  case QgsWfsParameters::Format::GML3:
370  switch ( wkbType )
371  {
373  case QgsWkbTypes::Point:
374  return QStringLiteral( "gml:PointPropertyType" );
375 
378  return QStringLiteral( "gml:LineStringPropertyType" );
379 
382  return QStringLiteral( "gml:PolygonPropertyType" );
383 
386  return QStringLiteral( "gml:MultiPointPropertyType" );
387 
391  return QStringLiteral( "gml:MultiCurvePropertyType" );
392 
396  return QStringLiteral( "gml:MultiSurfacePropertyType" );
397 
398  default:
399  return QStringLiteral( "gml:GeometryPropertyType" );
400  }
401  default:
402  return QStringLiteral( "gml:GeometryPropertyType" );
403  }
404  }
405 
406 } // namespace QgsWfs
A helper class that centralizes restrictions given by all the access control filter plugins.
bool layerReadPermission(const QgsMapLayer *layer) const
Returns the layer read right.
static QString defaultFormat(QVariant::Type type)
Gets the default format in function of the type.
Holder for the widget type and its configuration for a field.
QVariantMap config() const
Q_GADGET Constraints constraints
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString name
Definition: qgsfield.h:60
int precision
Definition: qgsfield.h:57
int length
Definition: qgsfield.h:56
ConfigurationFlags configurationFlags
Definition: qgsfield.h:64
QVariant::Type type
Definition: qgsfield.h:58
QString alias
Definition: qgsfield.h:61
QgsFieldConstraints constraints
Definition: qgsfield.h:63
@ HideFromWfs
Field is not available if layer is served as WFS from QGIS server.
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:602
Container of fields for a vector layer.
Definition: qgsfields.h:45
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
Base class for all map layer types.
Definition: qgsmaplayer.h:73
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:104
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
A helper class that centralizes caches accesses given by all the server cache filter plugins.
bool setCachedDocument(const QDomDocument *doc, const QgsProject *project, const QgsServerRequest &request, QgsAccessControl *accessControl) const
Updates or inserts the document in cache like capabilities.
bool getCachedDocument(QDomDocument *doc, const QgsProject *project, const QgsServerRequest &request, QgsAccessControl *accessControl) const
Returns cached document (or 0 if document not in cache) like capabilities.
QgsServerInterface Class defining interfaces exposed by QGIS Server and made available to plugins.
virtual QgsAccessControl * accessControls() const =0
Gets the registered access control filters.
virtual QgsServerCacheManager * cacheManager() const =0
Gets the registered server cache filters.
QgsServerRequest Class defining request interface passed to services QgsService::executeRequest() met...
virtual QByteArray data() const
Returns post/put data Check for QByteArray::isNull() to check if data is available.
QgsServerResponse Class defining response interface passed to services QgsService::executeRequest() m...
virtual void write(const QString &data)
Write string This is a convenient method that will write directly to the underlying I/O device.
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...
This is the base class for vector data providers.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
Exception thrown in case of malformed request.
Exception thrown when data access violates access controls.
Provides an interface to retrieve and manipulate WFS parameters received from the client.
QStringList typeNames() const
Returns TYPENAME parameter as list.
QString outputFormatAsString() const
Returns OUTPUTFORMAT parameter as a string.
Format
Output format for the response.
Format outputFormat() const
Returns format.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
@ MultiLineString25D
Definition: qgswkbtypes.h:129
SERVER_EXPORT QStringList wfsLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published in WFS.
WMS implementation.
Definition: qgswfs.cpp:36
QDomDocument createDescribeFeatureTypeDocument(QgsServerInterface *serverIface, const QgsProject *project, const QString &version, const QgsServerRequest &request)
Create get capabilities document.
QString layerTypeName(const QgsMapLayer *layer)
Returns typename from vector layer.
Definition: qgswfsutils.cpp:69
const QString OGC_NAMESPACE
Definition: qgswfsutils.h:74
const QString GML_NAMESPACE
Definition: qgswfsutils.h:73
void setSchemaLayer(QDomElement &parentElement, QDomDocument &doc, const QgsVectorLayer *layer, QgsWfsParameters::Format format)
QString getGmlGeometryType(const QgsVectorLayer *layer, QgsWfsParameters::Format format)
Returns the GML geometry type.
const QString QGS_NAMESPACE
Definition: qgswfsutils.h:75
void writeDescribeFeatureType(QgsServerInterface *serverIface, const QgsProject *project, const QString &version, const QgsServerRequest &request, QgsServerResponse &response)
Output WFS GetCapabilities response.
const QRegExp cleanTagNameRegExp("(?![\\w\\d\\.-]).")
const QgsField & field
Definition: qgsfield.h:463
const QString & typeName