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