QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
qgswmsgetcapabilities.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgswmsgetmap.h
3  -------------------------
4  begin : December 20 , 2016
5  copyright : (C) 2007 by Marco Hugentobler (original code)
6  (C) 2014 by Alessandro Pasotti (original code)
7  (C) 2016 by David Marteau
8  email : marco dot hugentobler at karto dot baug dot ethz dot ch
9  a dot pasotti at itopen dot it
10  david dot marteau at 3liz dot com
11  ***************************************************************************/
12 
13 /***************************************************************************
14  * *
15  * This program is free software; you can redistribute it and/or modify *
16  * it under the terms of the GNU General Public License as published by *
17  * the Free Software Foundation; either version 2 of the License, or *
18  * (at your option) any later version. *
19  * *
20  ***************************************************************************/
21 #include "qgswmsutils.h"
22 #include "qgswmsgetcapabilities.h"
23 #include "qgsserverprojectutils.h"
24 
25 #include "qgslayoutmanager.h"
26 #include "qgslayoutatlas.h"
27 #include "qgsprintlayout.h"
28 #include "qgslayoutitemmap.h"
29 #include "qgslayoutitemlabel.h"
30 #include "qgslayoutitemhtml.h"
31 #include "qgslayoutframe.h"
33 
35 
36 #include "qgsexception.h"
37 #include "qgsexpressionnodeimpl.h"
38 #include "qgsvectorlayer.h"
39 #include "qgsrasterdataprovider.h"
40 
42 
43 
44 namespace QgsWms
45 {
46 
47  namespace
48  {
49 
50  void appendLayerProjectSettings( QDomDocument &doc, QDomElement &layerElem, QgsMapLayer *currentLayer );
51 
52  void appendDrawingOrder( QDomDocument &doc, QDomElement &parentElem, QgsServerInterface *serverIface,
53  const QgsProject *project );
54 
55  void combineExtentAndCrsOfGroupChildren( QDomDocument &doc, QDomElement &groupElem, const QgsProject *project,
56  bool considerMapExtent = false );
57 
58  bool crsSetFromLayerElement( const QDomElement &layerElement, QSet<QString> &crsSet );
59 
60  QgsRectangle layerBoundingBoxInProjectCrs( const QDomDocument &doc, const QDomElement &layerElem,
61  const QgsProject *project );
62 
63  void appendLayerBoundingBox( QDomDocument &doc, QDomElement &layerElem, const QgsRectangle &layerExtent,
64  const QgsCoordinateReferenceSystem &layerCRS, const QString &crsText,
65  const QgsProject *project );
66 
67  void appendLayerBoundingBoxes( QDomDocument &doc, QDomElement &layerElem, const QgsRectangle &lExtent,
68  const QgsCoordinateReferenceSystem &layerCRS, const QStringList &crsList,
69  const QStringList &constrainedCrsList, const QgsProject *project );
70 
71  void appendCrsElementToLayer( QDomDocument &doc, QDomElement &layerElement, const QDomElement &precedingElement,
72  const QString &crsText );
73 
74  void appendCrsElementsToLayer( QDomDocument &doc, QDomElement &layerElement,
75  const QStringList &crsList, const QStringList &constrainedCrsList );
76 
77  void appendLayerStyles( QDomDocument &doc, QDomElement &layerElem, QgsMapLayer *currentLayer,
78  const QgsProject *project, const QString &version, const QgsServerRequest &request );
79 
80  void appendLayersFromTreeGroup( QDomDocument &doc,
81  QDomElement &parentLayer,
82  QgsServerInterface *serverIface,
83  const QgsProject *project,
84  const QString &version,
85  const QgsServerRequest &request,
86  const QgsLayerTreeGroup *layerTreeGroup,
87  bool projectSettings );
88 
89  void addKeywordListElement( const QgsProject *project, QDomDocument &doc, QDomElement &parent );
90  }
91 
92  void writeGetCapabilities( QgsServerInterface *serverIface, const QgsProject *project,
93  const QString &version, const QgsServerRequest &request,
94  QgsServerResponse &response, bool projectSettings )
95  {
96 #ifdef HAVE_SERVER_PYTHON_PLUGINS
97  QgsAccessControl *accessControl = serverIface->accessControls();
98 #endif
99 
100  QDomDocument doc;
101  const QDomDocument *capabilitiesDocument = nullptr;
102 
103  // Data for WMS capabilities server memory cache
104  QString configFilePath = serverIface->configFilePath();
105  QgsCapabilitiesCache *capabilitiesCache = serverIface->capabilitiesCache();
106  QStringList cacheKeyList;
107  cacheKeyList << ( projectSettings ? QStringLiteral( "projectSettings" ) : version );
108  cacheKeyList << request.url().host();
109  bool cache = true;
110 
111 #ifdef HAVE_SERVER_PYTHON_PLUGINS
112  if ( accessControl )
113  cache = accessControl->fillCacheKey( cacheKeyList );
114 #endif
115  QString cacheKey = cacheKeyList.join( '-' );
116 
117 #ifdef HAVE_SERVER_PYTHON_PLUGINS
118  QgsServerCacheManager *cacheManager = serverIface->cacheManager();
119  if ( cacheManager && cacheManager->getCachedDocument( &doc, project, request, accessControl ) )
120  {
121  capabilitiesDocument = &doc;
122  }
123 #endif
124  if ( !capabilitiesDocument && cache ) //capabilities xml not in cache plugins
125  {
126  capabilitiesDocument = capabilitiesCache->searchCapabilitiesDocument( configFilePath, cacheKey );
127  }
128 
129  if ( !capabilitiesDocument ) //capabilities xml not in cache. Create a new one
130  {
131  QgsMessageLog::logMessage( QStringLiteral( "WMS capabilities document not found in cache" ), QStringLiteral( "Server" ) );
132 
133  doc = getCapabilities( serverIface, project, version, request, projectSettings );
134 
135 #ifdef HAVE_SERVER_PYTHON_PLUGINS
136  if ( cacheManager &&
137  cacheManager->setCachedDocument( &doc, project, request, accessControl ) )
138  {
139  capabilitiesDocument = &doc;
140  }
141 #endif
142 
143  if ( !capabilitiesDocument )
144  {
145  capabilitiesCache->insertCapabilitiesDocument( configFilePath, cacheKey, &doc );
146  capabilitiesDocument = capabilitiesCache->searchCapabilitiesDocument( configFilePath, cacheKey );
147  }
148  if ( !capabilitiesDocument )
149  {
150  capabilitiesDocument = &doc;
151  }
152  else
153  {
154  QgsMessageLog::logMessage( QStringLiteral( "Set WMS capabilities document in cache" ), QStringLiteral( "Server" ) );
155  }
156  }
157  else
158  {
159  QgsMessageLog::logMessage( QStringLiteral( "Found WMS capabilities document in cache" ), QStringLiteral( "Server" ) );
160  }
161 
162  response.setHeader( QStringLiteral( "Content-Type" ), QStringLiteral( "text/xml; charset=utf-8" ) );
163  response.write( capabilitiesDocument->toByteArray() );
164  }
165 
166  QDomDocument getCapabilities( QgsServerInterface *serverIface, const QgsProject *project,
167  const QString &version, const QgsServerRequest &request,
168  bool projectSettings )
169  {
170  QDomDocument doc;
171  QDomElement wmsCapabilitiesElement;
172 
173  QgsServerRequest::Parameters parameters = request.parameters();
174 
175  // Get service URL
176  QUrl href = serviceUrl( request, project );
177 
178  //href needs to be a prefix
179  QString hrefString = href.toString();
180  hrefString.append( href.hasQuery() ? "&" : "?" );
181 
182  // XML declaration
183  QDomProcessingInstruction xmlDeclaration = doc.createProcessingInstruction( QStringLiteral( "xml" ),
184  QStringLiteral( "version=\"1.0\" encoding=\"utf-8\"" ) );
185 
186  // Append format helper
187  std::function < void ( QDomElement &, const QString & ) > appendFormat = [&doc]( QDomElement & elem, const QString & format )
188  {
189  QDomElement formatElem = doc.createElement( QStringLiteral( "Format" )/*wms:Format*/ );
190  formatElem.appendChild( doc.createTextNode( format ) );
191  elem.appendChild( formatElem );
192  };
193 
194  if ( version == QLatin1String( "1.1.1" ) )
195  {
196  doc = QDomDocument( QStringLiteral( "WMT_MS_Capabilities SYSTEM 'http://schemas.opengis.net/wms/1.1.1/WMS_MS_Capabilities.dtd'" ) ); //WMS 1.1.1 needs DOCTYPE "SYSTEM http://schemas.opengis.net/wms/1.1.1/WMS_MS_Capabilities.dtd"
197  doc.appendChild( xmlDeclaration );
198  wmsCapabilitiesElement = doc.createElement( QStringLiteral( "WMT_MS_Capabilities" )/*wms:WMS_Capabilities*/ );
199  }
200  else // 1.3.0 as default
201  {
202  doc.appendChild( xmlDeclaration );
203  wmsCapabilitiesElement = doc.createElement( QStringLiteral( "WMS_Capabilities" )/*wms:WMS_Capabilities*/ );
204  wmsCapabilitiesElement.setAttribute( QStringLiteral( "xmlns" ), QStringLiteral( "http://www.opengis.net/wms" ) );
205  wmsCapabilitiesElement.setAttribute( QStringLiteral( "xmlns:sld" ), QStringLiteral( "http://www.opengis.net/sld" ) );
206  wmsCapabilitiesElement.setAttribute( QStringLiteral( "xmlns:qgs" ), QStringLiteral( "http://www.qgis.org/wms" ) );
207  wmsCapabilitiesElement.setAttribute( QStringLiteral( "xmlns:xsi" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema-instance" ) );
208  QString schemaLocation = QStringLiteral( "http://www.opengis.net/wms" );
209  schemaLocation += QLatin1String( " http://schemas.opengis.net/wms/1.3.0/capabilities_1_3_0.xsd" );
210  schemaLocation += QLatin1String( " http://www.opengis.net/sld" );
211  schemaLocation += QLatin1String( " http://schemas.opengis.net/sld/1.1.0/sld_capabilities.xsd" );
212 
214  {
215  wmsCapabilitiesElement.setAttribute( QStringLiteral( "xmlns:inspire_common" ), QStringLiteral( "http://inspire.ec.europa.eu/schemas/common/1.0" ) );
216  wmsCapabilitiesElement.setAttribute( QStringLiteral( "xmlns:inspire_vs" ), QStringLiteral( "http://inspire.ec.europa.eu/schemas/inspire_vs/1.0" ) );
217  schemaLocation += QLatin1String( " http://inspire.ec.europa.eu/schemas/inspire_vs/1.0" );
218  schemaLocation += QLatin1String( " http://inspire.ec.europa.eu/schemas/inspire_vs/1.0/inspire_vs.xsd" );
219  }
220 
221  schemaLocation += QLatin1String( " http://www.qgis.org/wms" );
222  schemaLocation += " " + hrefString + "SERVICE=WMS&REQUEST=GetSchemaExtension";
223 
224  wmsCapabilitiesElement.setAttribute( QStringLiteral( "xsi:schemaLocation" ), schemaLocation );
225  }
226  wmsCapabilitiesElement.setAttribute( QStringLiteral( "version" ), version );
227  doc.appendChild( wmsCapabilitiesElement );
228 
229  //INSERT Service
230  wmsCapabilitiesElement.appendChild( getServiceElement( doc, project, version, request ) );
231 
232  //wms:Capability element
233  QDomElement capabilityElement = getCapabilityElement( doc, project, version, request, projectSettings );
234  wmsCapabilitiesElement.appendChild( capabilityElement );
235 
236  if ( projectSettings )
237  {
238  //Insert <ComposerTemplate> elements derived from wms:_ExtendedCapabilities
239  capabilityElement.appendChild( getComposerTemplatesElement( doc, project ) );
240 
241  //WFS layers
242  capabilityElement.appendChild( getWFSLayersElement( doc, project ) );
243  }
244 
245  capabilityElement.appendChild(
246  getLayersAndStylesCapabilitiesElement( doc, serverIface, project, version, request, projectSettings )
247  );
248 
249  if ( projectSettings )
250  {
251  appendDrawingOrder( doc, capabilityElement, serverIface, project );
252  }
253 
254  return doc;
255  }
256 
257  QDomElement getServiceElement( QDomDocument &doc, const QgsProject *project, const QString &version,
258  const QgsServerRequest &request )
259  {
260  //Service element
261  QDomElement serviceElem = doc.createElement( QStringLiteral( "Service" ) );
262 
263  //Service name
264  QDomElement nameElem = doc.createElement( QStringLiteral( "Name" ) );
265  QDomText nameText = doc.createTextNode( QStringLiteral( "WMS" ) );
266  nameElem.appendChild( nameText );
267  serviceElem.appendChild( nameElem );
268 
269  QString title = QgsServerProjectUtils::owsServiceTitle( *project );
270  if ( !title.isEmpty() )
271  {
272  QDomElement titleElem = doc.createElement( QStringLiteral( "Title" ) );
273  QDomText titleText = doc.createTextNode( title );
274  titleElem.appendChild( titleText );
275  serviceElem.appendChild( titleElem );
276  }
277 
278  QString abstract = QgsServerProjectUtils::owsServiceAbstract( *project );
279  if ( !abstract.isEmpty() )
280  {
281  QDomElement abstractElem = doc.createElement( QStringLiteral( "Abstract" ) );
282  QDomText abstractText = doc.createCDATASection( abstract );
283  abstractElem.appendChild( abstractText );
284  serviceElem.appendChild( abstractElem );
285  }
286 
287  addKeywordListElement( project, doc, serviceElem );
288 
289  QString onlineResource = QgsServerProjectUtils::owsServiceOnlineResource( *project );
290  if ( onlineResource.isEmpty() )
291  {
292  onlineResource = serviceUrl( request, project ).toString();
293  }
294  QDomElement onlineResourceElem = doc.createElement( QStringLiteral( "OnlineResource" ) );
295  onlineResourceElem.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
296  onlineResourceElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) );
297  onlineResourceElem.setAttribute( QStringLiteral( "xlink:href" ), onlineResource );
298  serviceElem.appendChild( onlineResourceElem );
299 
300  QString contactPerson = QgsServerProjectUtils::owsServiceContactPerson( *project );
301  QString contactOrganization = QgsServerProjectUtils::owsServiceContactOrganization( *project );
302  QString contactPosition = QgsServerProjectUtils::owsServiceContactPosition( *project );
303  QString contactMail = QgsServerProjectUtils::owsServiceContactMail( *project );
304  QString contactPhone = QgsServerProjectUtils::owsServiceContactPhone( *project );
305  if ( !contactPerson.isEmpty() ||
306  !contactOrganization.isEmpty() ||
307  !contactPosition.isEmpty() ||
308  !contactMail.isEmpty() ||
309  !contactPhone.isEmpty() )
310  {
311  //Contact information
312  QDomElement contactInfoElem = doc.createElement( QStringLiteral( "ContactInformation" ) );
313 
314  //Contact person primary
315  if ( !contactPerson.isEmpty() ||
316  !contactOrganization.isEmpty() ||
317  !contactPosition.isEmpty() )
318  {
319  QDomElement contactPersonPrimaryElem = doc.createElement( QStringLiteral( "ContactPersonPrimary" ) );
320 
321  if ( !contactPerson.isEmpty() )
322  {
323  QDomElement contactPersonElem = doc.createElement( QStringLiteral( "ContactPerson" ) );
324  QDomText contactPersonText = doc.createTextNode( contactPerson );
325  contactPersonElem.appendChild( contactPersonText );
326  contactPersonPrimaryElem.appendChild( contactPersonElem );
327  }
328 
329  if ( !contactOrganization.isEmpty() )
330  {
331  QDomElement contactOrganizationElem = doc.createElement( QStringLiteral( "ContactOrganization" ) );
332  QDomText contactOrganizationText = doc.createTextNode( contactOrganization );
333  contactOrganizationElem.appendChild( contactOrganizationText );
334  contactPersonPrimaryElem.appendChild( contactOrganizationElem );
335  }
336 
337  if ( !contactPosition.isEmpty() )
338  {
339  QDomElement contactPositionElem = doc.createElement( QStringLiteral( "ContactPosition" ) );
340  QDomText contactPositionText = doc.createTextNode( contactPosition );
341  contactPositionElem.appendChild( contactPositionText );
342  contactPersonPrimaryElem.appendChild( contactPositionElem );
343  }
344 
345  contactInfoElem.appendChild( contactPersonPrimaryElem );
346  }
347 
348  if ( !contactPhone.isEmpty() )
349  {
350  QDomElement phoneElem = doc.createElement( QStringLiteral( "ContactVoiceTelephone" ) );
351  QDomText phoneText = doc.createTextNode( contactPhone );
352  phoneElem.appendChild( phoneText );
353  contactInfoElem.appendChild( phoneElem );
354  }
355 
356  if ( !contactMail.isEmpty() )
357  {
358  QDomElement mailElem = doc.createElement( QStringLiteral( "ContactElectronicMailAddress" ) );
359  QDomText mailText = doc.createTextNode( contactMail );
360  mailElem.appendChild( mailText );
361  contactInfoElem.appendChild( mailElem );
362  }
363 
364  serviceElem.appendChild( contactInfoElem );
365  }
366 
367  QDomElement feesElem = doc.createElement( QStringLiteral( "Fees" ) );
368  QDomText feesText = doc.createTextNode( QStringLiteral( "None" ) ); // default value if fees are unknown
369  QString fees = QgsServerProjectUtils::owsServiceFees( *project );
370  if ( !fees.isEmpty() )
371  {
372  feesText = doc.createTextNode( fees );
373  }
374  feesElem.appendChild( feesText );
375  serviceElem.appendChild( feesElem );
376 
377  QDomElement accessConstraintsElem = doc.createElement( QStringLiteral( "AccessConstraints" ) );
378  QDomText accessConstraintsText = doc.createTextNode( QStringLiteral( "None" ) ); // default value if access constraints are unknown
379  QString accessConstraints = QgsServerProjectUtils::owsServiceAccessConstraints( *project );
380  if ( !accessConstraints.isEmpty() )
381  {
382  accessConstraintsText = doc.createTextNode( accessConstraints );
383  }
384  accessConstraintsElem.appendChild( accessConstraintsText );
385  serviceElem.appendChild( accessConstraintsElem );
386 
387  if ( version == QLatin1String( "1.3.0" ) )
388  {
389  int maxWidth = QgsServerProjectUtils::wmsMaxWidth( *project );
390  if ( maxWidth > 0 )
391  {
392  QDomElement maxWidthElem = doc.createElement( QStringLiteral( "MaxWidth" ) );
393  QDomText maxWidthText = doc.createTextNode( QString::number( maxWidth ) );
394  maxWidthElem.appendChild( maxWidthText );
395  serviceElem.appendChild( maxWidthElem );
396  }
397 
398  int maxHeight = QgsServerProjectUtils::wmsMaxHeight( *project );
399  if ( maxHeight > 0 )
400  {
401  QDomElement maxHeightElem = doc.createElement( QStringLiteral( "MaxHeight" ) );
402  QDomText maxHeightText = doc.createTextNode( QString::number( maxHeight ) );
403  maxHeightElem.appendChild( maxHeightText );
404  serviceElem.appendChild( maxHeightElem );
405  }
406  }
407 
408  return serviceElem;
409  }
410 
411  QDomElement getCapabilityElement( QDomDocument &doc, const QgsProject *project,
412  const QString &version, const QgsServerRequest &request,
413  bool projectSettings )
414  {
415  QgsServerRequest::Parameters parameters = request.parameters();
416 
417  // Get service URL
418  QUrl href = serviceUrl( request, project );
419 
420  //href needs to be a prefix
421  QString hrefString = href.toString();
422  hrefString.append( href.hasQuery() ? "&" : "?" );
423 
424  QDomElement capabilityElem = doc.createElement( QStringLiteral( "Capability" )/*wms:Capability*/ );
425 
426  //wms:Request element
427  QDomElement requestElem = doc.createElement( QStringLiteral( "Request" )/*wms:Request*/ );
428  capabilityElem.appendChild( requestElem );
429 
430  QDomElement dcpTypeElem = doc.createElement( QStringLiteral( "DCPType" )/*wms:DCPType*/ );
431  QDomElement httpElem = doc.createElement( QStringLiteral( "HTTP" )/*wms:HTTP*/ );
432  dcpTypeElem.appendChild( httpElem );
433 
434  // Append format helper
435  std::function < void ( QDomElement &, const QString & ) > appendFormat = [&doc]( QDomElement & elem, const QString & format )
436  {
437  QDomElement formatElem = doc.createElement( QStringLiteral( "Format" )/*wms:Format*/ );
438  formatElem.appendChild( doc.createTextNode( format ) );
439  elem.appendChild( formatElem );
440  };
441 
442  QDomElement elem;
443 
444  //wms:GetCapabilities
445  elem = doc.createElement( QStringLiteral( "GetCapabilities" )/*wms:GetCapabilities*/ );
446  appendFormat( elem, ( version == QLatin1String( "1.1.1" ) ? "application/vnd.ogc.wms_xml" : "text/xml" ) );
447  elem.appendChild( dcpTypeElem );
448  requestElem.appendChild( elem );
449 
450  // SOAP platform
451  //only give this information if it is not a WMS request to be in sync with the WMS capabilities schema
452  // XXX Not even sure that cam be ever true
453  if ( parameters.value( QStringLiteral( "SERVICE" ) ).compare( QLatin1String( "WMS" ), Qt::CaseInsensitive ) != 0 )
454  {
455  QDomElement soapElem = doc.createElement( QStringLiteral( "SOAP" )/*wms:SOAP*/ );
456  httpElem.appendChild( soapElem );
457  QDomElement soapResourceElem = doc.createElement( QStringLiteral( "OnlineResource" )/*wms:OnlineResource*/ );
458  soapResourceElem.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
459  soapResourceElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) );
460  soapResourceElem.setAttribute( QStringLiteral( "xlink:href" ), hrefString );
461  soapElem.appendChild( soapResourceElem );
462  }
463 
464  //only Get supported for the moment
465  QDomElement getElem = doc.createElement( QStringLiteral( "Get" )/*wms:Get*/ );
466  httpElem.appendChild( getElem );
467  QDomElement olResourceElem = doc.createElement( QStringLiteral( "OnlineResource" )/*wms:OnlineResource*/ );
468  olResourceElem.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
469  olResourceElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) );
470  olResourceElem.setAttribute( QStringLiteral( "xlink:href" ), hrefString );
471  getElem.appendChild( olResourceElem );
472 
473  //wms:GetMap
474  elem = doc.createElement( QStringLiteral( "GetMap" )/*wms:GetMap*/ );
475  appendFormat( elem, QStringLiteral( "image/jpeg" ) );
476  appendFormat( elem, QStringLiteral( "image/png" ) );
477  appendFormat( elem, QStringLiteral( "image/png; mode=16bit" ) );
478  appendFormat( elem, QStringLiteral( "image/png; mode=8bit" ) );
479  appendFormat( elem, QStringLiteral( "image/png; mode=1bit" ) );
480  appendFormat( elem, QStringLiteral( "application/dxf" ) );
481  elem.appendChild( dcpTypeElem.cloneNode().toElement() ); //this is the same as for 'GetCapabilities'
482  requestElem.appendChild( elem );
483 
484  //wms:GetFeatureInfo
485  elem = doc.createElement( QStringLiteral( "GetFeatureInfo" ) );
486  appendFormat( elem, QStringLiteral( "text/plain" ) );
487  appendFormat( elem, QStringLiteral( "text/html" ) );
488  appendFormat( elem, QStringLiteral( "text/xml" ) );
489  appendFormat( elem, QStringLiteral( "application/vnd.ogc.gml" ) );
490  appendFormat( elem, QStringLiteral( "application/vnd.ogc.gml/3.1.1" ) );
491  appendFormat( elem, QStringLiteral( "application/json" ) );
492  appendFormat( elem, QStringLiteral( "application/geo+json" ) );
493  elem.appendChild( dcpTypeElem.cloneNode().toElement() ); //this is the same as for 'GetCapabilities'
494  requestElem.appendChild( elem );
495 
496  //wms:GetLegendGraphic
497  elem = doc.createElement( ( version == QLatin1String( "1.1.1" ) ? "GetLegendGraphic" : "sld:GetLegendGraphic" )/*wms:GetLegendGraphic*/ );
498  appendFormat( elem, QStringLiteral( "image/jpeg" ) );
499  appendFormat( elem, QStringLiteral( "image/png" ) );
500  elem.appendChild( dcpTypeElem.cloneNode().toElement() ); //this is the same as for 'GetCapabilities'
501  requestElem.appendChild( elem );
502 
503  //wms:DescribeLayer
504  elem = doc.createElement( ( version == QLatin1String( "1.1.1" ) ? "DescribeLayer" : "sld:DescribeLayer" )/*wms:GetLegendGraphic*/ );
505  appendFormat( elem, QStringLiteral( "text/xml" ) );
506  elem.appendChild( dcpTypeElem.cloneNode().toElement() ); //this is the same as for 'GetCapabilities'
507  requestElem.appendChild( elem );
508 
509  //wms:GetStyles
510  elem = doc.createElement( ( version == QLatin1String( "1.1.1" ) ? "GetStyles" : "qgs:GetStyles" )/*wms:GetStyles*/ );
511  appendFormat( elem, QStringLiteral( "text/xml" ) );
512  elem.appendChild( dcpTypeElem.cloneNode().toElement() ); //this is the same as for 'GetCapabilities'
513  requestElem.appendChild( elem );
514 
515  if ( projectSettings ) //remove composer templates from GetCapabilities in the long term
516  {
517  //wms:GetPrint
518  elem = doc.createElement( QStringLiteral( "GetPrint" ) /*wms:GetPrint*/ );
519  appendFormat( elem, QStringLiteral( "svg" ) );
520  appendFormat( elem, QStringLiteral( "png" ) );
521  appendFormat( elem, QStringLiteral( "pdf" ) );
522  elem.appendChild( dcpTypeElem.cloneNode().toElement() ); //this is the same as for 'GetCapabilities'
523  requestElem.appendChild( elem );
524  }
525 
526  //Exception element is mandatory
527  elem = doc.createElement( QStringLiteral( "Exception" ) );
528  appendFormat( elem, ( version == QLatin1String( "1.1.1" ) ? "application/vnd.ogc.se_xml" : "XML" ) );
529  capabilityElem.appendChild( elem );
530 
531  //UserDefinedSymbolization element
532  if ( version == QLatin1String( "1.3.0" ) )
533  {
534  elem = doc.createElement( QStringLiteral( "sld:UserDefinedSymbolization" ) );
535  elem.setAttribute( QStringLiteral( "SupportSLD" ), QStringLiteral( "1" ) );
536  elem.setAttribute( QStringLiteral( "UserLayer" ), QStringLiteral( "0" ) );
537  elem.setAttribute( QStringLiteral( "UserStyle" ), QStringLiteral( "1" ) );
538  elem.setAttribute( QStringLiteral( "RemoteWFS" ), QStringLiteral( "0" ) );
539  elem.setAttribute( QStringLiteral( "InlineFeature" ), QStringLiteral( "0" ) );
540  elem.setAttribute( QStringLiteral( "RemoteWCS" ), QStringLiteral( "0" ) );
541  capabilityElem.appendChild( elem );
542 
544  {
545  capabilityElem.appendChild( getInspireCapabilitiesElement( doc, project ) );
546  }
547  }
548 
549  return capabilityElem;
550  }
551 
552  QDomElement getInspireCapabilitiesElement( QDomDocument &doc, const QgsProject *project )
553  {
554  QDomElement inspireCapabilitiesElem;
555 
557  return inspireCapabilitiesElem;
558 
559  inspireCapabilitiesElem = doc.createElement( QStringLiteral( "inspire_vs:ExtendedCapabilities" ) );
560 
561  QString inspireMetadataUrl = QgsServerProjectUtils::wmsInspireMetadataUrl( *project );
562  // inspire scenario 1
563  if ( !inspireMetadataUrl.isEmpty() )
564  {
565  QDomElement inspireCommonMetadataUrlElem = doc.createElement( QStringLiteral( "inspire_common:MetadataUrl" ) );
566  inspireCommonMetadataUrlElem.setAttribute( QStringLiteral( "xsi:type" ), QStringLiteral( "inspire_common:resourceLocatorType" ) );
567 
568  QDomElement inspireCommonMetadataUrlUrlElem = doc.createElement( QStringLiteral( "inspire_common:URL" ) );
569  inspireCommonMetadataUrlUrlElem.appendChild( doc.createTextNode( inspireMetadataUrl ) );
570  inspireCommonMetadataUrlElem.appendChild( inspireCommonMetadataUrlUrlElem );
571 
572  QString inspireMetadataUrlType = QgsServerProjectUtils::wmsInspireMetadataUrlType( *project );
573  if ( !inspireMetadataUrlType.isNull() )
574  {
575  QDomElement inspireCommonMetadataUrlMediaTypeElem = doc.createElement( QStringLiteral( "inspire_common:MediaType" ) );
576  inspireCommonMetadataUrlMediaTypeElem.appendChild( doc.createTextNode( inspireMetadataUrlType ) );
577  inspireCommonMetadataUrlElem.appendChild( inspireCommonMetadataUrlMediaTypeElem );
578  }
579 
580  inspireCapabilitiesElem.appendChild( inspireCommonMetadataUrlElem );
581  }
582  else
583  {
584  QDomElement inspireCommonResourceTypeElem = doc.createElement( QStringLiteral( "inspire_common:ResourceType" ) );
585  inspireCommonResourceTypeElem.appendChild( doc.createTextNode( QStringLiteral( "service" ) ) );
586  inspireCapabilitiesElem.appendChild( inspireCommonResourceTypeElem );
587 
588  QDomElement inspireCommonSpatialDataServiceTypeElem = doc.createElement( QStringLiteral( "inspire_common:SpatialDataServiceType" ) );
589  inspireCommonSpatialDataServiceTypeElem.appendChild( doc.createTextNode( QStringLiteral( "view" ) ) );
590  inspireCapabilitiesElem.appendChild( inspireCommonSpatialDataServiceTypeElem );
591 
592  QString inspireTemporalReference = QgsServerProjectUtils::wmsInspireTemporalReference( *project );
593  if ( !inspireTemporalReference.isNull() )
594  {
595  QDomElement inspireCommonTemporalReferenceElem = doc.createElement( QStringLiteral( "inspire_common:TemporalReference" ) );
596  QDomElement inspireCommonDateOfLastRevisionElem = doc.createElement( QStringLiteral( "inspire_common:DateOfLastRevision" ) );
597  inspireCommonDateOfLastRevisionElem.appendChild( doc.createTextNode( inspireTemporalReference ) );
598  inspireCommonTemporalReferenceElem.appendChild( inspireCommonDateOfLastRevisionElem );
599  inspireCapabilitiesElem.appendChild( inspireCommonTemporalReferenceElem );
600  }
601 
602  QDomElement inspireCommonMetadataPointOfContactElem = doc.createElement( QStringLiteral( "inspire_common:MetadataPointOfContact" ) );
603 
604  QString contactOrganization = QgsServerProjectUtils::owsServiceContactOrganization( *project );
605  QDomElement inspireCommonOrganisationNameElem = doc.createElement( QStringLiteral( "inspire_common:OrganisationName" ) );
606  if ( !contactOrganization.isNull() )
607  {
608  inspireCommonOrganisationNameElem.appendChild( doc.createTextNode( contactOrganization ) );
609  }
610  inspireCommonMetadataPointOfContactElem.appendChild( inspireCommonOrganisationNameElem );
611 
612  QString contactMail = QgsServerProjectUtils::owsServiceContactMail( *project );
613  QDomElement inspireCommonEmailAddressElem = doc.createElement( QStringLiteral( "inspire_common:EmailAddress" ) );
614  if ( !contactMail.isNull() )
615  {
616  inspireCommonEmailAddressElem.appendChild( doc.createTextNode( contactMail ) );
617  }
618  inspireCommonMetadataPointOfContactElem.appendChild( inspireCommonEmailAddressElem );
619 
620  inspireCapabilitiesElem.appendChild( inspireCommonMetadataPointOfContactElem );
621 
622  QString inspireMetadataDate = QgsServerProjectUtils::wmsInspireMetadataDate( *project );
623  if ( !inspireMetadataDate.isNull() )
624  {
625  QDomElement inspireCommonMetadataDateElem = doc.createElement( QStringLiteral( "inspire_common:MetadataDate" ) );
626  inspireCommonMetadataDateElem.appendChild( doc.createTextNode( inspireMetadataDate ) );
627  inspireCapabilitiesElem.appendChild( inspireCommonMetadataDateElem );
628  }
629  }
630 
631  // Supported languages
632  QDomElement inspireCommonSupportedLanguagesElem = doc.createElement( QStringLiteral( "inspire_common:SupportedLanguages" ) );
633  inspireCommonSupportedLanguagesElem.setAttribute( QStringLiteral( "xsi:type" ), QStringLiteral( "inspire_common:supportedLanguagesType" ) );
634 
635  QDomElement inspireCommonLanguageElem = doc.createElement( QStringLiteral( "inspire_common:Language" ) );
636  inspireCommonLanguageElem.appendChild( doc.createTextNode( QgsServerProjectUtils::wmsInspireLanguage( *project ) ) );
637 
638  QDomElement inspireCommonDefaultLanguageElem = doc.createElement( QStringLiteral( "inspire_common:DefaultLanguage" ) );
639  inspireCommonDefaultLanguageElem.appendChild( inspireCommonLanguageElem );
640  inspireCommonSupportedLanguagesElem.appendChild( inspireCommonDefaultLanguageElem );
641 
642 #if 0
643  /* Supported language has to be different from default one */
644  QDomElement inspireCommonSupportedLanguageElem = doc.createElement( "inspire_common:SupportedLanguage" );
645  inspireCommonSupportedLanguageElem.appendChild( inspireCommonLanguageElem.cloneNode().toElement() );
646  inspireCommonSupportedLanguagesElem.appendChild( inspireCommonSupportedLanguageElem );
647 #endif
648 
649  inspireCapabilitiesElem.appendChild( inspireCommonSupportedLanguagesElem );
650 
651  QDomElement inspireCommonResponseLanguageElem = doc.createElement( QStringLiteral( "inspire_common:ResponseLanguage" ) );
652  inspireCommonResponseLanguageElem.appendChild( inspireCommonLanguageElem.cloneNode().toElement() );
653  inspireCapabilitiesElem.appendChild( inspireCommonResponseLanguageElem );
654 
655  return inspireCapabilitiesElem;
656  }
657 
658  QDomElement getComposerTemplatesElement( QDomDocument &doc, const QgsProject *project )
659  {
660  QList< QgsPrintLayout * > projectComposers = project->layoutManager()->printLayouts();
661  if ( projectComposers.size() == 0 )
662  return QDomElement();
663 
664  QStringList restrictedComposers = QgsServerProjectUtils::wmsRestrictedComposers( *project );
665 
666  QDomElement composerTemplatesElem = doc.createElement( QStringLiteral( "ComposerTemplates" ) );
667  QList<QgsPrintLayout *>::const_iterator cIt = projectComposers.constBegin();
668  for ( ; cIt != projectComposers.constEnd(); ++cIt )
669  {
670  QgsPrintLayout *layout = *cIt;
671  if ( restrictedComposers.contains( layout->name() ) )
672  continue;
673 
674  // Check that we have at least one page
675  if ( layout->pageCollection()->pageCount() < 1 )
676  continue;
677 
678  // Get width and height from first page of the collection
679  QgsLayoutSize layoutSize( layout->pageCollection()->page( 0 )->sizeWithUnits() );
680  QgsLayoutMeasurement width( layout->convertFromLayoutUnits( layoutSize.width(), QgsUnitTypes::LayoutUnit::LayoutMillimeters ) );
681  QgsLayoutMeasurement height( layout->convertFromLayoutUnits( layoutSize.height(), QgsUnitTypes::LayoutUnit::LayoutMillimeters ) );
682 
683  QDomElement composerTemplateElem = doc.createElement( QStringLiteral( "ComposerTemplate" ) );
684  composerTemplateElem.setAttribute( QStringLiteral( "name" ), layout->name() );
685 
686  //get paper width and height in mm from composition
687  composerTemplateElem.setAttribute( QStringLiteral( "width" ), width.length() );
688  composerTemplateElem.setAttribute( QStringLiteral( "height" ), height.length() );
689 
690  //atlas enabled and atlas covering layer
691  QgsLayoutAtlas *atlas = layout->atlas();
692  if ( atlas && atlas->enabled() )
693  {
694  composerTemplateElem.setAttribute( QStringLiteral( "atlasEnabled" ), QStringLiteral( "1" ) );
695  QgsVectorLayer *cLayer = atlas->coverageLayer();
696  if ( cLayer )
697  {
698  QString layerName = cLayer->shortName();
699  if ( QgsServerProjectUtils::wmsUseLayerIds( *project ) )
700  {
701  layerName = cLayer->id();
702  }
703  else if ( layerName.isEmpty() )
704  {
705  layerName = cLayer->name();
706  }
707  composerTemplateElem.setAttribute( QStringLiteral( "atlasCoverageLayer" ), layerName );
708  }
709  }
710 
711  //add available composer maps and their size in mm
712  QList<QgsLayoutItemMap *> layoutMapList;
713  layout->layoutItems<QgsLayoutItemMap>( layoutMapList );
714  QList<QgsLayoutItemMap *>::const_iterator cmIt = layoutMapList.constBegin();
715  // Add map id
716  int mapId = 0;
717  for ( ; cmIt != layoutMapList.constEnd(); ++cmIt )
718  {
719  const QgsLayoutItemMap *composerMap = *cmIt;
720 
721  QDomElement composerMapElem = doc.createElement( QStringLiteral( "ComposerMap" ) );
722  composerMapElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "map%1" ).arg( mapId ) );
723  mapId++;
724  composerMapElem.setAttribute( QStringLiteral( "width" ), composerMap->rect().width() );
725  composerMapElem.setAttribute( QStringLiteral( "height" ), composerMap->rect().height() );
726  composerTemplateElem.appendChild( composerMapElem );
727  }
728 
729  //add available composer labels
730  QList<QgsLayoutItemLabel *> composerLabelList;
731  layout->layoutItems<QgsLayoutItemLabel>( composerLabelList );
732  QList<QgsLayoutItemLabel *>::const_iterator clIt = composerLabelList.constBegin();
733  for ( ; clIt != composerLabelList.constEnd(); ++clIt )
734  {
735  QgsLayoutItemLabel *composerLabel = *clIt;
736  QString id = composerLabel->id();
737  if ( id.isEmpty() )
738  continue;
739 
740  QDomElement composerLabelElem = doc.createElement( QStringLiteral( "ComposerLabel" ) );
741  composerLabelElem.setAttribute( QStringLiteral( "name" ), id );
742  composerTemplateElem.appendChild( composerLabelElem );
743  }
744 
745  //add available composer HTML
746  QList<QgsLayoutItemHtml *> composerHtmlList;
747  layout->layoutObjects<QgsLayoutItemHtml>( composerHtmlList );
748  QList<QgsLayoutItemHtml *>::const_iterator chIt = composerHtmlList.constBegin();
749  for ( ; chIt != composerHtmlList.constEnd(); ++chIt )
750  {
751  QgsLayoutItemHtml *composerHtml = *chIt;
752  if ( composerHtml->frameCount() == 0 )
753  continue;
754 
755  QString id = composerHtml->frame( 0 )->id();
756  if ( id.isEmpty() )
757  continue;
758 
759  QDomElement composerHtmlElem = doc.createElement( QStringLiteral( "ComposerHtml" ) );
760  composerHtmlElem.setAttribute( QStringLiteral( "name" ), id );
761  composerTemplateElem.appendChild( composerHtmlElem );
762  }
763 
764  composerTemplatesElem.appendChild( composerTemplateElem );
765  }
766 
767  if ( composerTemplatesElem.childNodes().size() == 0 )
768  return QDomElement();
769 
770  return composerTemplatesElem;
771  }
772 
773  QDomElement getWFSLayersElement( QDomDocument &doc, const QgsProject *project )
774  {
775  QStringList wfsLayerIds = QgsServerProjectUtils::wfsLayerIds( *project );
776  if ( wfsLayerIds.size() == 0 )
777  return QDomElement();
778 
779  QDomElement wfsLayersElem = doc.createElement( QStringLiteral( "WFSLayers" ) );
780  for ( int i = 0; i < wfsLayerIds.size(); ++i )
781  {
782  QgsMapLayer *layer = project->mapLayer( wfsLayerIds.at( i ) );
783  if ( ! layer || layer->type() != QgsMapLayerType::VectorLayer )
784  {
785  continue;
786  }
787 
788  QDomElement wfsLayerElem = doc.createElement( QStringLiteral( "WFSLayer" ) );
789  if ( QgsServerProjectUtils::wmsUseLayerIds( *project ) )
790  {
791  wfsLayerElem.setAttribute( QStringLiteral( "name" ), layer->id() );
792  }
793  else
794  {
795  wfsLayerElem.setAttribute( QStringLiteral( "name" ), layer->name() );
796  }
797  wfsLayersElem.appendChild( wfsLayerElem );
798  }
799 
800  return wfsLayersElem;
801  }
802 
803  QDomElement getLayersAndStylesCapabilitiesElement( QDomDocument &doc, QgsServerInterface *serverIface,
804  const QgsProject *project, const QString &version,
805  const QgsServerRequest &request, bool projectSettings )
806  {
807  const QgsLayerTree *projectLayerTreeRoot = project->layerTreeRoot();
808 
809  QDomElement layerParentElem = doc.createElement( QStringLiteral( "Layer" ) );
810 
811  if ( !project->title().isEmpty() )
812  {
813  // Root Layer title
814  QDomElement layerParentTitleElem = doc.createElement( QStringLiteral( "Title" ) );
815  QDomText layerParentTitleText = doc.createTextNode( project->title() );
816  layerParentTitleElem.appendChild( layerParentTitleText );
817  layerParentElem.appendChild( layerParentTitleElem );
818 
819  // Root Layer abstract
820  QDomElement layerParentAbstElem = doc.createElement( QStringLiteral( "Abstract" ) );
821  QDomText layerParentAbstText = doc.createTextNode( project->title() );
822  layerParentAbstElem.appendChild( layerParentAbstText );
823  layerParentElem.appendChild( layerParentAbstElem );
824  }
825 
826  // Root Layer name
827  QString rootLayerName = QgsServerProjectUtils::wmsRootName( *project );
828  if ( rootLayerName.isEmpty() && !project->title().isEmpty() )
829  {
830  rootLayerName = project->title();
831  }
832 
833  if ( !rootLayerName.isEmpty() )
834  {
835  QDomElement layerParentNameElem = doc.createElement( QStringLiteral( "Name" ) );
836  QDomText layerParentNameText = doc.createTextNode( rootLayerName );
837  layerParentNameElem.appendChild( layerParentNameText );
838  layerParentElem.appendChild( layerParentNameElem );
839  }
840 
841  // Keyword list
842  addKeywordListElement( project, doc, layerParentElem );
843 
844  // Root Layer tree name
845  if ( projectSettings )
846  {
847  QDomElement treeNameElem = doc.createElement( QStringLiteral( "TreeName" ) );
848  QDomText treeNameText = doc.createTextNode( project->title() );
849  treeNameElem.appendChild( treeNameText );
850  layerParentElem.appendChild( treeNameElem );
851  }
852 
853  if ( hasQueryableChildren( projectLayerTreeRoot, QgsServerProjectUtils::wmsRestrictedLayers( *project ) ) )
854  {
855  layerParentElem.setAttribute( QStringLiteral( "queryable" ), QStringLiteral( "1" ) );
856  }
857  else
858  {
859  layerParentElem.setAttribute( QStringLiteral( "queryable" ), QStringLiteral( "0" ) );
860  }
861 
862  appendLayersFromTreeGroup( doc, layerParentElem, serverIface, project, version, request, projectLayerTreeRoot, projectSettings );
863 
864  combineExtentAndCrsOfGroupChildren( doc, layerParentElem, project, true );
865 
866  return layerParentElem;
867  }
868 
869  namespace
870  {
871 
872  void appendLayersFromTreeGroup( QDomDocument &doc,
873  QDomElement &parentLayer,
874  QgsServerInterface *serverIface,
875  const QgsProject *project,
876  const QString &version,
877  const QgsServerRequest &request,
878  const QgsLayerTreeGroup *layerTreeGroup,
879  bool projectSettings )
880  {
881  bool useLayerIds = QgsServerProjectUtils::wmsUseLayerIds( *project );
882  bool siaFormat = QgsServerProjectUtils::wmsInfoFormatSia2045( *project );
883  const QStringList restrictedLayers = QgsServerProjectUtils::wmsRestrictedLayers( *project );
884 
885  QList< QgsLayerTreeNode * > layerTreeGroupChildren = layerTreeGroup->children();
886  for ( int i = 0; i < layerTreeGroupChildren.size(); ++i )
887  {
888  QgsLayerTreeNode *treeNode = layerTreeGroupChildren.at( i );
889  QDomElement layerElem = doc.createElement( QStringLiteral( "Layer" ) );
890 
891  if ( projectSettings )
892  {
893  layerElem.setAttribute( QStringLiteral( "visible" ), treeNode->isVisible() );
894  }
895 
896  if ( treeNode->nodeType() == QgsLayerTreeNode::NodeGroup )
897  {
898  QgsLayerTreeGroup *treeGroupChild = static_cast<QgsLayerTreeGroup *>( treeNode );
899 
900  QString name = treeGroupChild->name();
901  if ( restrictedLayers.contains( name ) ) //unpublished group
902  {
903  continue;
904  }
905 
906  if ( projectSettings )
907  {
908  layerElem.setAttribute( QStringLiteral( "mutuallyExclusive" ), treeGroupChild->isMutuallyExclusive() );
909  }
910 
911  QString shortName = treeGroupChild->customProperty( QStringLiteral( "wmsShortName" ) ).toString();
912  QString title = treeGroupChild->customProperty( QStringLiteral( "wmsTitle" ) ).toString();
913 
914  QDomElement nameElem = doc.createElement( QStringLiteral( "Name" ) );
915  QDomText nameText;
916  if ( !shortName.isEmpty() )
917  nameText = doc.createTextNode( shortName );
918  else
919  nameText = doc.createTextNode( name );
920  nameElem.appendChild( nameText );
921  layerElem.appendChild( nameElem );
922 
923  QDomElement titleElem = doc.createElement( QStringLiteral( "Title" ) );
924  QDomText titleText;
925  if ( !title.isEmpty() )
926  titleText = doc.createTextNode( title );
927  else
928  titleText = doc.createTextNode( name );
929  titleElem.appendChild( titleText );
930  layerElem.appendChild( titleElem );
931 
932  QString abstract = treeGroupChild->customProperty( QStringLiteral( "wmsAbstract" ) ).toString();
933  if ( !abstract.isEmpty() )
934  {
935  QDomElement abstractElem = doc.createElement( QStringLiteral( "Abstract" ) );
936  QDomText abstractText = doc.createTextNode( abstract );
937  abstractElem.appendChild( abstractText );
938  layerElem.appendChild( abstractElem );
939  }
940 
941  // Layer tree name
942  if ( projectSettings )
943  {
944  QDomElement treeNameElem = doc.createElement( QStringLiteral( "TreeName" ) );
945  QDomText treeNameText = doc.createTextNode( name );
946  treeNameElem.appendChild( treeNameText );
947  layerElem.appendChild( treeNameElem );
948  }
949 
950  // Set queryable if any of the children are
951  if ( hasQueryableChildren( treeNode, restrictedLayers ) )
952  {
953  layerElem.setAttribute( QStringLiteral( "queryable" ), QStringLiteral( "1" ) );
954  }
955  else
956  {
957  layerElem.setAttribute( QStringLiteral( "queryable" ), QStringLiteral( "0" ) );
958  }
959 
960  appendLayersFromTreeGroup( doc, layerElem, serverIface, project, version, request, treeGroupChild, projectSettings );
961 
962  combineExtentAndCrsOfGroupChildren( doc, layerElem, project );
963  }
964  else
965  {
966  QgsLayerTreeLayer *treeLayer = static_cast<QgsLayerTreeLayer *>( treeNode );
967  QgsMapLayer *l = treeLayer->layer();
968  if ( restrictedLayers.contains( l->name() ) ) //unpublished layer
969  {
970  continue;
971  }
972 
973 #ifdef HAVE_SERVER_PYTHON_PLUGINS
974  QgsAccessControl *accessControl = serverIface->accessControls();
975  if ( accessControl && !accessControl->layerReadPermission( l ) )
976  {
977  continue;
978  }
979 #endif
980  QString wmsName = l->name();
981  if ( useLayerIds )
982  {
983  wmsName = l->id();
984  }
985  else if ( !l->shortName().isEmpty() )
986  {
987  wmsName = l->shortName();
988  }
989 
990  // queryable layer
991  if ( !l->flags().testFlag( QgsMapLayer::Identifiable ) )
992  {
993  layerElem.setAttribute( QStringLiteral( "queryable" ), QStringLiteral( "0" ) );
994  }
995  else
996  {
997  layerElem.setAttribute( QStringLiteral( "queryable" ), QStringLiteral( "1" ) );
998  }
999 
1000  QDomElement nameElem = doc.createElement( QStringLiteral( "Name" ) );
1001  QDomText nameText = doc.createTextNode( wmsName );
1002  nameElem.appendChild( nameText );
1003  layerElem.appendChild( nameElem );
1004 
1005  QDomElement titleElem = doc.createElement( QStringLiteral( "Title" ) );
1006  QString title = l->title();
1007  if ( title.isEmpty() )
1008  {
1009  title = l->name();
1010  }
1011  QDomText titleText = doc.createTextNode( title );
1012  titleElem.appendChild( titleText );
1013  layerElem.appendChild( titleElem );
1014 
1015  QString abstract = l->abstract();
1016  if ( !abstract.isEmpty() )
1017  {
1018  QDomElement abstractElem = doc.createElement( QStringLiteral( "Abstract" ) );
1019  QDomText abstractText = doc.createTextNode( abstract );
1020  abstractElem.appendChild( abstractText );
1021  layerElem.appendChild( abstractElem );
1022  }
1023 
1024  //keyword list
1025  if ( !l->keywordList().isEmpty() )
1026  {
1027  QStringList keywordStringList = l->keywordList().split( ',' );
1028 
1029  QDomElement keywordListElem = doc.createElement( QStringLiteral( "KeywordList" ) );
1030  for ( int i = 0; i < keywordStringList.size(); ++i )
1031  {
1032  QDomElement keywordElem = doc.createElement( QStringLiteral( "Keyword" ) );
1033  QDomText keywordText = doc.createTextNode( keywordStringList.at( i ).trimmed() );
1034  keywordElem.appendChild( keywordText );
1035  if ( siaFormat )
1036  {
1037  keywordElem.setAttribute( QStringLiteral( "vocabulary" ), QStringLiteral( "SIA_Geo405" ) );
1038  }
1039  keywordListElem.appendChild( keywordElem );
1040  }
1041  layerElem.appendChild( keywordListElem );
1042  }
1043 
1044  //vector layer without geometry
1045  bool geometryLayer = true;
1046  if ( l->type() == QgsMapLayerType::VectorLayer )
1047  {
1048  QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( l );
1049  if ( vLayer )
1050  {
1051  if ( vLayer->wkbType() == QgsWkbTypes::NoGeometry )
1052  {
1053  geometryLayer = false;
1054  }
1055  }
1056  }
1057 
1058  //CRS
1059  if ( geometryLayer )
1060  {
1061  QStringList crsList;
1062  crsList << l->crs().authid();
1063  QStringList outputCrsList = QgsServerProjectUtils::wmsOutputCrsList( *project );
1064  appendCrsElementsToLayer( doc, layerElem, crsList, outputCrsList );
1065 
1066  //Ex_GeographicBoundingBox
1067  QgsRectangle extent = l->extent(); // layer extent by default
1068  if ( l->type() == QgsMapLayerType::VectorLayer )
1069  {
1070  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( l );
1071  if ( vl && vl->featureCount() == 0 )
1072  {
1073  // if there's no feature, use the wms extent defined in the
1074  // project...
1075  extent = QgsServerProjectUtils::wmsExtent( *project );
1076  if ( extent.isNull() )
1077  {
1078  // or the CRS extent otherwise
1079  extent = vl->crs().bounds();
1080  }
1081  // If CRS is different transform it to layer's CRS
1082  else if ( vl->crs() != project->crs() )
1083  {
1084  try
1085  {
1086  QgsCoordinateTransform ct( project->crs(), vl->crs(), project->transformContext() );
1087  extent = ct.transform( extent );
1088  }
1089  catch ( QgsCsException &cse )
1090  {
1091  QgsMessageLog::logMessage( QStringLiteral( "Error transforming extent for layer %1: %2" ).arg( vl->name() ).arg( cse.what() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
1092  continue;
1093  }
1094  }
1095  }
1096  }
1097 
1098  appendLayerBoundingBoxes( doc, layerElem, extent, l->crs(), crsList, outputCrsList, project );
1099  }
1100 
1101  // add details about supported styles of the layer
1102  appendLayerStyles( doc, layerElem, l, project, version, request );
1103 
1104  //min/max scale denominatorScaleBasedVisibility
1105  if ( l->hasScaleBasedVisibility() )
1106  {
1107  if ( version == QLatin1String( "1.1.1" ) )
1108  {
1109  double OGC_PX_M = 0.00028; // OGC reference pixel size in meter, also used by qgis
1110  double SCALE_TO_SCALEHINT = OGC_PX_M * M_SQRT2;
1111 
1112  QDomElement scaleHintElem = doc.createElement( QStringLiteral( "ScaleHint" ) );
1113  scaleHintElem.setAttribute( QStringLiteral( "min" ), QString::number( l->maximumScale() * SCALE_TO_SCALEHINT ) );
1114  scaleHintElem.setAttribute( QStringLiteral( "max" ), QString::number( l->minimumScale() * SCALE_TO_SCALEHINT ) );
1115  layerElem.appendChild( scaleHintElem );
1116  }
1117  else
1118  {
1119  QString minScaleString = QString::number( l->maximumScale() );
1120  QDomElement minScaleElem = doc.createElement( QStringLiteral( "MinScaleDenominator" ) );
1121  QDomText minScaleText = doc.createTextNode( minScaleString );
1122  minScaleElem.appendChild( minScaleText );
1123  layerElem.appendChild( minScaleElem );
1124 
1125  QString maxScaleString = QString::number( l->minimumScale() );
1126  QDomElement maxScaleElem = doc.createElement( QStringLiteral( "MaxScaleDenominator" ) );
1127  QDomText maxScaleText = doc.createTextNode( maxScaleString );
1128  maxScaleElem.appendChild( maxScaleText );
1129  layerElem.appendChild( maxScaleElem );
1130  }
1131  }
1132 
1133  // layer data URL
1134  QString dataUrl = l->dataUrl();
1135  if ( !dataUrl.isEmpty() )
1136  {
1137  QDomElement dataUrlElem = doc.createElement( QStringLiteral( "DataURL" ) );
1138  QDomElement dataUrlFormatElem = doc.createElement( QStringLiteral( "Format" ) );
1139  QString dataUrlFormat = l->dataUrlFormat();
1140  QDomText dataUrlFormatText = doc.createTextNode( dataUrlFormat );
1141  dataUrlFormatElem.appendChild( dataUrlFormatText );
1142  dataUrlElem.appendChild( dataUrlFormatElem );
1143  QDomElement dataORElem = doc.createElement( QStringLiteral( "OnlineResource" ) );
1144  dataORElem.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
1145  dataORElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) );
1146  dataORElem.setAttribute( QStringLiteral( "xlink:href" ), dataUrl );
1147  dataUrlElem.appendChild( dataORElem );
1148  layerElem.appendChild( dataUrlElem );
1149  }
1150 
1151  // layer attribution
1152  QString attribution = l->attribution();
1153  if ( !attribution.isEmpty() )
1154  {
1155  QDomElement attribElem = doc.createElement( QStringLiteral( "Attribution" ) );
1156  QDomElement attribTitleElem = doc.createElement( QStringLiteral( "Title" ) );
1157  QDomText attribText = doc.createTextNode( attribution );
1158  attribTitleElem.appendChild( attribText );
1159  attribElem.appendChild( attribTitleElem );
1160  QString attributionUrl = l->attributionUrl();
1161  if ( !attributionUrl.isEmpty() )
1162  {
1163  QDomElement attribORElem = doc.createElement( QStringLiteral( "OnlineResource" ) );
1164  attribORElem.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
1165  attribORElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) );
1166  attribORElem.setAttribute( QStringLiteral( "xlink:href" ), attributionUrl );
1167  attribElem.appendChild( attribORElem );
1168  }
1169  layerElem.appendChild( attribElem );
1170  }
1171 
1172  // layer metadata URL
1173  QString metadataUrl = l->metadataUrl();
1174  if ( !metadataUrl.isEmpty() )
1175  {
1176  QDomElement metaUrlElem = doc.createElement( QStringLiteral( "MetadataURL" ) );
1177  QString metadataUrlType = l->metadataUrlType();
1178  if ( version == QLatin1String( "1.1.1" ) )
1179  {
1180  metaUrlElem.setAttribute( QStringLiteral( "type" ), metadataUrlType );
1181  }
1182  else if ( metadataUrlType == QLatin1String( "FGDC" ) )
1183  {
1184  metaUrlElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "FGDC:1998" ) );
1185  }
1186  else if ( metadataUrlType == QLatin1String( "TC211" ) )
1187  {
1188  metaUrlElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "ISO19115:2003" ) );
1189  }
1190  else
1191  {
1192  metaUrlElem.setAttribute( QStringLiteral( "type" ), metadataUrlType );
1193  }
1194  QString metadataUrlFormat = l->metadataUrlFormat();
1195  if ( !metadataUrlFormat.isEmpty() )
1196  {
1197  QDomElement metaUrlFormatElem = doc.createElement( QStringLiteral( "Format" ) );
1198  QDomText metaUrlFormatText = doc.createTextNode( metadataUrlFormat );
1199  metaUrlFormatElem.appendChild( metaUrlFormatText );
1200  metaUrlElem.appendChild( metaUrlFormatElem );
1201  }
1202  QDomElement metaUrlORElem = doc.createElement( QStringLiteral( "OnlineResource" ) );
1203  metaUrlORElem.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
1204  metaUrlORElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) );
1205  metaUrlORElem.setAttribute( QStringLiteral( "xlink:href" ), metadataUrl );
1206  metaUrlElem.appendChild( metaUrlORElem );
1207  layerElem.appendChild( metaUrlElem );
1208  }
1209 
1210  // Add dimensions
1211  if ( l->type() == QgsMapLayerType::VectorLayer )
1212  {
1213  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( l );
1214  const QList<QgsVectorLayerServerProperties::WmsDimensionInfo> wmsDims = vl->serverProperties()->wmsDimensions();
1215  for ( const QgsVectorLayerServerProperties::WmsDimensionInfo &dim : wmsDims )
1216  {
1217  int fieldIndex = vl->fields().indexOf( dim.fieldName );
1218  // Check field index
1219  if ( fieldIndex == -1 )
1220  {
1221  continue;
1222  }
1223  // get unique values
1224  QSet<QVariant> uniqueValues = vl->uniqueValues( fieldIndex );
1225 
1226  // get unique values from endfield name if define
1227  if ( !dim.endFieldName.isEmpty() )
1228  {
1229  int endFieldIndex = vl->fields().indexOf( dim.endFieldName );
1230  // Check end field index
1231  if ( endFieldIndex == -1 )
1232  {
1233  continue;
1234  }
1235  uniqueValues.unite( vl->uniqueValues( endFieldIndex ) );
1236  }
1237  // sort unique values
1238  QList<QVariant> values = uniqueValues.toList();
1239  std::sort( values.begin(), values.end() );
1240 
1241  QDomElement dimElem = doc.createElement( QStringLiteral( "Dimension" ) );
1242  dimElem.setAttribute( QStringLiteral( "name" ), dim.name );
1243  if ( !dim.units.isEmpty() )
1244  {
1245  dimElem.setAttribute( QStringLiteral( "units" ), dim.units );
1246  }
1247  if ( !dim.unitSymbol.isEmpty() )
1248  {
1249  dimElem.setAttribute( QStringLiteral( "unitSymbol" ), dim.unitSymbol );
1250  }
1251  if ( dim.defaultDisplayType == QgsVectorLayerServerProperties::WmsDimensionInfo::MinValue )
1252  {
1253  dimElem.setAttribute( QStringLiteral( "default" ), values.first().toString() );
1254  }
1255  else if ( dim.defaultDisplayType == QgsVectorLayerServerProperties::WmsDimensionInfo::MaxValue )
1256  {
1257  dimElem.setAttribute( QStringLiteral( "default" ), values.last().toString() );
1258  }
1259  else if ( dim.defaultDisplayType == QgsVectorLayerServerProperties::WmsDimensionInfo::ReferenceValue )
1260  {
1261  dimElem.setAttribute( QStringLiteral( "default" ), dim.referenceValue.toString() );
1262  }
1263  dimElem.setAttribute( QStringLiteral( "multipleValue" ), '1' );
1264  dimElem.setAttribute( QStringLiteral( "nearestValue" ), '0' );
1265  // values list
1266  QStringList strValues;
1267  for ( const QVariant &v : values )
1268  {
1269  strValues << v.toString();
1270  }
1271  QDomText dimValuesText = doc.createTextNode( strValues.join( QStringLiteral( ", " ) ) );
1272  dimElem.appendChild( dimValuesText );
1273  layerElem.appendChild( dimElem );
1274  }
1275  }
1276 
1277  if ( projectSettings )
1278  {
1279  appendLayerProjectSettings( doc, layerElem, l );
1280  }
1281  }
1282 
1283  parentLayer.appendChild( layerElem );
1284  }
1285  }
1286 
1287  void appendLayerStyles( QDomDocument &doc, QDomElement &layerElem, QgsMapLayer *currentLayer,
1288  const QgsProject *project, const QString &version, const QgsServerRequest &request )
1289  {
1290  // Get service URL
1291  QUrl href = serviceUrl( request, project );
1292 
1293  //href needs to be a prefix
1294  QString hrefString = href.toString();
1295  hrefString.append( href.hasQuery() ? "&" : "?" );
1296  for ( const QString &styleName : currentLayer->styleManager()->styles() )
1297  {
1298  QDomElement styleElem = doc.createElement( QStringLiteral( "Style" ) );
1299  QDomElement styleNameElem = doc.createElement( QStringLiteral( "Name" ) );
1300  QDomText styleNameText = doc.createTextNode( styleName );
1301  styleNameElem.appendChild( styleNameText );
1302  QDomElement styleTitleElem = doc.createElement( QStringLiteral( "Title" ) );
1303  QDomText styleTitleText = doc.createTextNode( styleName );
1304  styleTitleElem.appendChild( styleTitleText );
1305  styleElem.appendChild( styleNameElem );
1306  styleElem.appendChild( styleTitleElem );
1307 
1308  // QString LegendURL for explicit layerbased GetLegendGraphic request
1309  QDomElement getLayerLegendGraphicElem = doc.createElement( QStringLiteral( "LegendURL" ) );
1310 
1311  QString customHrefString = currentLayer->legendUrl();
1312 
1313  QStringList getLayerLegendGraphicFormats;
1314  if ( !customHrefString.isEmpty() )
1315  {
1316  getLayerLegendGraphicFormats << currentLayer->legendUrlFormat();
1317  }
1318  else
1319  {
1320  getLayerLegendGraphicFormats << QStringLiteral( "image/png" ); // << "jpeg" << "image/jpeg"
1321  }
1322 
1323  for ( int i = 0; i < getLayerLegendGraphicFormats.size(); ++i )
1324  {
1325  QDomElement getLayerLegendGraphicFormatElem = doc.createElement( QStringLiteral( "Format" ) );
1326  QString getLayerLegendGraphicFormat = getLayerLegendGraphicFormats[i];
1327  QDomText getLayerLegendGraphicFormatText = doc.createTextNode( getLayerLegendGraphicFormat );
1328  getLayerLegendGraphicFormatElem.appendChild( getLayerLegendGraphicFormatText );
1329  getLayerLegendGraphicElem.appendChild( getLayerLegendGraphicFormatElem );
1330  }
1331 
1332  // no parameters on custom hrefUrl, because should link directly to graphic
1333  if ( customHrefString.isEmpty() )
1334  {
1335  QString layerName = currentLayer->name();
1336  if ( QgsServerProjectUtils::wmsUseLayerIds( *project ) )
1337  layerName = currentLayer->id();
1338  else if ( !currentLayer->shortName().isEmpty() )
1339  layerName = currentLayer->shortName();
1340  QUrlQuery mapUrl( hrefString );
1341  mapUrl.addQueryItem( QStringLiteral( "SERVICE" ), QStringLiteral( "WMS" ) );
1342  mapUrl.addQueryItem( QStringLiteral( "VERSION" ), version );
1343  mapUrl.addQueryItem( QStringLiteral( "REQUEST" ), QStringLiteral( "GetLegendGraphic" ) );
1344  mapUrl.addQueryItem( QStringLiteral( "LAYER" ), layerName );
1345  mapUrl.addQueryItem( QStringLiteral( "FORMAT" ), QStringLiteral( "image/png" ) );
1346  mapUrl.addQueryItem( QStringLiteral( "STYLE" ), styleNameText.data() );
1347  if ( version == QLatin1String( "1.3.0" ) )
1348  {
1349  mapUrl.addQueryItem( QStringLiteral( "SLD_VERSION" ), QStringLiteral( "1.1.0" ) );
1350  }
1351  customHrefString = mapUrl.toString();
1352  }
1353 
1354  QDomElement getLayerLegendGraphicORElem = doc.createElement( QStringLiteral( "OnlineResource" ) );
1355  getLayerLegendGraphicORElem.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
1356  getLayerLegendGraphicORElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) );
1357  getLayerLegendGraphicORElem.setAttribute( QStringLiteral( "xlink:href" ), customHrefString );
1358  getLayerLegendGraphicElem.appendChild( getLayerLegendGraphicORElem );
1359  styleElem.appendChild( getLayerLegendGraphicElem );
1360 
1361  layerElem.appendChild( styleElem );
1362  }
1363  }
1364 
1365  void appendCrsElementsToLayer( QDomDocument &doc, QDomElement &layerElement,
1366  const QStringList &crsList, const QStringList &constrainedCrsList )
1367  {
1368  if ( layerElement.isNull() )
1369  {
1370  return;
1371  }
1372 
1373  //insert the CRS elements after the title element to be in accordance with the WMS 1.3 specification
1374  QDomElement titleElement = layerElement.firstChildElement( QStringLiteral( "Title" ) );
1375  QDomElement abstractElement = layerElement.firstChildElement( QStringLiteral( "Abstract" ) );
1376  QDomElement CRSPrecedingElement = abstractElement.isNull() ? titleElement : abstractElement; //last element before the CRS elements
1377 
1378  if ( CRSPrecedingElement.isNull() )
1379  {
1380  // keyword list element is never empty
1381  const QDomElement keyElement = layerElement.firstChildElement( QStringLiteral( "KeywordList" ) );
1382  CRSPrecedingElement = keyElement;
1383  }
1384 
1385  //In case the number of advertised CRS is constrained
1386  if ( !constrainedCrsList.isEmpty() )
1387  {
1388  for ( int i = constrainedCrsList.size() - 1; i >= 0; --i )
1389  {
1390  appendCrsElementToLayer( doc, layerElement, CRSPrecedingElement, constrainedCrsList.at( i ) );
1391  }
1392  }
1393  else //no crs constraint
1394  {
1395  for ( const QString &crs : crsList )
1396  {
1397  appendCrsElementToLayer( doc, layerElement, CRSPrecedingElement, crs );
1398  }
1399  }
1400 
1401  //Support for CRS:84 is mandatory (equals EPSG:4326 with reversed axis)
1402  appendCrsElementToLayer( doc, layerElement, CRSPrecedingElement, QString( "CRS:84" ) );
1403  }
1404 
1405  void appendCrsElementToLayer( QDomDocument &doc, QDomElement &layerElement, const QDomElement &precedingElement,
1406  const QString &crsText )
1407  {
1408  if ( crsText.isEmpty() )
1409  return;
1410  QString version = doc.documentElement().attribute( QStringLiteral( "version" ) );
1411  QDomElement crsElement = doc.createElement( version == QLatin1String( "1.1.1" ) ? "SRS" : "CRS" );
1412  QDomText crsTextNode = doc.createTextNode( crsText );
1413  crsElement.appendChild( crsTextNode );
1414  layerElement.insertAfter( crsElement, precedingElement );
1415  }
1416 
1417  void appendLayerBoundingBoxes( QDomDocument &doc, QDomElement &layerElem, const QgsRectangle &lExtent,
1418  const QgsCoordinateReferenceSystem &layerCRS, const QStringList &crsList,
1419  const QStringList &constrainedCrsList, const QgsProject *project )
1420  {
1421  if ( layerElem.isNull() )
1422  {
1423  return;
1424  }
1425 
1426  QgsRectangle layerExtent = lExtent;
1427  if ( qgsDoubleNear( layerExtent.xMinimum(), layerExtent.xMaximum() ) || qgsDoubleNear( layerExtent.yMinimum(), layerExtent.yMaximum() ) )
1428  {
1429  //layer bbox cannot be empty
1430  layerExtent.grow( 0.000001 );
1431  }
1432 
1434 
1435  QString version = doc.documentElement().attribute( QStringLiteral( "version" ) );
1436 
1437  //Ex_GeographicBoundingBox
1438  QDomElement ExGeoBBoxElement;
1439  //transform the layers native CRS into WGS84
1440  QgsRectangle wgs84BoundingRect;
1441  if ( !layerExtent.isNull() )
1442  {
1443  QgsCoordinateTransform exGeoTransform( layerCRS, wgs84, project );
1444  try
1445  {
1446  wgs84BoundingRect = exGeoTransform.transformBoundingBox( layerExtent );
1447  }
1448  catch ( const QgsCsException &cse )
1449  {
1450  QgsMessageLog::logMessage( QStringLiteral( "Error transforming extent: %1" ).arg( cse.what() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
1451  wgs84BoundingRect = QgsRectangle();
1452  }
1453  }
1454 
1455  if ( version == QLatin1String( "1.1.1" ) ) // WMS Version 1.1.1
1456  {
1457  ExGeoBBoxElement = doc.createElement( QStringLiteral( "LatLonBoundingBox" ) );
1458  ExGeoBBoxElement.setAttribute( QStringLiteral( "minx" ), QString::number( wgs84BoundingRect.xMinimum() ) );
1459  ExGeoBBoxElement.setAttribute( QStringLiteral( "maxx" ), QString::number( wgs84BoundingRect.xMaximum() ) );
1460  ExGeoBBoxElement.setAttribute( QStringLiteral( "miny" ), QString::number( wgs84BoundingRect.yMinimum() ) );
1461  ExGeoBBoxElement.setAttribute( QStringLiteral( "maxy" ), QString::number( wgs84BoundingRect.yMaximum() ) );
1462  }
1463  else // WMS Version 1.3.0
1464  {
1465  ExGeoBBoxElement = doc.createElement( QStringLiteral( "EX_GeographicBoundingBox" ) );
1466  QDomElement wBoundLongitudeElement = doc.createElement( QStringLiteral( "westBoundLongitude" ) );
1467  QDomText wBoundLongitudeText = doc.createTextNode( QString::number( wgs84BoundingRect.xMinimum() ) );
1468  wBoundLongitudeElement.appendChild( wBoundLongitudeText );
1469  ExGeoBBoxElement.appendChild( wBoundLongitudeElement );
1470  QDomElement eBoundLongitudeElement = doc.createElement( QStringLiteral( "eastBoundLongitude" ) );
1471  QDomText eBoundLongitudeText = doc.createTextNode( QString::number( wgs84BoundingRect.xMaximum() ) );
1472  eBoundLongitudeElement.appendChild( eBoundLongitudeText );
1473  ExGeoBBoxElement.appendChild( eBoundLongitudeElement );
1474  QDomElement sBoundLatitudeElement = doc.createElement( QStringLiteral( "southBoundLatitude" ) );
1475  QDomText sBoundLatitudeText = doc.createTextNode( QString::number( wgs84BoundingRect.yMinimum() ) );
1476  sBoundLatitudeElement.appendChild( sBoundLatitudeText );
1477  ExGeoBBoxElement.appendChild( sBoundLatitudeElement );
1478  QDomElement nBoundLatitudeElement = doc.createElement( QStringLiteral( "northBoundLatitude" ) );
1479  QDomText nBoundLatitudeText = doc.createTextNode( QString::number( wgs84BoundingRect.yMaximum() ) );
1480  nBoundLatitudeElement.appendChild( nBoundLatitudeText );
1481  ExGeoBBoxElement.appendChild( nBoundLatitudeElement );
1482  }
1483 
1484  if ( !wgs84BoundingRect.isNull() ) //LatLonBoundingBox / Ex_GeographicBounding box is optional
1485  {
1486  QDomElement lastCRSElem = layerElem.lastChildElement( version == QLatin1String( "1.1.1" ) ? "SRS" : "CRS" );
1487  if ( !lastCRSElem.isNull() )
1488  {
1489  layerElem.insertAfter( ExGeoBBoxElement, lastCRSElem );
1490  }
1491  else
1492  {
1493  layerElem.appendChild( ExGeoBBoxElement );
1494  }
1495  }
1496 
1497  //In case the number of advertised CRS is constrained
1498  if ( !constrainedCrsList.isEmpty() )
1499  {
1500  for ( int i = constrainedCrsList.size() - 1; i >= 0; --i )
1501  {
1502  appendLayerBoundingBox( doc, layerElem, layerExtent, layerCRS, constrainedCrsList.at( i ), project );
1503  }
1504  }
1505  else //no crs constraint
1506  {
1507  for ( const QString &crs : crsList )
1508  {
1509  appendLayerBoundingBox( doc, layerElem, layerExtent, layerCRS, crs, project );
1510  }
1511  }
1512  }
1513 
1514 
1515  void appendLayerBoundingBox( QDomDocument &doc, QDomElement &layerElem, const QgsRectangle &layerExtent,
1516  const QgsCoordinateReferenceSystem &layerCRS, const QString &crsText,
1517  const QgsProject *project )
1518  {
1519  if ( layerElem.isNull() )
1520  {
1521  return;
1522  }
1523 
1524  if ( crsText.isEmpty() )
1525  {
1526  return;
1527  }
1528 
1529  QString version = doc.documentElement().attribute( QStringLiteral( "version" ) );
1530 
1532 
1533  //transform the layers native CRS into CRS
1534  QgsRectangle crsExtent;
1535  if ( !layerExtent.isNull() )
1536  {
1537  QgsCoordinateTransform crsTransform( layerCRS, crs, project );
1538  try
1539  {
1540  crsExtent = crsTransform.transformBoundingBox( layerExtent );
1541  }
1542  catch ( QgsCsException &cse )
1543  {
1544  QgsMessageLog::logMessage( QStringLiteral( "Error transforming extent: %1" ).arg( cse.what() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
1545  return;
1546  }
1547  }
1548 
1549  if ( crsExtent.isNull() )
1550  {
1551  return;
1552  }
1553 
1554  //BoundingBox element
1555  QDomElement bBoxElement = doc.createElement( QStringLiteral( "BoundingBox" ) );
1556  if ( crs.isValid() )
1557  {
1558  bBoxElement.setAttribute( version == QLatin1String( "1.1.1" ) ? "SRS" : "CRS", crs.authid() );
1559  }
1560 
1561  if ( version != QLatin1String( "1.1.1" ) && crs.hasAxisInverted() )
1562  {
1563  crsExtent.invert();
1564  }
1565 
1566  bBoxElement.setAttribute( QStringLiteral( "minx" ), QString::number( crsExtent.xMinimum() ) );
1567  bBoxElement.setAttribute( QStringLiteral( "miny" ), QString::number( crsExtent.yMinimum() ) );
1568  bBoxElement.setAttribute( QStringLiteral( "maxx" ), QString::number( crsExtent.xMaximum() ) );
1569  bBoxElement.setAttribute( QStringLiteral( "maxy" ), QString::number( crsExtent.yMaximum() ) );
1570 
1571  QDomElement lastBBoxElem = layerElem.lastChildElement( QStringLiteral( "BoundingBox" ) );
1572  if ( !lastBBoxElem.isNull() )
1573  {
1574  layerElem.insertAfter( bBoxElement, lastBBoxElem );
1575  }
1576  else
1577  {
1578  lastBBoxElem = layerElem.lastChildElement( version == QLatin1String( "1.1.1" ) ? "LatLonBoundingBox" : "EX_GeographicBoundingBox" );
1579  if ( !lastBBoxElem.isNull() )
1580  {
1581  layerElem.insertAfter( bBoxElement, lastBBoxElem );
1582  }
1583  else
1584  {
1585  layerElem.appendChild( bBoxElement );
1586  }
1587  }
1588  }
1589 
1590  QgsRectangle layerBoundingBoxInProjectCrs( const QDomDocument &doc, const QDomElement &layerElem,
1591  const QgsProject *project )
1592  {
1593  QgsRectangle BBox;
1594  if ( layerElem.isNull() )
1595  {
1596  return BBox;
1597  }
1598 
1599  //read box coordinates and layer auth. id
1600  QDomElement boundingBoxElem = layerElem.firstChildElement( QStringLiteral( "BoundingBox" ) );
1601  if ( boundingBoxElem.isNull() )
1602  {
1603  return BBox;
1604  }
1605 
1606  double minx, miny, maxx, maxy;
1607  bool conversionOk;
1608  minx = boundingBoxElem.attribute( QStringLiteral( "minx" ) ).toDouble( &conversionOk );
1609  if ( !conversionOk )
1610  {
1611  return BBox;
1612  }
1613  miny = boundingBoxElem.attribute( QStringLiteral( "miny" ) ).toDouble( &conversionOk );
1614  if ( !conversionOk )
1615  {
1616  return BBox;
1617  }
1618  maxx = boundingBoxElem.attribute( QStringLiteral( "maxx" ) ).toDouble( &conversionOk );
1619  if ( !conversionOk )
1620  {
1621  return BBox;
1622  }
1623  maxy = boundingBoxElem.attribute( QStringLiteral( "maxy" ) ).toDouble( &conversionOk );
1624  if ( !conversionOk )
1625  {
1626  return BBox;
1627  }
1628 
1629 
1630  QString version = doc.documentElement().attribute( QStringLiteral( "version" ) );
1631 
1632  //create layer crs
1633  QgsCoordinateReferenceSystem layerCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( boundingBoxElem.attribute( version == QLatin1String( "1.1.1" ) ? "SRS" : "CRS" ) );
1634  if ( !layerCrs.isValid() )
1635  {
1636  return BBox;
1637  }
1638 
1639  BBox.setXMinimum( minx );
1640  BBox.setXMaximum( maxx );
1641  BBox.setYMinimum( miny );
1642  BBox.setYMaximum( maxy );
1643 
1644  if ( version != QLatin1String( "1.1.1" ) && layerCrs.hasAxisInverted() )
1645  {
1646  BBox.invert();
1647  }
1648 
1649  //get project crs
1650  QgsCoordinateTransform t( layerCrs, project->crs(), project );
1651 
1652  //transform
1653  try
1654  {
1655  BBox = t.transformBoundingBox( BBox );
1656  }
1657  catch ( const QgsCsException &cse )
1658  {
1659  QgsMessageLog::logMessage( QStringLiteral( "Error transforming extent: %1" ).arg( cse.what() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
1660  BBox = QgsRectangle();
1661  }
1662 
1663  return BBox;
1664  }
1665 
1666  bool crsSetFromLayerElement( const QDomElement &layerElement, QSet<QString> &crsSet )
1667  {
1668  if ( layerElement.isNull() )
1669  {
1670  return false;
1671  }
1672 
1673  crsSet.clear();
1674 
1675  QDomNodeList crsNodeList;
1676  crsNodeList = layerElement.elementsByTagName( QStringLiteral( "CRS" ) ); // WMS 1.3.0
1677  for ( int i = 0; i < crsNodeList.size(); ++i )
1678  {
1679  crsSet.insert( crsNodeList.at( i ).toElement().text() );
1680  }
1681 
1682  crsNodeList = layerElement.elementsByTagName( QStringLiteral( "SRS" ) ); // WMS 1.1.1
1683  for ( int i = 0; i < crsNodeList.size(); ++i )
1684  {
1685  crsSet.insert( crsNodeList.at( i ).toElement().text() );
1686  }
1687 
1688  return true;
1689  }
1690 
1691  void combineExtentAndCrsOfGroupChildren( QDomDocument &doc, QDomElement &groupElem, const QgsProject *project,
1692  bool considerMapExtent )
1693  {
1694  QgsRectangle combinedBBox;
1695  QSet<QString> combinedCRSSet;
1696  bool firstBBox = true;
1697  bool firstCRSSet = true;
1698 
1699  QDomNodeList layerChildren = groupElem.childNodes();
1700  for ( int j = 0; j < layerChildren.size(); ++j )
1701  {
1702  QDomElement childElem = layerChildren.at( j ).toElement();
1703 
1704  if ( childElem.tagName() != QLatin1String( "Layer" ) )
1705  continue;
1706 
1707  QgsRectangle bbox = layerBoundingBoxInProjectCrs( doc, childElem, project );
1708  if ( bbox.isNull() )
1709  {
1710  continue;
1711  }
1712 
1713  if ( !bbox.isEmpty() )
1714  {
1715  if ( firstBBox )
1716  {
1717  combinedBBox = bbox;
1718  firstBBox = false;
1719  }
1720  else
1721  {
1722  combinedBBox.combineExtentWith( bbox );
1723  }
1724  }
1725 
1726  //combine crs set
1727  QSet<QString> crsSet;
1728  if ( crsSetFromLayerElement( childElem, crsSet ) )
1729  {
1730  if ( firstCRSSet )
1731  {
1732  combinedCRSSet = crsSet;
1733  firstCRSSet = false;
1734  }
1735  else
1736  {
1737  combinedCRSSet.intersect( crsSet );
1738  }
1739  }
1740  }
1741 
1742  QStringList outputCrsList = QgsServerProjectUtils::wmsOutputCrsList( *project );
1743  appendCrsElementsToLayer( doc, groupElem, combinedCRSSet.toList(), outputCrsList );
1744 
1745  QgsCoordinateReferenceSystem groupCRS = project->crs();
1746  if ( considerMapExtent )
1747  {
1748  QgsRectangle mapRect = QgsServerProjectUtils::wmsExtent( *project );
1749  if ( !mapRect.isEmpty() )
1750  {
1751  combinedBBox = mapRect;
1752  }
1753  }
1754  appendLayerBoundingBoxes( doc, groupElem, combinedBBox, groupCRS, combinedCRSSet.toList(), outputCrsList, project );
1755 
1756  }
1757 
1758  void appendDrawingOrder( QDomDocument &doc, QDomElement &parentElem, QgsServerInterface *serverIface,
1759  const QgsProject *project )
1760  {
1761 #ifdef HAVE_SERVER_PYTHON_PLUGINS
1762  QgsAccessControl *accessControl = serverIface->accessControls();
1763 #else
1764  ( void )serverIface;
1765 #endif
1766  bool useLayerIds = QgsServerProjectUtils::wmsUseLayerIds( *project );
1767  QStringList restrictedLayers = QgsServerProjectUtils::wmsRestrictedLayers( *project );
1768 
1769  QStringList layerList;
1770 
1771  const QgsLayerTree *projectLayerTreeRoot = project->layerTreeRoot();
1772  QList< QgsMapLayer * > projectLayerOrder = projectLayerTreeRoot->layerOrder();
1773  for ( int i = 0; i < projectLayerOrder.size(); ++i )
1774  {
1775  QgsMapLayer *l = projectLayerOrder.at( i );
1776 
1777  if ( restrictedLayers.contains( l->name() ) ) //unpublished layer
1778  {
1779  continue;
1780  }
1781 #ifdef HAVE_SERVER_PYTHON_PLUGINS
1782  if ( accessControl && !accessControl->layerReadPermission( l ) )
1783  {
1784  continue;
1785  }
1786 #endif
1787  QString wmsName = l->name();
1788  if ( useLayerIds )
1789  {
1790  wmsName = l->id();
1791  }
1792  else if ( !l->shortName().isEmpty() )
1793  {
1794  wmsName = l->shortName();
1795  }
1796 
1797  layerList << wmsName;
1798  }
1799 
1800  if ( !layerList.isEmpty() )
1801  {
1802  QStringList reversedList;
1803  reversedList.reserve( layerList.size() );
1804  for ( int i = layerList.size() - 1; i >= 0; --i )
1805  reversedList << layerList[ i ];
1806 
1807  QDomElement layerDrawingOrderElem = doc.createElement( QStringLiteral( "LayerDrawingOrder" ) );
1808  QDomText drawingOrderText = doc.createTextNode( reversedList.join( ',' ) );
1809  layerDrawingOrderElem.appendChild( drawingOrderText );
1810  parentElem.appendChild( layerDrawingOrderElem );
1811  }
1812  }
1813 
1814  void appendLayerProjectSettings( QDomDocument &doc, QDomElement &layerElem, QgsMapLayer *currentLayer )
1815  {
1816  if ( !currentLayer )
1817  {
1818  return;
1819  }
1820 
1821  // Layer tree name
1822  QDomElement treeNameElem = doc.createElement( QStringLiteral( "TreeName" ) );
1823  QDomText treeNameText = doc.createTextNode( currentLayer->name() );
1824  treeNameElem.appendChild( treeNameText );
1825  layerElem.appendChild( treeNameElem );
1826 
1827  switch ( currentLayer->type() )
1828  {
1830  {
1831  QgsVectorLayer *vLayer = static_cast<QgsVectorLayer *>( currentLayer );
1832  const QSet<QString> &excludedAttributes = vLayer->excludeAttributesWms();
1833 
1834  int displayFieldIdx = -1;
1835  QString displayField = QStringLiteral( "maptip" );
1836  QgsExpression exp( vLayer->displayExpression() );
1837  if ( exp.isField() )
1838  {
1839  displayField = static_cast<const QgsExpressionNodeColumnRef *>( exp.rootNode() )->name();
1840  displayFieldIdx = vLayer->fields().lookupField( displayField );
1841  }
1842 
1843  //attributes
1844  QDomElement attributesElem = doc.createElement( QStringLiteral( "Attributes" ) );
1845  const QgsFields layerFields = vLayer->fields();
1846  for ( int idx = 0; idx < layerFields.count(); ++idx )
1847  {
1848  QgsField field = layerFields.at( idx );
1849  if ( excludedAttributes.contains( field.name() ) )
1850  {
1851  continue;
1852  }
1853  // field alias in case of displayField
1854  if ( idx == displayFieldIdx )
1855  {
1856  displayField = vLayer->attributeDisplayName( idx );
1857  }
1858  QDomElement attributeElem = doc.createElement( QStringLiteral( "Attribute" ) );
1859  attributeElem.setAttribute( QStringLiteral( "name" ), field.name() );
1860  attributeElem.setAttribute( QStringLiteral( "type" ), QVariant::typeToName( field.type() ) );
1861  attributeElem.setAttribute( QStringLiteral( "typeName" ), field.typeName() );
1862  QString alias = field.alias();
1863  if ( !alias.isEmpty() )
1864  {
1865  attributeElem.setAttribute( QStringLiteral( "alias" ), alias );
1866  }
1867 
1868  //edit type to text
1869  attributeElem.setAttribute( QStringLiteral( "editType" ), vLayer->editorWidgetSetup( idx ).type() );
1870  attributeElem.setAttribute( QStringLiteral( "comment" ), field.comment() );
1871  attributeElem.setAttribute( QStringLiteral( "length" ), field.length() );
1872  attributeElem.setAttribute( QStringLiteral( "precision" ), field.precision() );
1873  attributesElem.appendChild( attributeElem );
1874  }
1875 
1876  //displayfield
1877  layerElem.setAttribute( QStringLiteral( "displayField" ), displayField );
1878 
1879  //primary key
1880  QgsAttributeList pkAttributes = vLayer->primaryKeyAttributes();
1881  if ( pkAttributes.size() > 0 )
1882  {
1883  QDomElement pkElem = doc.createElement( QStringLiteral( "PrimaryKey" ) );
1884  QgsAttributeList::const_iterator pkIt = pkAttributes.constBegin();
1885  for ( ; pkIt != pkAttributes.constEnd(); ++pkIt )
1886  {
1887  QDomElement pkAttributeElem = doc.createElement( QStringLiteral( "PrimaryKeyAttribute" ) );
1888  QDomText pkAttName = doc.createTextNode( layerFields.at( *pkIt ).name() );
1889  pkAttributeElem.appendChild( pkAttName );
1890  pkElem.appendChild( pkAttributeElem );
1891  }
1892  layerElem.appendChild( pkElem );
1893  }
1894 
1895  //geometry type
1896  layerElem.setAttribute( QStringLiteral( "geometryType" ), QgsWkbTypes::displayString( vLayer->wkbType() ) );
1897 
1898  layerElem.appendChild( attributesElem );
1899  break;
1900  }
1901 
1903  {
1904  const QgsDataProvider *provider = currentLayer->dataProvider();
1905  if ( provider && provider->name() == "wms" )
1906  {
1907  //advertise as web map background layer
1908  QVariant wmsBackgroundLayer = currentLayer->customProperty( QStringLiteral( "WMSBackgroundLayer" ), false );
1909  QDomElement wmsBackgroundLayerElem = doc.createElement( "WMSBackgroundLayer" );
1910  QDomText wmsBackgroundLayerText = doc.createTextNode( wmsBackgroundLayer.toBool() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
1911  wmsBackgroundLayerElem.appendChild( wmsBackgroundLayerText );
1912  layerElem.appendChild( wmsBackgroundLayerElem );
1913 
1914  //publish datasource
1915  QVariant wmsPublishDataSourceUrl = currentLayer->customProperty( QStringLiteral( "WMSPublishDataSourceUrl" ), false );
1916  if ( wmsPublishDataSourceUrl.toBool() )
1917  {
1918  bool tiled = qobject_cast< const QgsRasterDataProvider * >( provider )
1919  ? !qobject_cast< const QgsRasterDataProvider * >( provider )->nativeResolutions().isEmpty()
1920  : false;
1921 
1922  QDomElement dataSourceElem = doc.createElement( tiled ? QStringLiteral( "WMTSDataSource" ) : QStringLiteral( "WMSDataSource" ) );
1923  QDomText dataSourceUri = doc.createTextNode( provider->dataSourceUri() );
1924  dataSourceElem.appendChild( dataSourceUri );
1925  layerElem.appendChild( dataSourceElem );
1926  }
1927  }
1928 
1929  QVariant wmsPrintLayer = currentLayer->customProperty( QStringLiteral( "WMSPrintLayer" ) );
1930  if ( wmsPrintLayer.isValid() )
1931  {
1932  QDomElement wmsPrintLayerElem = doc.createElement( "WMSPrintLayer" );
1933  QDomText wmsPrintLayerText = doc.createTextNode( wmsPrintLayer.toString() );
1934  wmsPrintLayerElem.appendChild( wmsPrintLayerText );
1935  layerElem.appendChild( wmsPrintLayerElem );
1936  }
1937  break;
1938  }
1939 
1942  break;
1943  }
1944  }
1945 
1946  void addKeywordListElement( const QgsProject *project, QDomDocument &doc, QDomElement &parent )
1947  {
1948  bool sia2045 = QgsServerProjectUtils::wmsInfoFormatSia2045( *project );
1949 
1950  QDomElement keywordsElem = doc.createElement( QStringLiteral( "KeywordList" ) );
1951  //add default keyword
1952  QDomElement keywordElem = doc.createElement( QStringLiteral( "Keyword" ) );
1953  keywordElem.setAttribute( QStringLiteral( "vocabulary" ), QStringLiteral( "ISO" ) );
1954  QDomText keywordText = doc.createTextNode( QStringLiteral( "infoMapAccessService" ) );
1955  keywordElem.appendChild( keywordText );
1956  keywordsElem.appendChild( keywordElem );
1957  parent.appendChild( keywordsElem );
1958  QStringList keywords = QgsServerProjectUtils::owsServiceKeywords( *project );
1959  for ( const QString &keyword : qgis::as_const( keywords ) )
1960  {
1961  if ( !keyword.isEmpty() )
1962  {
1963  keywordElem = doc.createElement( QStringLiteral( "Keyword" ) );
1964  keywordText = doc.createTextNode( keyword );
1965  keywordElem.appendChild( keywordText );
1966  if ( sia2045 )
1967  {
1968  keywordElem.setAttribute( QStringLiteral( "vocabulary" ), QStringLiteral( "SIA_Geo405" ) );
1969  }
1970  keywordsElem.appendChild( keywordElem );
1971  }
1972  }
1973  parent.appendChild( keywordsElem );
1974  }
1975  }
1976 
1977  bool hasQueryableChildren( const QgsLayerTreeNode *childNode, const QStringList &wmsRestrictedLayers )
1978  {
1979  if ( childNode->nodeType() == QgsLayerTreeNode::NodeGroup )
1980  {
1981  for ( int j = 0; j < childNode->children().size(); ++j )
1982  {
1983  if ( hasQueryableChildren( childNode->children().at( j ), wmsRestrictedLayers ) )
1984  return true;
1985  }
1986  return false;
1987  }
1988  else if ( childNode->nodeType() == QgsLayerTreeNode::NodeLayer )
1989  {
1990  const auto treeLayer { static_cast<const QgsLayerTreeLayer *>( childNode ) };
1991  const auto l { treeLayer->layer() };
1992  return ! wmsRestrictedLayers.contains( l->name() ) && l->flags().testFlag( QgsMapLayer::Identifiable );
1993  }
1994  return false;
1995  }
1996 
1997 
1998 } // namespace QgsWms
1999 
2000 
2001 
2002 
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:324
QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:387
SERVER_EXPORT QString wmsInspireMetadataUrl(const QgsProject &project)
Returns the Inspire metadata URL.
Class for parsing and evaluation of expressions (formerly called "search strings").
Layer tree group node serves as a container for layers and further groups.
bool fillCacheKey(QStringList &cacheKey) const
Fill the capabilities caching key.
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...
QString dataUrlFormat() const
Returns the DataUrl format of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:349
SERVER_EXPORT QStringList wmsOutputCrsList(const QgsProject &project)
Returns the WMS output CRS list.
A rectangle specified with double values.
Definition: qgsrectangle.h:41
Base class for all map layer types.
Definition: qgsmaplayer.h:79
QStringList styles() const
Returns list of all defined style names.
SERVER_EXPORT int wmsMaxWidth(const QgsProject &project)
Returns the maximum width for WMS images defined in a QGIS project.
void layoutObjects(QList< T *> &objectList) const
Returns a list of layout objects (items and multiframes) of a specific type.
Definition: qgslayout.h:140
QgsMapLayerType type() const
Returns the type of the layer.
QString name
Definition: qgsfield.h:58
int precision
Definition: qgsfield.h:55
Setting to define QGIS Server WMS Dimension.
virtual QgsDataProvider * dataProvider()
Returns the layer&#39;s data provider, it may be nullptr.
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:135
SERVER_EXPORT QString owsServiceContactPosition(const QgsProject &project)
Returns the owsService contact position defined in project.
SERVER_EXPORT QString owsServiceContactPerson(const QgsProject &project)
Returns the owsService contact person defined in project.
SERVER_EXPORT QStringList owsServiceKeywords(const QgsProject &project)
Returns the owsService keywords defined in project.
QSet< QString > excludeAttributesWms() const
A set of attributes that are not advertised in WMS requests with QGIS server.
A layout item subclass for text labels.
QString shortName() const
Returns the short name of the layer used by QGIS Server to identify the layer.
QString alias
Definition: qgsfield.h:59
QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
QString dataUrl() const
Returns the DataUrl of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:331
SERVER_EXPORT QString owsServiceAbstract(const QgsProject &project)
Returns the owsService abstract defined in project.
QString abstract() const
Returns the abstract of the layer used by QGIS Server in GetCapabilities request. ...
Definition: qgsmaplayer.h:295
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:280
QgsLayoutSize sizeWithUnits() const
Returns the item&#39;s current size, including units.
bool hasQueryableChildren(const QgsLayerTreeNode *childNode, const QStringList &wmsRestrictedLayers)
Modify current selection to include only select features which match.
const QList< QgsVectorLayerServerProperties::WmsDimensionInfo > wmsDimensions() const
Returns the QGIS Server WMS Dimension list.
QString comment
Definition: qgsfield.h:57
QDomElement getServiceElement(QDomDocument &doc, const QgsProject *project, const QString &version, const QgsServerRequest &request)
Create Service element for get capabilities document.
Container of fields for a vector layer.
Definition: qgsfields.h:42
QgsMapLayer::LayerFlags flags() const
Returns the flags for this layer.
SERVER_EXPORT bool wmsInfoFormatSia2045(const QgsProject &project)
Returns if the info format is SIA20145.
bool isVisible() const
Returns whether a node is really visible (ie checked and all its ancestors checked as well) ...
Abstract base class for spatial data provider implementations.
QList< QgsPrintLayout *> printLayouts() const
Returns a list of all print layouts contained in the manager.
int frameCount() const
Returns the number of frames associated with this multiframe.
const QgsCoordinateReferenceSystem & crs
SERVER_EXPORT QString wmsInspireMetadataDate(const QgsProject &project)
Returns the Inspire metadata date.
virtual void write(const QString &data)
Write string This is a convenient method that will write directly to the underlying I/O device...
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
bool isMutuallyExclusive() const
Returns whether the group is mutually exclusive (only one child can be checked at a time) ...
virtual QString name() const =0
Returns a provider name.
virtual QgsRectangle extent() const
Returns the extent of the layer.
void insertCapabilitiesDocument(const QString &configFilePath, const QString &key, const QDomDocument *doc)
Inserts new capabilities document (creates a copy of the document, does not take ownership) ...
QString what() const
Definition: qgsexception.h:48
virtual QString configFilePath()=0
Returns the configuration file path.
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
int length
Definition: qgsfield.h:54
QgsMapLayerStyleManager * styleManager() const
Gets access to the layer&#39;s style manager.
QDomElement getLayersAndStylesCapabilitiesElement(QDomDocument &doc, QgsServerInterface *serverIface, const QgsProject *project, const QString &version, const QgsServerRequest &request, bool projectSettings)
Create element for get capabilities document.
void layoutItems(QList< T *> &itemList) const
Returns a list of layout items of a specific type.
Definition: qgslayout.h:121
QString metadataUrlFormat() const
Returns the metadata format of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:443
void writeGetCapabilities(QgsServerInterface *serverIface, const QgsProject *project, const QString &version, const QgsServerRequest &request, QgsServerResponse &response, bool projectSettings)
Output GetCapabilities response.
Layout graphical items for displaying a map.
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
QgsLayoutItemPage * page(int pageNumber)
Returns a specific page (by pageNumber) from the collection.
virtual QgsCapabilitiesCache * capabilitiesCache()=0
Gets pointer to the capabiblities cache.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:32
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
void grow(double delta)
Grows the rectangle in place by the specified amount.
Definition: qgsrectangle.h:275
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:426
QString typeName() const
Gets the field type.
Definition: qgsfield.cpp:105
QgsLayoutPageCollection * pageCollection()
Returns a pointer to the layout&#39;s page collection, which stores and manages page items in the layout...
Definition: qgslayout.cpp:458
virtual QList< double > nativeResolutions() const
Returns a list of native resolutions if available, i.e.
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QDomElement getComposerTemplatesElement(QDomDocument &doc, const QgsProject *project)
Create ComposerTemplates element for get capabilities document.
QgsServerRequest::Parameters parameters() const
Returns a map of query parameters with keys converted to uppercase.
SERVER_EXPORT QStringList wmsRestrictedComposers(const QgsProject &project)
Returns the restricted composer list.
QDomDocument getCapabilities(QgsServerInterface *serverIface, const QgsProject *project, const QString &version, const QgsServerRequest &request, bool projectSettings)
Creates the WMS GetCapabilities XML document.
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:140
QString keywordList() const
Returns the keyword list of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:311
QgsCoordinateReferenceSystem crs
Definition: qgsproject.h:95
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).
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
Class used to render QgsLayout as an atlas, by iterating over the features from an associated vector ...
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer. Properties are stored in a map and saved in project file...
QgsAttributeList primaryKeyAttributes() const
Returns the list of attributes which make up the layer&#39;s primary keys.
SERVER_EXPORT bool wmsInspireActivate(const QgsProject &project)
Returns if Inspire is activated.
SERVER_EXPORT QString wmsInspireTemporalReference(const QgsProject &project)
Returns the Inspire temporal reference.
A helper class that centralizes caches accesses given by all the server cache filter plugins...
An expression node which takes it value from a feature&#39;s field.
QString legendUrl() const
Returns the URL for the layer&#39;s legend.
Definition: qgsmaplayer.h:955
This class is a base class for nodes in a layer tree.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc.
Definition: qgsproject.h:89
const QString GEO_EPSG_CRS_AUTHID
Geographic coord sys from EPSG authority.
Definition: qgis.cpp:70
QString id() const
Returns the item&#39;s ID name.
SERVER_EXPORT QString wmsInspireLanguage(const QgsProject &project)
Returns the Inspire language.
SERVER_EXPORT QString owsServiceFees(const QgsProject &project)
Returns the owsService fees defined in project.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
const double OGC_PX_M
double minimumScale() const
Returns the minimum map scale (i.e.
double maximumScale() const
Returns the maximum map scale (i.e.
QgsLayoutAtlas * atlas()
Returns the print layout&#39;s atlas.
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.
QString displayExpression
QString legendUrlFormat() const
Returns the format for a URL based layer legend.
Definition: qgsmaplayer.h:965
SERVER_EXPORT QString owsServiceAccessConstraints(const QgsProject &project)
Returns the owsService access constraints defined in project.
NodeType nodeType() const
Find out about type of the node. It is usually shorter to use convenience functions from QgsLayerTree...
const QgsLayoutManager * layoutManager() const
Returns the project&#39;s layout manager, which manages compositions within the project.
SERVER_EXPORT QString owsServiceOnlineResource(const QgsProject &project)
Returns the owsService online resource defined in project.
QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:279
Median cut implementation.
SERVER_EXPORT QString owsServiceContactOrganization(const QgsProject &project)
Returns the owsService contact organization defined in project.
int pageCount() const
Returns the number of pages in the collection.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
QgsLayoutFrame * frame(int index) const
Returns the child frame at a specified index from the multiframe.
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project&#39;s layer tree.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:96
QgsMapLayer * layer() const
Returns the map layer associated with this node.
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle...
Definition: qgsrectangle.h:359
A cache for capabilities xml documents (by configuration file path)
QgsServerRequest Class defining request interface passed to services QgsService::executeRequest() met...
Leaf node pointing to a layer.
bool layerReadPermission(const QgsMapLayer *layer) const
Returns the layer read right.
QgsServerInterface Class defining interfaces exposed by QGIS Server and made available to plugins...
QList< QgsMapLayer * > layerOrder() const
The order in which layers will be rendered on the canvas.
double length() const
Returns the length of the measurement.
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
QgsEditorWidgetSetup editorWidgetSetup(int index) const
The editor widget setup defines which QgsFieldFormatter and editor widget will be used for the field ...
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:207
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:145
QDomElement getInspireCapabilitiesElement(QDomDocument &doc, const QgsProject *project)
Create InspireCapabilities element for get capabilities document.
SERVER_EXPORT QgsRectangle wmsExtent(const QgsProject &project)
Returns the WMS Extent restriction.
QDomElement getCapabilityElement(QDomDocument &doc, const QgsProject *project, const QString &version, const QgsServerRequest &request, bool projectSettings)
Create Capability element for get capabilities document.
bool hasAxisInverted() const
Returns whether axis is inverted (e.g., for WMS 1.3) for the CRS.
SERVER_EXPORT QStringList wfsLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published in WFS.
SERVER_EXPORT QString wmsInspireMetadataUrlType(const QgsProject &project)
Returns the Inspire metadata URL type.
This class represents a coordinate reference system (CRS).
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:436
bool setCachedDocument(const QDomDocument *doc, const QgsProject *project, const QgsServerRequest &request, QgsAccessControl *accessControl) const
Updates or inserts the document in cache like capabilities.
QgsLayoutMeasurement convertFromLayoutUnits(double length, QgsUnitTypes::LayoutUnit unit) const
Converts a length measurement from the layout&#39;s native units to a specified target unit...
Definition: qgslayout.cpp:343
Class for doing transforms between two map coordinate systems.
static QString displayString(Type type)
Returns a display string type for a WKB type, e.g., the geometry name used in WKT geometry representa...
virtual QgsServerCacheManager * cacheManager() const =0
Gets the registered server cache filters.
QUrl serviceUrl(const QgsServerRequest &request, const QgsProject *project)
Returns WMS service URL.
Definition: qgswmsutils.cpp:32
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
bool enabled() const
Returns whether the atlas generation is enabled.
A helper class that centralizes restrictions given by all the access control filter plugins...
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QgsVectorLayer * coverageLayer() const
Returns the coverage layer used for the atlas features.
QString metadataUrl() const
Returns the metadata URL of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:407
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
QgsVectorLayerServerProperties * serverProperties()
Returns QGIS Server Properties of the vector layer.
QString name
Definition: qgsmaplayer.h:83
const QDomDocument * searchCapabilitiesDocument(const QString &configFilePath, const QString &key)
Returns cached capabilities document (or 0 if document for configuration file not in cache) ...
QgsServerResponse Class defining response interface passed to services QgsService::executeRequest() m...
QString name() const override
Returns the group&#39;s name.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
virtual QgsAccessControl * accessControls() const =0
Gets the registered access control filters.
QString title() const
Returns the project&#39;s title.
Definition: qgsproject.cpp:463
SERVER_EXPORT QString owsServiceContactMail(const QgsProject &project)
Returns the owsService contact mail defined in project.
QList< int > QgsAttributeList
Definition: qgsfield.h:27
Print layout, a QgsLayout subclass for static or atlas-based layouts.
QgsRectangle bounds() const
Returns the approximate bounds for the region the CRS is usable within.
SERVER_EXPORT QStringList wmsRestrictedLayers(const QgsProject &project)
Returns the restricted layer name list.
Container of other groups and layers.
This class provides a method of storing sizes, consisting of a width and height, for use in QGIS layo...
Definition: qgslayoutsize.h:40
SERVER_EXPORT QString wmsRootName(const QgsProject &project)
Returns the WMS root layer name defined in a QGIS project.
Represents a vector layer which manages a vector based data sets.
A layout multiframe subclass for HTML content.
QString attributeDisplayName(int index) const
Convenience function that returns the attribute alias if defined or the field name else...
If the layer is identifiable using the identify map tool and as a WMS layer.
Definition: qgsmaplayer.h:137
QVariant::Type type
Definition: qgsfield.h:56
QString authid() const
Returns the authority identifier for the CRS.
SERVER_EXPORT QString owsServiceTitle(const QgsProject &project)
Returns the owsService title defined in project.
QDomElement getWFSLayersElement(QDomDocument &doc, const QgsProject *project)
Create WFSLayers element for get capabilities document.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, TransformDirection direction=ForwardTransform, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:130
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:86
QString attribution() const
Returns the attribution of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:369
QString metadataUrlType() const
Returns the metadata type of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:425
void invert()
Swap x/y coordinates in the rectangle.
Definition: qgsrectangle.h:532
QMap< QString, QString > Parameters
Layer tree node points to a map layer.
SERVER_EXPORT QString owsServiceContactPhone(const QgsProject &project)
Returns the owsService contact phone defined in project.
SERVER_EXPORT int wmsMaxHeight(const QgsProject &project)
Returns the maximum height for WMS images defined in a QGIS project.
Base class for raster data providers.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
SERVER_EXPORT bool wmsUseLayerIds(const QgsProject &project)
Returns if layer ids are used as name in WMS.