QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 QgsServerRequest::Parameters parameters = request.parameters();
76  const QgsWfsParameters wfsParameters( QUrlQuery( request.url() ) );
77  const QgsWfsParameters::Format oFormat = wfsParameters.outputFormat();
78 
79  // test oFormat
80  if ( oFormat == QgsWfsParameters::Format::NONE )
81  throw QgsBadRequestException( QStringLiteral( "Invalid WFS Parameter" ),
82  QStringLiteral( "OUTPUTFORMAT %1 is not supported" ).arg( wfsParameters.outputFormatAsString() ) );
83 
84 #ifdef HAVE_SERVER_PYTHON_PLUGINS
85  QgsAccessControl *accessControl = serverIface->accessControls();
86 #else
87  ( void )serverIface;
88 #endif
89 
90  //xsd:schema
91  QDomElement schemaElement = doc.createElement( QStringLiteral( "schema" )/*xsd:schema*/ );
92  schemaElement.setAttribute( QStringLiteral( "xmlns" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema" ) );
93  schemaElement.setAttribute( QStringLiteral( "xmlns:xsd" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema" ) );
94  schemaElement.setAttribute( QStringLiteral( "xmlns:ogc" ), OGC_NAMESPACE );
95  schemaElement.setAttribute( QStringLiteral( "xmlns:gml" ), GML_NAMESPACE );
96  schemaElement.setAttribute( QStringLiteral( "xmlns:qgs" ), QGS_NAMESPACE );
97  schemaElement.setAttribute( QStringLiteral( "targetNamespace" ), QGS_NAMESPACE );
98  schemaElement.setAttribute( QStringLiteral( "elementFormDefault" ), QStringLiteral( "qualified" ) );
99  schemaElement.setAttribute( QStringLiteral( "version" ), QStringLiteral( "1.0" ) );
100  doc.appendChild( schemaElement );
101 
102  //xsd:import
103  QDomElement importElement = doc.createElement( QStringLiteral( "import" )/*xsd:import*/ );
104  importElement.setAttribute( QStringLiteral( "namespace" ), GML_NAMESPACE );
105  if ( oFormat == QgsWfsParameters::Format::GML2 )
106  importElement.setAttribute( QStringLiteral( "schemaLocation" ), QStringLiteral( "http://schemas.opengis.net/gml/2.1.2/feature.xsd" ) );
107  else if ( oFormat == QgsWfsParameters::Format::GML3 )
108  importElement.setAttribute( QStringLiteral( "schemaLocation" ), QStringLiteral( "http://schemas.opengis.net/gml/3.1.1/base/gml.xsd" ) );
109  schemaElement.appendChild( importElement );
110 
111  QStringList typeNameList;
112  QDomDocument queryDoc;
113  QString errorMsg;
114  if ( queryDoc.setContent( request.data(), true, &errorMsg ) )
115  {
116  //read doc
117  const QDomElement queryDocElem = queryDoc.documentElement();
118  const QDomNodeList docChildNodes = queryDocElem.childNodes();
119  if ( docChildNodes.size() )
120  {
121  for ( int i = 0; i < docChildNodes.size(); i++ )
122  {
123  const QDomElement docChildElem = docChildNodes.at( i ).toElement();
124  if ( docChildElem.tagName() == QLatin1String( "TypeName" ) )
125  {
126  const QString typeName = docChildElem.text().trimmed();
127  if ( typeName.contains( ':' ) )
128  typeNameList << typeName.section( ':', 1, 1 );
129  else
130  typeNameList << typeName;
131  }
132  }
133  }
134  }
135  else
136  {
137  typeNameList = wfsParameters.typeNames();
138  }
139 
140  const QStringList wfsLayerIds = QgsServerProjectUtils::wfsLayerIds( *project );
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 
149  const QString name = layerTypeName( layer );
150 
151  if ( !typeNameList.isEmpty() && !typeNameList.contains( name ) )
152  {
153  continue;
154  }
155 #ifdef HAVE_SERVER_PYTHON_PLUGINS
156  if ( accessControl && !accessControl->layerReadPermission( layer ) )
157  {
158  if ( !typeNameList.isEmpty() )
159  {
160  throw QgsSecurityAccessException( QStringLiteral( "Feature access permission denied" ) );
161  }
162  else
163  {
164  continue;
165  }
166  }
167 #endif
168  QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( layer );
169  QgsVectorDataProvider *provider = vLayer->dataProvider();
170  if ( !provider )
171  {
172  continue;
173  }
174  setSchemaLayer( schemaElement, doc, const_cast<QgsVectorLayer *>( vLayer ) );
175  }
176  return doc;
177  }
178 
179  void setSchemaLayer( QDomElement &parentElement, QDomDocument &doc, const QgsVectorLayer *layer )
180  {
181  const QgsVectorDataProvider *provider = layer->dataProvider();
182  if ( !provider )
183  {
184  return;
185  }
186 
187  const QString typeName = layerTypeName( layer );
188 
189  //xsd:element
190  QDomElement elementElem = doc.createElement( QStringLiteral( "element" )/*xsd:element*/ );
191  elementElem.setAttribute( QStringLiteral( "name" ), typeName );
192  elementElem.setAttribute( QStringLiteral( "type" ), "qgs:" + typeName + "Type" );
193  elementElem.setAttribute( QStringLiteral( "substitutionGroup" ), QStringLiteral( "gml:_Feature" ) );
194  parentElement.appendChild( elementElem );
195 
196  //xsd:complexType
197  QDomElement complexTypeElem = doc.createElement( QStringLiteral( "complexType" )/*xsd:complexType*/ );
198  complexTypeElem.setAttribute( QStringLiteral( "name" ), typeName + "Type" );
199  parentElement.appendChild( complexTypeElem );
200 
201  //xsd:complexType
202  QDomElement complexContentElem = doc.createElement( QStringLiteral( "complexContent" )/*xsd:complexContent*/ );
203  complexTypeElem.appendChild( complexContentElem );
204 
205  //xsd:extension
206  QDomElement extensionElem = doc.createElement( QStringLiteral( "extension" )/*xsd:extension*/ );
207  extensionElem.setAttribute( QStringLiteral( "base" ), QStringLiteral( "gml:AbstractFeatureType" ) );
208  complexContentElem.appendChild( extensionElem );
209 
210  //xsd:sequence
211  QDomElement sequenceElem = doc.createElement( QStringLiteral( "sequence" )/*xsd:sequence*/ );
212  extensionElem.appendChild( sequenceElem );
213 
214  //xsd:element
215  if ( layer->isSpatial() )
216  {
217  QDomElement geomElem = doc.createElement( QStringLiteral( "element" )/*xsd:element*/ );
218  geomElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "geometry" ) );
219 
220  const QgsWkbTypes::Type wkbType = layer->wkbType();
221  switch ( wkbType )
222  {
224  case QgsWkbTypes::Point:
225  geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:PointPropertyType" ) );
226  break;
229  geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:LineStringPropertyType" ) );
230  break;
233  geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:PolygonPropertyType" ) );
234  break;
237  geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:MultiPointPropertyType" ) );
238  break;
240  geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:MultiCurvePropertyType" ) );
241  break;
244  geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:MultiLineStringPropertyType" ) );
245  break;
247  geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:MultiSurfacePropertyType" ) );
248  break;
251  geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:MultiPolygonPropertyType" ) );
252  break;
253  default:
254  geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:GeometryPropertyType" ) );
255  break;
256  }
257  geomElem.setAttribute( QStringLiteral( "minOccurs" ), QStringLiteral( "0" ) );
258  geomElem.setAttribute( QStringLiteral( "maxOccurs" ), QStringLiteral( "1" ) );
259  sequenceElem.appendChild( geomElem );
260  }
261 
262  //Attributes
263  const QgsFields fields = layer->fields();
264  //hidden attributes for this layer
265  for ( int idx = 0; idx < fields.count(); ++idx )
266  {
267  const QgsField field = fields.at( idx );
268  QString attributeName = field.name();
269  //skip attribute if excluded from WFS publication
271  {
272  continue;
273  }
274 
275  //xsd:element
276  QDomElement attElem = doc.createElement( QStringLiteral( "element" )/*xsd:element*/ );
277  attElem.setAttribute( QStringLiteral( "name" ), attributeName.replace( ' ', '_' ).replace( cleanTagNameRegExp, QString() ) );
278  const QVariant::Type attributeType = field.type();
279  if ( attributeType == QVariant::Int )
280  {
281  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "int" ) );
282  }
283  else if ( attributeType == QVariant::UInt )
284  {
285  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "unsignedInt" ) );
286  }
287  else if ( attributeType == QVariant::LongLong )
288  {
289  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "long" ) );
290  }
291  else if ( attributeType == QVariant::ULongLong )
292  {
293  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "unsignedLong" ) );
294  }
295  else if ( attributeType == QVariant::Double )
296  {
297  if ( field.length() > 0 && field.precision() == 0 )
298  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "integer" ) );
299  else
300  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "decimal" ) );
301  }
302  else if ( attributeType == QVariant::Bool )
303  {
304  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "boolean" ) );
305  }
306  else if ( attributeType == QVariant::Date )
307  {
308  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "date" ) );
309  }
310  else if ( attributeType == QVariant::Time )
311  {
312  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "time" ) );
313  }
314  else if ( attributeType == QVariant::DateTime )
315  {
316  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "dateTime" ) );
317  }
318  else
319  {
320  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "string" ) );
321  }
322 
324  if ( setup.type() == QStringLiteral( "DateTime" ) )
325  {
326  const QgsDateTimeFieldFormatter fieldFormatter;
327  const QVariantMap config = setup.config();
328  const QString fieldFormat = config.value( QStringLiteral( "field_format" ), fieldFormatter.defaultFormat( field.type() ) ).toString();
329  if ( fieldFormat == QLatin1String( "yyyy-MM-dd" ) )
330  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "date" ) );
331  else if ( fieldFormat == QLatin1String( "HH:mm:ss" ) )
332  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "time" ) );
333  else
334  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "dateTime" ) );
335  }
336  else if ( setup.type() == QStringLiteral( "Range" ) )
337  {
338  const QVariantMap config = setup.config();
339  if ( config.contains( QStringLiteral( "Precision" ) ) )
340  {
341  // if precision in range config is not the same as the attributePrec
342  // we need to update type
343  bool ok;
344  const int configPrec( config[ QStringLiteral( "Precision" ) ].toInt( &ok ) );
345  if ( ok && configPrec != field.precision() )
346  {
347  if ( configPrec == 0 )
348  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "integer" ) );
349  else
350  attElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "decimal" ) );
351  }
352  }
353  }
354 
355  if ( !( field.constraints().constraints() & QgsFieldConstraints::Constraint::ConstraintNotNull ) )
356  {
357  attElem.setAttribute( QStringLiteral( "nillable" ), QStringLiteral( "true" ) );
358  }
359 
360  sequenceElem.appendChild( attElem );
361 
362  const QString alias = field.alias();
363  if ( !alias.isEmpty() )
364  {
365  attElem.setAttribute( QStringLiteral( "alias" ), alias );
366  }
367  }
368  }
369 
370 } // 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.
Field formatter for a date time field.
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
Fields is available if layer is served as WFS from QGIS server.
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:572
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:101
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...
QgsServerRequest::Parameters parameters() const
Returns a map of query parameters with keys converted to uppercase.
QMap< QString, QString > Parameters
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
void setSchemaLayer(QDomElement &parentElement, QDomDocument &doc, const QgsVectorLayer *layer)
const QString GML_NAMESPACE
Definition: qgswfsutils.h:73
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