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