QGIS API Documentation 3.27.0-Master (f261cc1f8b)
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"
25#include "qgswfsparameters.h"
26
27#include "qgsproject.h"
28#include "qgsvectorlayer.h"
30
31namespace 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 {
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 {
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 QgsServerCacheManager * cacheManager() const =0
Gets the registered server cache filters.
virtual QgsAccessControl * accessControls() const =0
Gets the registered access control 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