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