QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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 );
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 )
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 ( projectSettings ) //remove composer templates from GetCapabilities in the long term
538  {
539  //wms:GetPrint
540  elem = doc.createElement( QStringLiteral( "GetPrint" ) /*wms:GetPrint*/ );
541  appendFormat( elem, QStringLiteral( "svg" ) );
542  appendFormat( elem, QStringLiteral( "png" ) );
543  appendFormat( elem, QStringLiteral( "pdf" ) );
544  elem.appendChild( dcpTypeElem.cloneNode().toElement() ); //this is the same as for 'GetCapabilities'
545  requestElem.appendChild( elem );
546  }
547 
548  //Exception element is mandatory
549  elem = doc.createElement( QStringLiteral( "Exception" ) );
550  appendFormat( elem, ( version == QLatin1String( "1.1.1" ) ? "application/vnd.ogc.se_xml" : "XML" ) );
551  capabilityElem.appendChild( elem );
552 
553  //UserDefinedSymbolization element
554  if ( version == QLatin1String( "1.3.0" ) )
555  {
556  elem = doc.createElement( QStringLiteral( "sld:UserDefinedSymbolization" ) );
557  elem.setAttribute( QStringLiteral( "SupportSLD" ), QStringLiteral( "1" ) );
558  elem.setAttribute( QStringLiteral( "UserLayer" ), QStringLiteral( "0" ) );
559  elem.setAttribute( QStringLiteral( "UserStyle" ), QStringLiteral( "1" ) );
560  elem.setAttribute( QStringLiteral( "RemoteWFS" ), QStringLiteral( "0" ) );
561  elem.setAttribute( QStringLiteral( "InlineFeature" ), QStringLiteral( "0" ) );
562  elem.setAttribute( QStringLiteral( "RemoteWCS" ), QStringLiteral( "0" ) );
563  capabilityElem.appendChild( elem );
564 
566  {
567  capabilityElem.appendChild( getInspireCapabilitiesElement( doc, project ) );
568  }
569  }
570 
571  return capabilityElem;
572  }
573 
574  QDomElement getInspireCapabilitiesElement( QDomDocument &doc, const QgsProject *project )
575  {
576  QDomElement inspireCapabilitiesElem;
577 
579  return inspireCapabilitiesElem;
580 
581  inspireCapabilitiesElem = doc.createElement( QStringLiteral( "inspire_vs:ExtendedCapabilities" ) );
582 
583  QString inspireMetadataUrl = QgsServerProjectUtils::wmsInspireMetadataUrl( *project );
584  // inspire scenario 1
585  if ( !inspireMetadataUrl.isEmpty() )
586  {
587  QDomElement inspireCommonMetadataUrlElem = doc.createElement( QStringLiteral( "inspire_common:MetadataUrl" ) );
588  inspireCommonMetadataUrlElem.setAttribute( QStringLiteral( "xsi:type" ), QStringLiteral( "inspire_common:resourceLocatorType" ) );
589 
590  QDomElement inspireCommonMetadataUrlUrlElem = doc.createElement( QStringLiteral( "inspire_common:URL" ) );
591  inspireCommonMetadataUrlUrlElem.appendChild( doc.createTextNode( inspireMetadataUrl ) );
592  inspireCommonMetadataUrlElem.appendChild( inspireCommonMetadataUrlUrlElem );
593 
594  QString inspireMetadataUrlType = QgsServerProjectUtils::wmsInspireMetadataUrlType( *project );
595  if ( !inspireMetadataUrlType.isNull() )
596  {
597  QDomElement inspireCommonMetadataUrlMediaTypeElem = doc.createElement( QStringLiteral( "inspire_common:MediaType" ) );
598  inspireCommonMetadataUrlMediaTypeElem.appendChild( doc.createTextNode( inspireMetadataUrlType ) );
599  inspireCommonMetadataUrlElem.appendChild( inspireCommonMetadataUrlMediaTypeElem );
600  }
601 
602  inspireCapabilitiesElem.appendChild( inspireCommonMetadataUrlElem );
603  }
604  else
605  {
606  QDomElement inspireCommonResourceTypeElem = doc.createElement( QStringLiteral( "inspire_common:ResourceType" ) );
607  inspireCommonResourceTypeElem.appendChild( doc.createTextNode( QStringLiteral( "service" ) ) );
608  inspireCapabilitiesElem.appendChild( inspireCommonResourceTypeElem );
609 
610  QDomElement inspireCommonSpatialDataServiceTypeElem = doc.createElement( QStringLiteral( "inspire_common:SpatialDataServiceType" ) );
611  inspireCommonSpatialDataServiceTypeElem.appendChild( doc.createTextNode( QStringLiteral( "view" ) ) );
612  inspireCapabilitiesElem.appendChild( inspireCommonSpatialDataServiceTypeElem );
613 
614  QString inspireTemporalReference = QgsServerProjectUtils::wmsInspireTemporalReference( *project );
615  if ( !inspireTemporalReference.isNull() )
616  {
617  QDomElement inspireCommonTemporalReferenceElem = doc.createElement( QStringLiteral( "inspire_common:TemporalReference" ) );
618  QDomElement inspireCommonDateOfLastRevisionElem = doc.createElement( QStringLiteral( "inspire_common:DateOfLastRevision" ) );
619  inspireCommonDateOfLastRevisionElem.appendChild( doc.createTextNode( inspireTemporalReference ) );
620  inspireCommonTemporalReferenceElem.appendChild( inspireCommonDateOfLastRevisionElem );
621  inspireCapabilitiesElem.appendChild( inspireCommonTemporalReferenceElem );
622  }
623 
624  QDomElement inspireCommonMetadataPointOfContactElem = doc.createElement( QStringLiteral( "inspire_common:MetadataPointOfContact" ) );
625 
626  QString contactOrganization = QgsServerProjectUtils::owsServiceContactOrganization( *project );
627  QDomElement inspireCommonOrganisationNameElem = doc.createElement( QStringLiteral( "inspire_common:OrganisationName" ) );
628  if ( !contactOrganization.isNull() )
629  {
630  inspireCommonOrganisationNameElem.appendChild( doc.createTextNode( contactOrganization ) );
631  }
632  inspireCommonMetadataPointOfContactElem.appendChild( inspireCommonOrganisationNameElem );
633 
634  QString contactMail = QgsServerProjectUtils::owsServiceContactMail( *project );
635  QDomElement inspireCommonEmailAddressElem = doc.createElement( QStringLiteral( "inspire_common:EmailAddress" ) );
636  if ( !contactMail.isNull() )
637  {
638  inspireCommonEmailAddressElem.appendChild( doc.createTextNode( contactMail ) );
639  }
640  inspireCommonMetadataPointOfContactElem.appendChild( inspireCommonEmailAddressElem );
641 
642  inspireCapabilitiesElem.appendChild( inspireCommonMetadataPointOfContactElem );
643 
644  QString inspireMetadataDate = QgsServerProjectUtils::wmsInspireMetadataDate( *project );
645  if ( !inspireMetadataDate.isNull() )
646  {
647  QDomElement inspireCommonMetadataDateElem = doc.createElement( QStringLiteral( "inspire_common:MetadataDate" ) );
648  inspireCommonMetadataDateElem.appendChild( doc.createTextNode( inspireMetadataDate ) );
649  inspireCapabilitiesElem.appendChild( inspireCommonMetadataDateElem );
650  }
651  }
652 
653  // Supported languages
654  QDomElement inspireCommonSupportedLanguagesElem = doc.createElement( QStringLiteral( "inspire_common:SupportedLanguages" ) );
655  inspireCommonSupportedLanguagesElem.setAttribute( QStringLiteral( "xsi:type" ), QStringLiteral( "inspire_common:supportedLanguagesType" ) );
656 
657  QDomElement inspireCommonLanguageElem = doc.createElement( QStringLiteral( "inspire_common:Language" ) );
658  inspireCommonLanguageElem.appendChild( doc.createTextNode( QgsServerProjectUtils::wmsInspireLanguage( *project ) ) );
659 
660  QDomElement inspireCommonDefaultLanguageElem = doc.createElement( QStringLiteral( "inspire_common:DefaultLanguage" ) );
661  inspireCommonDefaultLanguageElem.appendChild( inspireCommonLanguageElem );
662  inspireCommonSupportedLanguagesElem.appendChild( inspireCommonDefaultLanguageElem );
663 
664 #if 0
665  /* Supported language has to be different from default one */
666  QDomElement inspireCommonSupportedLanguageElem = doc.createElement( "inspire_common:SupportedLanguage" );
667  inspireCommonSupportedLanguageElem.appendChild( inspireCommonLanguageElem.cloneNode().toElement() );
668  inspireCommonSupportedLanguagesElem.appendChild( inspireCommonSupportedLanguageElem );
669 #endif
670 
671  inspireCapabilitiesElem.appendChild( inspireCommonSupportedLanguagesElem );
672 
673  QDomElement inspireCommonResponseLanguageElem = doc.createElement( QStringLiteral( "inspire_common:ResponseLanguage" ) );
674  inspireCommonResponseLanguageElem.appendChild( inspireCommonLanguageElem.cloneNode().toElement() );
675  inspireCapabilitiesElem.appendChild( inspireCommonResponseLanguageElem );
676 
677  return inspireCapabilitiesElem;
678  }
679 
680  QDomElement getComposerTemplatesElement( QDomDocument &doc, const QgsProject *project )
681  {
682  QList< QgsPrintLayout * > projectComposers = project->layoutManager()->printLayouts();
683  if ( projectComposers.size() == 0 )
684  return QDomElement();
685 
686  QStringList restrictedComposers = QgsServerProjectUtils::wmsRestrictedComposers( *project );
687 
688  QDomElement composerTemplatesElem = doc.createElement( QStringLiteral( "ComposerTemplates" ) );
689  QList<QgsPrintLayout *>::const_iterator cIt = projectComposers.constBegin();
690  for ( ; cIt != projectComposers.constEnd(); ++cIt )
691  {
692  QgsPrintLayout *layout = *cIt;
693  if ( restrictedComposers.contains( layout->name() ) )
694  continue;
695 
696  // Check that we have at least one page
697  if ( layout->pageCollection()->pageCount() < 1 )
698  continue;
699 
700  // Get width and height from first page of the collection
701  QgsLayoutSize layoutSize( layout->pageCollection()->page( 0 )->sizeWithUnits() );
702  QgsLayoutMeasurement width( layout->convertFromLayoutUnits( layoutSize.width(), QgsUnitTypes::LayoutUnit::LayoutMillimeters ) );
703  QgsLayoutMeasurement height( layout->convertFromLayoutUnits( layoutSize.height(), QgsUnitTypes::LayoutUnit::LayoutMillimeters ) );
704 
705  QDomElement composerTemplateElem = doc.createElement( QStringLiteral( "ComposerTemplate" ) );
706  composerTemplateElem.setAttribute( QStringLiteral( "name" ), layout->name() );
707 
708  //get paper width and height in mm from composition
709  composerTemplateElem.setAttribute( QStringLiteral( "width" ), width.length() );
710  composerTemplateElem.setAttribute( QStringLiteral( "height" ), height.length() );
711 
712  //atlas enabled and atlas covering layer
713  QgsLayoutAtlas *atlas = layout->atlas();
714  if ( atlas && atlas->enabled() )
715  {
716  composerTemplateElem.setAttribute( QStringLiteral( "atlasEnabled" ), QStringLiteral( "1" ) );
717  QgsVectorLayer *cLayer = atlas->coverageLayer();
718  if ( cLayer )
719  {
720  QString layerName = cLayer->shortName();
721  if ( QgsServerProjectUtils::wmsUseLayerIds( *project ) )
722  {
723  layerName = cLayer->id();
724  }
725  else if ( layerName.isEmpty() )
726  {
727  layerName = cLayer->name();
728  }
729  composerTemplateElem.setAttribute( QStringLiteral( "atlasCoverageLayer" ), layerName );
730  }
731  }
732 
733  //add available composer maps and their size in mm
734  QList<QgsLayoutItemMap *> layoutMapList;
735  layout->layoutItems<QgsLayoutItemMap>( layoutMapList );
736  QList<QgsLayoutItemMap *>::const_iterator cmIt = layoutMapList.constBegin();
737  // Add map id
738  int mapId = 0;
739  for ( ; cmIt != layoutMapList.constEnd(); ++cmIt )
740  {
741  const QgsLayoutItemMap *composerMap = *cmIt;
742 
743  QDomElement composerMapElem = doc.createElement( QStringLiteral( "ComposerMap" ) );
744  composerMapElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "map%1" ).arg( mapId ) );
745  mapId++;
746  composerMapElem.setAttribute( QStringLiteral( "width" ), composerMap->rect().width() );
747  composerMapElem.setAttribute( QStringLiteral( "height" ), composerMap->rect().height() );
748  composerTemplateElem.appendChild( composerMapElem );
749  }
750 
751  //add available composer labels
752  QList<QgsLayoutItemLabel *> composerLabelList;
753  layout->layoutItems<QgsLayoutItemLabel>( composerLabelList );
754  QList<QgsLayoutItemLabel *>::const_iterator clIt = composerLabelList.constBegin();
755  for ( ; clIt != composerLabelList.constEnd(); ++clIt )
756  {
757  QgsLayoutItemLabel *composerLabel = *clIt;
758  QString id = composerLabel->id();
759  if ( id.isEmpty() )
760  continue;
761 
762  QDomElement composerLabelElem = doc.createElement( QStringLiteral( "ComposerLabel" ) );
763  composerLabelElem.setAttribute( QStringLiteral( "name" ), id );
764  composerTemplateElem.appendChild( composerLabelElem );
765  }
766 
767  //add available composer HTML
768  QList<QgsLayoutItemHtml *> composerHtmlList;
769  layout->layoutObjects<QgsLayoutItemHtml>( composerHtmlList );
770  QList<QgsLayoutItemHtml *>::const_iterator chIt = composerHtmlList.constBegin();
771  for ( ; chIt != composerHtmlList.constEnd(); ++chIt )
772  {
773  QgsLayoutItemHtml *composerHtml = *chIt;
774  if ( composerHtml->frameCount() == 0 )
775  continue;
776 
777  QString id = composerHtml->frame( 0 )->id();
778  if ( id.isEmpty() )
779  continue;
780 
781  QDomElement composerHtmlElem = doc.createElement( QStringLiteral( "ComposerHtml" ) );
782  composerHtmlElem.setAttribute( QStringLiteral( "name" ), id );
783  composerTemplateElem.appendChild( composerHtmlElem );
784  }
785 
786  composerTemplatesElem.appendChild( composerTemplateElem );
787  }
788 
789  if ( composerTemplatesElem.childNodes().size() == 0 )
790  return QDomElement();
791 
792  return composerTemplatesElem;
793  }
794 
795  QDomElement getWFSLayersElement( QDomDocument &doc, const QgsProject *project )
796  {
797  QStringList wfsLayerIds = QgsServerProjectUtils::wfsLayerIds( *project );
798  if ( wfsLayerIds.size() == 0 )
799  return QDomElement();
800 
801  QDomElement wfsLayersElem = doc.createElement( QStringLiteral( "WFSLayers" ) );
802  for ( int i = 0; i < wfsLayerIds.size(); ++i )
803  {
804  QgsMapLayer *layer = project->mapLayer( wfsLayerIds.at( i ) );
805  if ( ! layer || layer->type() != QgsMapLayerType::VectorLayer )
806  {
807  continue;
808  }
809 
810  QDomElement wfsLayerElem = doc.createElement( QStringLiteral( "WFSLayer" ) );
811  if ( QgsServerProjectUtils::wmsUseLayerIds( *project ) )
812  {
813  wfsLayerElem.setAttribute( QStringLiteral( "name" ), layer->id() );
814  }
815  else
816  {
817  wfsLayerElem.setAttribute( QStringLiteral( "name" ), layer->name() );
818  }
819  wfsLayersElem.appendChild( wfsLayerElem );
820  }
821 
822  return wfsLayersElem;
823  }
824 
825  QDomElement getLayersAndStylesCapabilitiesElement( QDomDocument &doc, QgsServerInterface *serverIface,
826  const QgsProject *project, const QString &version,
827  const QgsServerRequest &request, bool projectSettings )
828  {
829  const QgsLayerTree *projectLayerTreeRoot = project->layerTreeRoot();
830 
831  QDomElement layerParentElem = doc.createElement( QStringLiteral( "Layer" ) );
832 
833  // Root Layer name
834  QString rootLayerName = QgsServerProjectUtils::wmsRootName( *project );
835  if ( rootLayerName.isEmpty() && !project->title().isEmpty() )
836  {
837  rootLayerName = project->title();
838  }
839 
840  if ( !rootLayerName.isEmpty() )
841  {
842  QDomElement layerParentNameElem = doc.createElement( QStringLiteral( "Name" ) );
843  QDomText layerParentNameText = doc.createTextNode( rootLayerName );
844  layerParentNameElem.appendChild( layerParentNameText );
845  layerParentElem.appendChild( layerParentNameElem );
846  }
847 
848  if ( !project->title().isEmpty() )
849  {
850  // Root Layer title
851  QDomElement layerParentTitleElem = doc.createElement( QStringLiteral( "Title" ) );
852  QDomText layerParentTitleText = doc.createTextNode( project->title() );
853  layerParentTitleElem.appendChild( layerParentTitleText );
854  layerParentElem.appendChild( layerParentTitleElem );
855 
856  // Root Layer abstract
857  QDomElement layerParentAbstElem = doc.createElement( QStringLiteral( "Abstract" ) );
858  QDomText layerParentAbstText = doc.createTextNode( project->title() );
859  layerParentAbstElem.appendChild( layerParentAbstText );
860  layerParentElem.appendChild( layerParentAbstElem );
861  }
862 
863  // Keyword list
864  addKeywordListElement( project, doc, layerParentElem );
865 
866  // Root Layer tree name
867  if ( projectSettings )
868  {
869  QDomElement treeNameElem = doc.createElement( QStringLiteral( "TreeName" ) );
870  QDomText treeNameText = doc.createTextNode( project->title() );
871  treeNameElem.appendChild( treeNameText );
872  layerParentElem.appendChild( treeNameElem );
873  }
874 
875  if ( hasQueryableChildren( projectLayerTreeRoot, QgsServerProjectUtils::wmsRestrictedLayers( *project ) ) )
876  {
877  layerParentElem.setAttribute( QStringLiteral( "queryable" ), QStringLiteral( "1" ) );
878  }
879  else
880  {
881  layerParentElem.setAttribute( QStringLiteral( "queryable" ), QStringLiteral( "0" ) );
882  }
883 
884  appendLayersFromTreeGroup( doc, layerParentElem, serverIface, project, version, request, projectLayerTreeRoot, projectSettings );
885 
886  combineExtentAndCrsOfGroupChildren( doc, layerParentElem, project, true );
887 
888  return layerParentElem;
889  }
890 
891  namespace
892  {
893 
894  void appendLayersFromTreeGroup( QDomDocument &doc,
895  QDomElement &parentLayer,
896  QgsServerInterface *serverIface,
897  const QgsProject *project,
898  const QString &version,
899  const QgsServerRequest &request,
900  const QgsLayerTreeGroup *layerTreeGroup,
901  bool projectSettings )
902  {
903  bool useLayerIds = QgsServerProjectUtils::wmsUseLayerIds( *project );
904  bool siaFormat = QgsServerProjectUtils::wmsInfoFormatSia2045( *project );
905  const QStringList restrictedLayers = QgsServerProjectUtils::wmsRestrictedLayers( *project );
906 
907  QList< QgsLayerTreeNode * > layerTreeGroupChildren = layerTreeGroup->children();
908  for ( int i = 0; i < layerTreeGroupChildren.size(); ++i )
909  {
910  QgsLayerTreeNode *treeNode = layerTreeGroupChildren.at( i );
911  QDomElement layerElem = doc.createElement( QStringLiteral( "Layer" ) );
912 
913  if ( projectSettings )
914  {
915  layerElem.setAttribute( QStringLiteral( "visible" ), treeNode->isVisible() );
916  layerElem.setAttribute( QStringLiteral( "expanded" ), treeNode->isExpanded() );
917  }
918 
919  if ( treeNode->nodeType() == QgsLayerTreeNode::NodeGroup )
920  {
921  QgsLayerTreeGroup *treeGroupChild = static_cast<QgsLayerTreeGroup *>( treeNode );
922 
923  QString name = treeGroupChild->name();
924  if ( restrictedLayers.contains( name ) ) //unpublished group
925  {
926  continue;
927  }
928 
929  if ( projectSettings )
930  {
931  layerElem.setAttribute( QStringLiteral( "mutuallyExclusive" ), treeGroupChild->isMutuallyExclusive() );
932  }
933 
934  QString shortName = treeGroupChild->customProperty( QStringLiteral( "wmsShortName" ) ).toString();
935  QString title = treeGroupChild->customProperty( QStringLiteral( "wmsTitle" ) ).toString();
936 
937  QDomElement nameElem = doc.createElement( QStringLiteral( "Name" ) );
938  QDomText nameText;
939  if ( !shortName.isEmpty() )
940  nameText = doc.createTextNode( shortName );
941  else
942  nameText = doc.createTextNode( name );
943  nameElem.appendChild( nameText );
944  layerElem.appendChild( nameElem );
945 
946  QDomElement titleElem = doc.createElement( QStringLiteral( "Title" ) );
947  QDomText titleText;
948  if ( !title.isEmpty() )
949  titleText = doc.createTextNode( title );
950  else
951  titleText = doc.createTextNode( name );
952  titleElem.appendChild( titleText );
953  layerElem.appendChild( titleElem );
954 
955  QString abstract = treeGroupChild->customProperty( QStringLiteral( "wmsAbstract" ) ).toString();
956  if ( !abstract.isEmpty() )
957  {
958  QDomElement abstractElem = doc.createElement( QStringLiteral( "Abstract" ) );
959  QDomText abstractText = doc.createTextNode( abstract );
960  abstractElem.appendChild( abstractText );
961  layerElem.appendChild( abstractElem );
962  }
963 
964  // Layer tree name
965  if ( projectSettings )
966  {
967  QDomElement treeNameElem = doc.createElement( QStringLiteral( "TreeName" ) );
968  QDomText treeNameText = doc.createTextNode( name );
969  treeNameElem.appendChild( treeNameText );
970  layerElem.appendChild( treeNameElem );
971  }
972 
973  // Set queryable if any of the children are
974  if ( hasQueryableChildren( treeNode, restrictedLayers ) )
975  {
976  layerElem.setAttribute( QStringLiteral( "queryable" ), QStringLiteral( "1" ) );
977  }
978  else
979  {
980  layerElem.setAttribute( QStringLiteral( "queryable" ), QStringLiteral( "0" ) );
981  }
982 
983  appendLayersFromTreeGroup( doc, layerElem, serverIface, project, version, request, treeGroupChild, projectSettings );
984 
985  combineExtentAndCrsOfGroupChildren( doc, layerElem, project );
986  }
987  else
988  {
989  QgsLayerTreeLayer *treeLayer = static_cast<QgsLayerTreeLayer *>( treeNode );
990  QgsMapLayer *l = treeLayer->layer();
991  if ( restrictedLayers.contains( l->name() ) ) //unpublished layer
992  {
993  continue;
994  }
995 
996 #ifdef HAVE_SERVER_PYTHON_PLUGINS
997  QgsAccessControl *accessControl = serverIface->accessControls();
998  if ( accessControl && !accessControl->layerReadPermission( l ) )
999  {
1000  continue;
1001  }
1002 #endif
1003  QString wmsName = l->name();
1004  if ( useLayerIds )
1005  {
1006  wmsName = l->id();
1007  }
1008  else if ( !l->shortName().isEmpty() )
1009  {
1010  wmsName = l->shortName();
1011  }
1012 
1013  // queryable layer
1014  if ( !l->flags().testFlag( QgsMapLayer::Identifiable ) )
1015  {
1016  layerElem.setAttribute( QStringLiteral( "queryable" ), QStringLiteral( "0" ) );
1017  }
1018  else
1019  {
1020  layerElem.setAttribute( QStringLiteral( "queryable" ), QStringLiteral( "1" ) );
1021  }
1022 
1023  QDomElement nameElem = doc.createElement( QStringLiteral( "Name" ) );
1024  QDomText nameText = doc.createTextNode( wmsName );
1025  nameElem.appendChild( nameText );
1026  layerElem.appendChild( nameElem );
1027 
1028  QDomElement titleElem = doc.createElement( QStringLiteral( "Title" ) );
1029  QString title = l->title();
1030  if ( title.isEmpty() )
1031  {
1032  title = l->name();
1033  }
1034  QDomText titleText = doc.createTextNode( title );
1035  titleElem.appendChild( titleText );
1036  layerElem.appendChild( titleElem );
1037 
1038  QString abstract = l->abstract();
1039  if ( !abstract.isEmpty() )
1040  {
1041  QDomElement abstractElem = doc.createElement( QStringLiteral( "Abstract" ) );
1042  QDomText abstractText = doc.createTextNode( abstract );
1043  abstractElem.appendChild( abstractText );
1044  layerElem.appendChild( abstractElem );
1045  }
1046 
1047  //keyword list
1048  if ( !l->keywordList().isEmpty() )
1049  {
1050  QStringList keywordStringList = l->keywordList().split( ',' );
1051 
1052  QDomElement keywordListElem = doc.createElement( QStringLiteral( "KeywordList" ) );
1053  for ( int i = 0; i < keywordStringList.size(); ++i )
1054  {
1055  QDomElement keywordElem = doc.createElement( QStringLiteral( "Keyword" ) );
1056  QDomText keywordText = doc.createTextNode( keywordStringList.at( i ).trimmed() );
1057  keywordElem.appendChild( keywordText );
1058  if ( siaFormat )
1059  {
1060  keywordElem.setAttribute( QStringLiteral( "vocabulary" ), QStringLiteral( "SIA_Geo405" ) );
1061  }
1062  keywordListElem.appendChild( keywordElem );
1063  }
1064  layerElem.appendChild( keywordListElem );
1065  }
1066 
1067  //vector layer without geometry
1068  bool geometryLayer = true;
1069  if ( l->type() == QgsMapLayerType::VectorLayer )
1070  {
1071  QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( l );
1072  if ( vLayer )
1073  {
1074  if ( vLayer->wkbType() == QgsWkbTypes::NoGeometry )
1075  {
1076  geometryLayer = false;
1077  }
1078  }
1079  }
1080 
1081  //CRS
1082  if ( geometryLayer )
1083  {
1084  QStringList crsList;
1085  crsList << l->crs().authid();
1086  QStringList outputCrsList = QgsServerProjectUtils::wmsOutputCrsList( *project );
1087  appendCrsElementsToLayer( doc, layerElem, crsList, outputCrsList );
1088 
1089  //Ex_GeographicBoundingBox
1090  QgsRectangle extent = l->extent(); // layer extent by default
1091  if ( l->type() == QgsMapLayerType::VectorLayer )
1092  {
1093  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( l );
1094  if ( vl && vl->featureCount() == 0 )
1095  {
1096  // if there's no feature, use the wms extent defined in the
1097  // project...
1098  extent = QgsServerProjectUtils::wmsExtent( *project );
1099  if ( extent.isNull() )
1100  {
1101  // or the CRS extent otherwise
1102  extent = vl->crs().bounds();
1103  }
1104  // If CRS is different transform it to layer's CRS
1105  else if ( vl->crs() != project->crs() )
1106  {
1107  try
1108  {
1109  QgsCoordinateTransform ct( project->crs(), vl->crs(), project->transformContext() );
1110  extent = ct.transform( extent );
1111  }
1112  catch ( QgsCsException &cse )
1113  {
1114  QgsMessageLog::logMessage( QStringLiteral( "Error transforming extent for layer %1: %2" ).arg( vl->name() ).arg( cse.what() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
1115  continue;
1116  }
1117  }
1118  }
1119  }
1120 
1121  appendLayerBoundingBoxes( doc, layerElem, extent, l->crs(), crsList, outputCrsList, project );
1122  }
1123 
1124  // add details about supported styles of the layer
1125  appendLayerStyles( doc, layerElem, l, project, version, request );
1126 
1127  //min/max scale denominatorScaleBasedVisibility
1128  if ( l->hasScaleBasedVisibility() )
1129  {
1130  if ( version == QLatin1String( "1.1.1" ) )
1131  {
1132  double OGC_PX_M = 0.00028; // OGC reference pixel size in meter, also used by qgis
1133  double SCALE_TO_SCALEHINT = OGC_PX_M * M_SQRT2;
1134 
1135  QDomElement scaleHintElem = doc.createElement( QStringLiteral( "ScaleHint" ) );
1136  scaleHintElem.setAttribute( QStringLiteral( "min" ), QString::number( l->maximumScale() * SCALE_TO_SCALEHINT ) );
1137  scaleHintElem.setAttribute( QStringLiteral( "max" ), QString::number( l->minimumScale() * SCALE_TO_SCALEHINT ) );
1138  layerElem.appendChild( scaleHintElem );
1139  }
1140  else
1141  {
1142  QString minScaleString = QString::number( l->maximumScale() );
1143  QDomElement minScaleElem = doc.createElement( QStringLiteral( "MinScaleDenominator" ) );
1144  QDomText minScaleText = doc.createTextNode( minScaleString );
1145  minScaleElem.appendChild( minScaleText );
1146  layerElem.appendChild( minScaleElem );
1147 
1148  QString maxScaleString = QString::number( l->minimumScale() );
1149  QDomElement maxScaleElem = doc.createElement( QStringLiteral( "MaxScaleDenominator" ) );
1150  QDomText maxScaleText = doc.createTextNode( maxScaleString );
1151  maxScaleElem.appendChild( maxScaleText );
1152  layerElem.appendChild( maxScaleElem );
1153  }
1154  }
1155 
1156  // layer data URL
1157  QString dataUrl = l->dataUrl();
1158  if ( !dataUrl.isEmpty() )
1159  {
1160  QDomElement dataUrlElem = doc.createElement( QStringLiteral( "DataURL" ) );
1161  QDomElement dataUrlFormatElem = doc.createElement( QStringLiteral( "Format" ) );
1162  QString dataUrlFormat = l->dataUrlFormat();
1163  QDomText dataUrlFormatText = doc.createTextNode( dataUrlFormat );
1164  dataUrlFormatElem.appendChild( dataUrlFormatText );
1165  dataUrlElem.appendChild( dataUrlFormatElem );
1166  QDomElement dataORElem = doc.createElement( QStringLiteral( "OnlineResource" ) );
1167  dataORElem.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
1168  dataORElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) );
1169  dataORElem.setAttribute( QStringLiteral( "xlink:href" ), dataUrl );
1170  dataUrlElem.appendChild( dataORElem );
1171  layerElem.appendChild( dataUrlElem );
1172  }
1173 
1174  // layer attribution
1175  QString attribution = l->attribution();
1176  if ( !attribution.isEmpty() )
1177  {
1178  QDomElement attribElem = doc.createElement( QStringLiteral( "Attribution" ) );
1179  QDomElement attribTitleElem = doc.createElement( QStringLiteral( "Title" ) );
1180  QDomText attribText = doc.createTextNode( attribution );
1181  attribTitleElem.appendChild( attribText );
1182  attribElem.appendChild( attribTitleElem );
1183  QString attributionUrl = l->attributionUrl();
1184  if ( !attributionUrl.isEmpty() )
1185  {
1186  QDomElement attribORElem = doc.createElement( QStringLiteral( "OnlineResource" ) );
1187  attribORElem.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
1188  attribORElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) );
1189  attribORElem.setAttribute( QStringLiteral( "xlink:href" ), attributionUrl );
1190  attribElem.appendChild( attribORElem );
1191  }
1192  layerElem.appendChild( attribElem );
1193  }
1194 
1195  // layer metadata URL
1196  QString metadataUrl = l->metadataUrl();
1197  if ( !metadataUrl.isEmpty() )
1198  {
1199  QDomElement metaUrlElem = doc.createElement( QStringLiteral( "MetadataURL" ) );
1200  QString metadataUrlType = l->metadataUrlType();
1201  if ( version == QLatin1String( "1.1.1" ) )
1202  {
1203  metaUrlElem.setAttribute( QStringLiteral( "type" ), metadataUrlType );
1204  }
1205  else if ( metadataUrlType == QLatin1String( "FGDC" ) )
1206  {
1207  metaUrlElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "FGDC:1998" ) );
1208  }
1209  else if ( metadataUrlType == QLatin1String( "TC211" ) )
1210  {
1211  metaUrlElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "ISO19115:2003" ) );
1212  }
1213  else
1214  {
1215  metaUrlElem.setAttribute( QStringLiteral( "type" ), metadataUrlType );
1216  }
1217  QString metadataUrlFormat = l->metadataUrlFormat();
1218  if ( !metadataUrlFormat.isEmpty() )
1219  {
1220  QDomElement metaUrlFormatElem = doc.createElement( QStringLiteral( "Format" ) );
1221  QDomText metaUrlFormatText = doc.createTextNode( metadataUrlFormat );
1222  metaUrlFormatElem.appendChild( metaUrlFormatText );
1223  metaUrlElem.appendChild( metaUrlFormatElem );
1224  }
1225  QDomElement metaUrlORElem = doc.createElement( QStringLiteral( "OnlineResource" ) );
1226  metaUrlORElem.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
1227  metaUrlORElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) );
1228  metaUrlORElem.setAttribute( QStringLiteral( "xlink:href" ), metadataUrl );
1229  metaUrlElem.appendChild( metaUrlORElem );
1230  layerElem.appendChild( metaUrlElem );
1231  }
1232 
1233  // Add dimensions
1234  if ( l->type() == QgsMapLayerType::VectorLayer )
1235  {
1236  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( l );
1237  const QList<QgsVectorLayerServerProperties::WmsDimensionInfo> wmsDims = vl->serverProperties()->wmsDimensions();
1238  for ( const QgsVectorLayerServerProperties::WmsDimensionInfo &dim : wmsDims )
1239  {
1240  int fieldIndex = vl->fields().indexOf( dim.fieldName );
1241  // Check field index
1242  if ( fieldIndex == -1 )
1243  {
1244  continue;
1245  }
1246  // get unique values
1247  QSet<QVariant> uniqueValues = vl->uniqueValues( fieldIndex );
1248 
1249  // get unique values from endfield name if define
1250  if ( !dim.endFieldName.isEmpty() )
1251  {
1252  int endFieldIndex = vl->fields().indexOf( dim.endFieldName );
1253  // Check end field index
1254  if ( endFieldIndex == -1 )
1255  {
1256  continue;
1257  }
1258  uniqueValues.unite( vl->uniqueValues( endFieldIndex ) );
1259  }
1260  // sort unique values
1261  QList<QVariant> values = qgis::setToList( uniqueValues );
1262  std::sort( values.begin(), values.end() );
1263 
1264  QDomElement dimElem = doc.createElement( QStringLiteral( "Dimension" ) );
1265  dimElem.setAttribute( QStringLiteral( "name" ), dim.name );
1266  if ( !dim.units.isEmpty() )
1267  {
1268  dimElem.setAttribute( QStringLiteral( "units" ), dim.units );
1269  }
1270  if ( !dim.unitSymbol.isEmpty() )
1271  {
1272  dimElem.setAttribute( QStringLiteral( "unitSymbol" ), dim.unitSymbol );
1273  }
1274  if ( dim.defaultDisplayType == QgsVectorLayerServerProperties::WmsDimensionInfo::MinValue )
1275  {
1276  dimElem.setAttribute( QStringLiteral( "default" ), values.first().toString() );
1277  }
1278  else if ( dim.defaultDisplayType == QgsVectorLayerServerProperties::WmsDimensionInfo::MaxValue )
1279  {
1280  dimElem.setAttribute( QStringLiteral( "default" ), values.last().toString() );
1281  }
1282  else if ( dim.defaultDisplayType == QgsVectorLayerServerProperties::WmsDimensionInfo::ReferenceValue )
1283  {
1284  dimElem.setAttribute( QStringLiteral( "default" ), dim.referenceValue.toString() );
1285  }
1286  dimElem.setAttribute( QStringLiteral( "multipleValue" ), '1' );
1287  dimElem.setAttribute( QStringLiteral( "nearestValue" ), '0' );
1288  // values list
1289  QStringList strValues;
1290  for ( const QVariant &v : values )
1291  {
1292  strValues << v.toString();
1293  }
1294  QDomText dimValuesText = doc.createTextNode( strValues.join( QStringLiteral( ", " ) ) );
1295  dimElem.appendChild( dimValuesText );
1296  layerElem.appendChild( dimElem );
1297  }
1298  }
1299 
1300  if ( projectSettings )
1301  {
1302  appendLayerProjectSettings( doc, layerElem, l );
1303  }
1304  }
1305 
1306  parentLayer.appendChild( layerElem );
1307  }
1308  }
1309 
1310  void appendLayerStyles( QDomDocument &doc, QDomElement &layerElem, QgsMapLayer *currentLayer,
1311  const QgsProject *project, const QString &version, const QgsServerRequest &request )
1312  {
1313  // Get service URL
1314  QUrl href = serviceUrl( request, project );
1315 
1316  //href needs to be a prefix
1317  QString hrefString = href.toString();
1318  hrefString.append( href.hasQuery() ? "&" : "?" );
1319  for ( const QString &styleName : currentLayer->styleManager()->styles() )
1320  {
1321  QDomElement styleElem = doc.createElement( QStringLiteral( "Style" ) );
1322  QDomElement styleNameElem = doc.createElement( QStringLiteral( "Name" ) );
1323  QDomText styleNameText = doc.createTextNode( styleName );
1324  styleNameElem.appendChild( styleNameText );
1325  QDomElement styleTitleElem = doc.createElement( QStringLiteral( "Title" ) );
1326  QDomText styleTitleText = doc.createTextNode( styleName );
1327  styleTitleElem.appendChild( styleTitleText );
1328  styleElem.appendChild( styleNameElem );
1329  styleElem.appendChild( styleTitleElem );
1330 
1331  // QString LegendURL for explicit layerbased GetLegendGraphic request
1332  QDomElement getLayerLegendGraphicElem = doc.createElement( QStringLiteral( "LegendURL" ) );
1333 
1334  QString customHrefString = currentLayer->legendUrl();
1335 
1336  QStringList getLayerLegendGraphicFormats;
1337  if ( !customHrefString.isEmpty() )
1338  {
1339  getLayerLegendGraphicFormats << currentLayer->legendUrlFormat();
1340  }
1341  else
1342  {
1343  getLayerLegendGraphicFormats << QStringLiteral( "image/png" ); // << "jpeg" << "image/jpeg"
1344  }
1345 
1346  for ( int i = 0; i < getLayerLegendGraphicFormats.size(); ++i )
1347  {
1348  QDomElement getLayerLegendGraphicFormatElem = doc.createElement( QStringLiteral( "Format" ) );
1349  QString getLayerLegendGraphicFormat = getLayerLegendGraphicFormats[i];
1350  QDomText getLayerLegendGraphicFormatText = doc.createTextNode( getLayerLegendGraphicFormat );
1351  getLayerLegendGraphicFormatElem.appendChild( getLayerLegendGraphicFormatText );
1352  getLayerLegendGraphicElem.appendChild( getLayerLegendGraphicFormatElem );
1353  }
1354 
1355  // no parameters on custom hrefUrl, because should link directly to graphic
1356  if ( customHrefString.isEmpty() )
1357  {
1358  QString layerName = currentLayer->name();
1359  if ( QgsServerProjectUtils::wmsUseLayerIds( *project ) )
1360  layerName = currentLayer->id();
1361  else if ( !currentLayer->shortName().isEmpty() )
1362  layerName = currentLayer->shortName();
1363  QUrlQuery mapUrl( hrefString );
1364  mapUrl.addQueryItem( QStringLiteral( "SERVICE" ), QStringLiteral( "WMS" ) );
1365  mapUrl.addQueryItem( QStringLiteral( "VERSION" ), version );
1366  mapUrl.addQueryItem( QStringLiteral( "REQUEST" ), QStringLiteral( "GetLegendGraphic" ) );
1367  mapUrl.addQueryItem( QStringLiteral( "LAYER" ), layerName );
1368  mapUrl.addQueryItem( QStringLiteral( "FORMAT" ), QStringLiteral( "image/png" ) );
1369  mapUrl.addQueryItem( QStringLiteral( "STYLE" ), styleNameText.data() );
1370  if ( version == QLatin1String( "1.3.0" ) )
1371  {
1372  mapUrl.addQueryItem( QStringLiteral( "SLD_VERSION" ), QStringLiteral( "1.1.0" ) );
1373  }
1374  customHrefString = mapUrl.toString();
1375  }
1376 
1377  QDomElement getLayerLegendGraphicORElem = doc.createElement( QStringLiteral( "OnlineResource" ) );
1378  getLayerLegendGraphicORElem.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
1379  getLayerLegendGraphicORElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) );
1380  getLayerLegendGraphicORElem.setAttribute( QStringLiteral( "xlink:href" ), customHrefString );
1381  getLayerLegendGraphicElem.appendChild( getLayerLegendGraphicORElem );
1382  styleElem.appendChild( getLayerLegendGraphicElem );
1383 
1384  layerElem.appendChild( styleElem );
1385  }
1386  }
1387 
1388  void appendCrsElementsToLayer( QDomDocument &doc, QDomElement &layerElement,
1389  const QStringList &crsList, const QStringList &constrainedCrsList )
1390  {
1391  if ( layerElement.isNull() )
1392  {
1393  return;
1394  }
1395 
1396  //insert the CRS elements after the title element to be in accordance with the WMS 1.3 specification
1397  QDomElement titleElement = layerElement.firstChildElement( QStringLiteral( "Title" ) );
1398  QDomElement abstractElement = layerElement.firstChildElement( QStringLiteral( "Abstract" ) );
1399  QDomElement keywordListElement = layerElement.firstChildElement( QStringLiteral( "KeywordList" ) );
1400  QDomElement CRSPrecedingElement = !keywordListElement.isNull() ? keywordListElement : !abstractElement.isNull() ? abstractElement : titleElement;
1401 
1402  if ( CRSPrecedingElement.isNull() )
1403  {
1404  // keyword list element is never empty
1405  const QDomElement keyElement = layerElement.firstChildElement( QStringLiteral( "KeywordList" ) );
1406  CRSPrecedingElement = keyElement;
1407  }
1408 
1409  //In case the number of advertised CRS is constrained
1410  if ( !constrainedCrsList.isEmpty() )
1411  {
1412  for ( int i = constrainedCrsList.size() - 1; i >= 0; --i )
1413  {
1414  appendCrsElementToLayer( doc, layerElement, CRSPrecedingElement, constrainedCrsList.at( i ) );
1415  }
1416  }
1417  else //no crs constraint
1418  {
1419  for ( const QString &crs : crsList )
1420  {
1421  appendCrsElementToLayer( doc, layerElement, CRSPrecedingElement, crs );
1422  }
1423  }
1424 
1425  //Support for CRS:84 is mandatory (equals EPSG:4326 with reversed axis)
1426  appendCrsElementToLayer( doc, layerElement, CRSPrecedingElement, QString( "CRS:84" ) );
1427  }
1428 
1429  void appendCrsElementToLayer( QDomDocument &doc, QDomElement &layerElement, const QDomElement &precedingElement,
1430  const QString &crsText )
1431  {
1432  if ( crsText.isEmpty() )
1433  return;
1434  QString version = doc.documentElement().attribute( QStringLiteral( "version" ) );
1435  QDomElement crsElement = doc.createElement( version == QLatin1String( "1.1.1" ) ? "SRS" : "CRS" );
1436  QDomText crsTextNode = doc.createTextNode( crsText );
1437  crsElement.appendChild( crsTextNode );
1438  layerElement.insertAfter( crsElement, precedingElement );
1439  }
1440 
1441  void appendLayerBoundingBoxes( QDomDocument &doc, QDomElement &layerElem, const QgsRectangle &lExtent,
1442  const QgsCoordinateReferenceSystem &layerCRS, const QStringList &crsList,
1443  const QStringList &constrainedCrsList, const QgsProject *project )
1444  {
1445  if ( layerElem.isNull() )
1446  {
1447  return;
1448  }
1449 
1450  QgsRectangle layerExtent = lExtent;
1451  if ( qgsDoubleNear( layerExtent.xMinimum(), layerExtent.xMaximum() ) || qgsDoubleNear( layerExtent.yMinimum(), layerExtent.yMaximum() ) )
1452  {
1453  //layer bbox cannot be empty
1454  layerExtent.grow( 0.000001 );
1455  }
1456 
1458  int wgs84precision = 6;
1459 
1460  QString version = doc.documentElement().attribute( QStringLiteral( "version" ) );
1461 
1462  //Ex_GeographicBoundingBox
1463  QDomElement ExGeoBBoxElement;
1464  //transform the layers native CRS into WGS84
1465  QgsRectangle wgs84BoundingRect;
1466  if ( !layerExtent.isNull() )
1467  {
1468  QgsCoordinateTransform exGeoTransform( layerCRS, wgs84, project );
1469  try
1470  {
1471  wgs84BoundingRect = exGeoTransform.transformBoundingBox( layerExtent );
1472  }
1473  catch ( const QgsCsException &cse )
1474  {
1475  QgsMessageLog::logMessage( QStringLiteral( "Error transforming extent: %1" ).arg( cse.what() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
1476  wgs84BoundingRect = QgsRectangle();
1477  }
1478  }
1479 
1480  if ( version == QLatin1String( "1.1.1" ) ) // WMS Version 1.1.1
1481  {
1482  ExGeoBBoxElement = doc.createElement( QStringLiteral( "LatLonBoundingBox" ) );
1483  ExGeoBBoxElement.setAttribute( QStringLiteral( "minx" ), qgsDoubleToString( QgsServerProjectUtils::floorWithPrecision( wgs84BoundingRect.xMinimum(), wgs84precision ), wgs84precision ) );
1484  ExGeoBBoxElement.setAttribute( QStringLiteral( "miny" ), qgsDoubleToString( QgsServerProjectUtils::floorWithPrecision( wgs84BoundingRect.yMinimum(), wgs84precision ), wgs84precision ) );
1485  ExGeoBBoxElement.setAttribute( QStringLiteral( "maxx" ), qgsDoubleToString( QgsServerProjectUtils::ceilWithPrecision( wgs84BoundingRect.xMaximum(), wgs84precision ), wgs84precision ) );
1486  ExGeoBBoxElement.setAttribute( QStringLiteral( "maxy" ), qgsDoubleToString( QgsServerProjectUtils::ceilWithPrecision( wgs84BoundingRect.yMaximum(), wgs84precision ), wgs84precision ) );
1487  }
1488  else // WMS Version 1.3.0
1489  {
1490  ExGeoBBoxElement = doc.createElement( QStringLiteral( "EX_GeographicBoundingBox" ) );
1491  QDomElement wBoundLongitudeElement = doc.createElement( QStringLiteral( "westBoundLongitude" ) );
1492  QDomText wBoundLongitudeText = doc.createTextNode( qgsDoubleToString( QgsServerProjectUtils::floorWithPrecision( wgs84BoundingRect.xMinimum(), wgs84precision ), wgs84precision ) );
1493  wBoundLongitudeElement.appendChild( wBoundLongitudeText );
1494  ExGeoBBoxElement.appendChild( wBoundLongitudeElement );
1495  QDomElement eBoundLongitudeElement = doc.createElement( QStringLiteral( "eastBoundLongitude" ) );
1496  QDomText eBoundLongitudeText = doc.createTextNode( qgsDoubleToString( QgsServerProjectUtils::ceilWithPrecision( wgs84BoundingRect.xMaximum(), wgs84precision ), wgs84precision ) );
1497  eBoundLongitudeElement.appendChild( eBoundLongitudeText );
1498  ExGeoBBoxElement.appendChild( eBoundLongitudeElement );
1499  QDomElement sBoundLatitudeElement = doc.createElement( QStringLiteral( "southBoundLatitude" ) );
1500  QDomText sBoundLatitudeText = doc.createTextNode( qgsDoubleToString( QgsServerProjectUtils::floorWithPrecision( wgs84BoundingRect.yMinimum(), wgs84precision ), wgs84precision ) );
1501  sBoundLatitudeElement.appendChild( sBoundLatitudeText );
1502  ExGeoBBoxElement.appendChild( sBoundLatitudeElement );
1503  QDomElement nBoundLatitudeElement = doc.createElement( QStringLiteral( "northBoundLatitude" ) );
1504  QDomText nBoundLatitudeText = doc.createTextNode( qgsDoubleToString( QgsServerProjectUtils::ceilWithPrecision( wgs84BoundingRect.yMaximum(), wgs84precision ), wgs84precision ) );
1505  nBoundLatitudeElement.appendChild( nBoundLatitudeText );
1506  ExGeoBBoxElement.appendChild( nBoundLatitudeElement );
1507  }
1508 
1509  if ( !wgs84BoundingRect.isNull() ) //LatLonBoundingBox / Ex_GeographicBounding box is optional
1510  {
1511  QDomElement lastCRSElem = layerElem.lastChildElement( version == QLatin1String( "1.1.1" ) ? "SRS" : "CRS" );
1512  if ( !lastCRSElem.isNull() )
1513  {
1514  layerElem.insertAfter( ExGeoBBoxElement, lastCRSElem );
1515  }
1516  else
1517  {
1518  layerElem.appendChild( ExGeoBBoxElement );
1519  }
1520  }
1521 
1522  //In case the number of advertised CRS is constrained
1523  if ( !constrainedCrsList.isEmpty() )
1524  {
1525  for ( int i = constrainedCrsList.size() - 1; i >= 0; --i )
1526  {
1527  appendLayerBoundingBox( doc, layerElem, layerExtent, layerCRS, constrainedCrsList.at( i ), project );
1528  }
1529  }
1530  else //no crs constraint
1531  {
1532  for ( const QString &crs : crsList )
1533  {
1534  appendLayerBoundingBox( doc, layerElem, layerExtent, layerCRS, crs, project );
1535  }
1536  }
1537  }
1538 
1539 
1540  void appendLayerBoundingBox( QDomDocument &doc, QDomElement &layerElem, const QgsRectangle &layerExtent,
1541  const QgsCoordinateReferenceSystem &layerCRS, const QString &crsText,
1542  const QgsProject *project )
1543  {
1544  if ( layerElem.isNull() )
1545  {
1546  return;
1547  }
1548 
1549  if ( crsText.isEmpty() )
1550  {
1551  return;
1552  }
1553 
1554  QString version = doc.documentElement().attribute( QStringLiteral( "version" ) );
1555 
1557 
1558  //transform the layers native CRS into CRS
1559  QgsRectangle crsExtent;
1560  if ( !layerExtent.isNull() )
1561  {
1562  QgsCoordinateTransform crsTransform( layerCRS, crs, project );
1563  try
1564  {
1565  crsExtent = crsTransform.transformBoundingBox( layerExtent );
1566  }
1567  catch ( QgsCsException &cse )
1568  {
1569  QgsMessageLog::logMessage( QStringLiteral( "Error transforming extent: %1" ).arg( cse.what() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
1570  return;
1571  }
1572  }
1573 
1574  if ( crsExtent.isNull() )
1575  {
1576  return;
1577  }
1578 
1579  int precision = 3;
1580  if ( crs.isGeographic() )
1581  {
1582  precision = 6;
1583  }
1584 
1585  //BoundingBox element
1586  QDomElement bBoxElement = doc.createElement( QStringLiteral( "BoundingBox" ) );
1587  if ( crs.isValid() )
1588  {
1589  bBoxElement.setAttribute( version == QLatin1String( "1.1.1" ) ? "SRS" : "CRS", crs.authid() );
1590  }
1591 
1592  if ( version != QLatin1String( "1.1.1" ) && crs.hasAxisInverted() )
1593  {
1594  crsExtent.invert();
1595  }
1596 
1597  bBoxElement.setAttribute( QStringLiteral( "minx" ), qgsDoubleToString( QgsServerProjectUtils::floorWithPrecision( crsExtent.xMinimum(), precision ), precision ) );
1598  bBoxElement.setAttribute( QStringLiteral( "miny" ), qgsDoubleToString( QgsServerProjectUtils::floorWithPrecision( crsExtent.yMinimum(), precision ), precision ) );
1599  bBoxElement.setAttribute( QStringLiteral( "maxx" ), qgsDoubleToString( QgsServerProjectUtils::ceilWithPrecision( crsExtent.xMaximum(), precision ), precision ) );
1600  bBoxElement.setAttribute( QStringLiteral( "maxy" ), qgsDoubleToString( QgsServerProjectUtils::ceilWithPrecision( crsExtent.yMaximum(), precision ), precision ) );
1601 
1602  QDomElement lastBBoxElem = layerElem.lastChildElement( QStringLiteral( "BoundingBox" ) );
1603  if ( !lastBBoxElem.isNull() )
1604  {
1605  layerElem.insertAfter( bBoxElement, lastBBoxElem );
1606  }
1607  else
1608  {
1609  lastBBoxElem = layerElem.lastChildElement( version == QLatin1String( "1.1.1" ) ? "LatLonBoundingBox" : "EX_GeographicBoundingBox" );
1610  if ( !lastBBoxElem.isNull() )
1611  {
1612  layerElem.insertAfter( bBoxElement, lastBBoxElem );
1613  }
1614  else
1615  {
1616  layerElem.appendChild( bBoxElement );
1617  }
1618  }
1619  }
1620 
1621  QgsRectangle layerBoundingBoxInProjectCrs( const QDomDocument &doc, const QDomElement &layerElem,
1622  const QgsProject *project )
1623  {
1624  QgsRectangle BBox;
1625  if ( layerElem.isNull() )
1626  {
1627  return BBox;
1628  }
1629 
1630  //read box coordinates and layer auth. id
1631  QDomElement boundingBoxElem = layerElem.firstChildElement( QStringLiteral( "BoundingBox" ) );
1632  if ( boundingBoxElem.isNull() )
1633  {
1634  return BBox;
1635  }
1636 
1637  double minx, miny, maxx, maxy;
1638  bool conversionOk;
1639  minx = boundingBoxElem.attribute( QStringLiteral( "minx" ) ).toDouble( &conversionOk );
1640  if ( !conversionOk )
1641  {
1642  return BBox;
1643  }
1644  miny = boundingBoxElem.attribute( QStringLiteral( "miny" ) ).toDouble( &conversionOk );
1645  if ( !conversionOk )
1646  {
1647  return BBox;
1648  }
1649  maxx = boundingBoxElem.attribute( QStringLiteral( "maxx" ) ).toDouble( &conversionOk );
1650  if ( !conversionOk )
1651  {
1652  return BBox;
1653  }
1654  maxy = boundingBoxElem.attribute( QStringLiteral( "maxy" ) ).toDouble( &conversionOk );
1655  if ( !conversionOk )
1656  {
1657  return BBox;
1658  }
1659 
1660 
1661  QString version = doc.documentElement().attribute( QStringLiteral( "version" ) );
1662 
1663  //create layer crs
1664  QgsCoordinateReferenceSystem layerCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( boundingBoxElem.attribute( version == QLatin1String( "1.1.1" ) ? "SRS" : "CRS" ) );
1665  if ( !layerCrs.isValid() )
1666  {
1667  return BBox;
1668  }
1669 
1670  BBox.setXMinimum( minx );
1671  BBox.setXMaximum( maxx );
1672  BBox.setYMinimum( miny );
1673  BBox.setYMaximum( maxy );
1674 
1675  if ( version != QLatin1String( "1.1.1" ) && layerCrs.hasAxisInverted() )
1676  {
1677  BBox.invert();
1678  }
1679 
1680  //get project crs
1681  QgsCoordinateTransform t( layerCrs, project->crs(), project );
1682 
1683  //transform
1684  try
1685  {
1686  BBox = t.transformBoundingBox( BBox );
1687  }
1688  catch ( const QgsCsException &cse )
1689  {
1690  QgsMessageLog::logMessage( QStringLiteral( "Error transforming extent: %1" ).arg( cse.what() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
1691  BBox = QgsRectangle();
1692  }
1693 
1694  return BBox;
1695  }
1696 
1697  bool crsSetFromLayerElement( const QDomElement &layerElement, QSet<QString> &crsSet )
1698  {
1699  if ( layerElement.isNull() )
1700  {
1701  return false;
1702  }
1703 
1704  crsSet.clear();
1705 
1706  QDomNodeList crsNodeList;
1707  crsNodeList = layerElement.elementsByTagName( QStringLiteral( "CRS" ) ); // WMS 1.3.0
1708  for ( int i = 0; i < crsNodeList.size(); ++i )
1709  {
1710  crsSet.insert( crsNodeList.at( i ).toElement().text() );
1711  }
1712 
1713  crsNodeList = layerElement.elementsByTagName( QStringLiteral( "SRS" ) ); // WMS 1.1.1
1714  for ( int i = 0; i < crsNodeList.size(); ++i )
1715  {
1716  crsSet.insert( crsNodeList.at( i ).toElement().text() );
1717  }
1718 
1719  return true;
1720  }
1721 
1722  void combineExtentAndCrsOfGroupChildren( QDomDocument &doc, QDomElement &groupElem, const QgsProject *project,
1723  bool considerMapExtent )
1724  {
1725  QgsRectangle combinedBBox;
1726  QSet<QString> combinedCRSSet;
1727  bool firstBBox = true;
1728  bool firstCRSSet = true;
1729 
1730  QDomNodeList layerChildren = groupElem.childNodes();
1731  for ( int j = 0; j < layerChildren.size(); ++j )
1732  {
1733  QDomElement childElem = layerChildren.at( j ).toElement();
1734 
1735  if ( childElem.tagName() != QLatin1String( "Layer" ) )
1736  continue;
1737 
1738  QgsRectangle bbox = layerBoundingBoxInProjectCrs( doc, childElem, project );
1739  if ( bbox.isNull() )
1740  {
1741  continue;
1742  }
1743 
1744  if ( !bbox.isEmpty() )
1745  {
1746  if ( firstBBox )
1747  {
1748  combinedBBox = bbox;
1749  firstBBox = false;
1750  }
1751  else
1752  {
1753  combinedBBox.combineExtentWith( bbox );
1754  }
1755  }
1756 
1757  //combine crs set
1758  QSet<QString> crsSet;
1759  if ( crsSetFromLayerElement( childElem, crsSet ) )
1760  {
1761  if ( firstCRSSet )
1762  {
1763  combinedCRSSet = crsSet;
1764  firstCRSSet = false;
1765  }
1766  else
1767  {
1768  combinedCRSSet.intersect( crsSet );
1769  }
1770  }
1771  }
1772 
1773  QStringList outputCrsList = QgsServerProjectUtils::wmsOutputCrsList( *project );
1774  appendCrsElementsToLayer( doc, groupElem, qgis::setToList( combinedCRSSet ), outputCrsList );
1775 
1776  QgsCoordinateReferenceSystem groupCRS = project->crs();
1777  if ( considerMapExtent )
1778  {
1779  QgsRectangle mapRect = QgsServerProjectUtils::wmsExtent( *project );
1780  if ( !mapRect.isEmpty() )
1781  {
1782  combinedBBox = mapRect;
1783  }
1784  }
1785  appendLayerBoundingBoxes( doc, groupElem, combinedBBox, groupCRS, qgis::setToList( combinedCRSSet ), outputCrsList, project );
1786 
1787  }
1788 
1789  void appendDrawingOrder( QDomDocument &doc, QDomElement &parentElem, QgsServerInterface *serverIface,
1790  const QgsProject *project )
1791  {
1792 #ifdef HAVE_SERVER_PYTHON_PLUGINS
1793  QgsAccessControl *accessControl = serverIface->accessControls();
1794 #else
1795  ( void )serverIface;
1796 #endif
1797  bool useLayerIds = QgsServerProjectUtils::wmsUseLayerIds( *project );
1798  QStringList restrictedLayers = QgsServerProjectUtils::wmsRestrictedLayers( *project );
1799 
1800  QStringList layerList;
1801 
1802  const QgsLayerTree *projectLayerTreeRoot = project->layerTreeRoot();
1803  QList< QgsMapLayer * > projectLayerOrder = projectLayerTreeRoot->layerOrder();
1804  for ( int i = 0; i < projectLayerOrder.size(); ++i )
1805  {
1806  QgsMapLayer *l = projectLayerOrder.at( i );
1807 
1808  if ( restrictedLayers.contains( l->name() ) ) //unpublished layer
1809  {
1810  continue;
1811  }
1812 #ifdef HAVE_SERVER_PYTHON_PLUGINS
1813  if ( accessControl && !accessControl->layerReadPermission( l ) )
1814  {
1815  continue;
1816  }
1817 #endif
1818  QString wmsName = l->name();
1819  if ( useLayerIds )
1820  {
1821  wmsName = l->id();
1822  }
1823  else if ( !l->shortName().isEmpty() )
1824  {
1825  wmsName = l->shortName();
1826  }
1827 
1828  layerList << wmsName;
1829  }
1830 
1831  if ( !layerList.isEmpty() )
1832  {
1833  QStringList reversedList;
1834  reversedList.reserve( layerList.size() );
1835  for ( int i = layerList.size() - 1; i >= 0; --i )
1836  reversedList << layerList[ i ];
1837 
1838  QDomElement layerDrawingOrderElem = doc.createElement( QStringLiteral( "LayerDrawingOrder" ) );
1839  QDomText drawingOrderText = doc.createTextNode( reversedList.join( ',' ) );
1840  layerDrawingOrderElem.appendChild( drawingOrderText );
1841  parentElem.appendChild( layerDrawingOrderElem );
1842  }
1843  }
1844 
1845  void appendLayerProjectSettings( QDomDocument &doc, QDomElement &layerElem, QgsMapLayer *currentLayer )
1846  {
1847  if ( !currentLayer )
1848  {
1849  return;
1850  }
1851 
1852  // Layer tree name
1853  QDomElement treeNameElem = doc.createElement( QStringLiteral( "TreeName" ) );
1854  QDomText treeNameText = doc.createTextNode( currentLayer->name() );
1855  treeNameElem.appendChild( treeNameText );
1856  layerElem.appendChild( treeNameElem );
1857 
1858  switch ( currentLayer->type() )
1859  {
1861  {
1862  QgsVectorLayer *vLayer = static_cast<QgsVectorLayer *>( currentLayer );
1863  const QSet<QString> &excludedAttributes = vLayer->excludeAttributesWms();
1864 
1865  int displayFieldIdx = -1;
1866  QString displayField = QStringLiteral( "maptip" );
1867  QgsExpression exp( vLayer->displayExpression() );
1868  if ( exp.isField() )
1869  {
1870  displayField = static_cast<const QgsExpressionNodeColumnRef *>( exp.rootNode() )->name();
1871  displayFieldIdx = vLayer->fields().lookupField( displayField );
1872  }
1873 
1874  //attributes
1875  QDomElement attributesElem = doc.createElement( QStringLiteral( "Attributes" ) );
1876  const QgsFields layerFields = vLayer->fields();
1877  for ( int idx = 0; idx < layerFields.count(); ++idx )
1878  {
1879  QgsField field = layerFields.at( idx );
1880  if ( excludedAttributes.contains( field.name() ) )
1881  {
1882  continue;
1883  }
1884  // field alias in case of displayField
1885  if ( idx == displayFieldIdx )
1886  {
1887  displayField = vLayer->attributeDisplayName( idx );
1888  }
1889  QDomElement attributeElem = doc.createElement( QStringLiteral( "Attribute" ) );
1890  attributeElem.setAttribute( QStringLiteral( "name" ), field.name() );
1891  attributeElem.setAttribute( QStringLiteral( "type" ), QVariant::typeToName( field.type() ) );
1892  attributeElem.setAttribute( QStringLiteral( "typeName" ), field.typeName() );
1893  QString alias = field.alias();
1894  if ( !alias.isEmpty() )
1895  {
1896  attributeElem.setAttribute( QStringLiteral( "alias" ), alias );
1897  }
1898 
1899  //edit type to text
1900  attributeElem.setAttribute( QStringLiteral( "editType" ), vLayer->editorWidgetSetup( idx ).type() );
1901  attributeElem.setAttribute( QStringLiteral( "comment" ), field.comment() );
1902  attributeElem.setAttribute( QStringLiteral( "length" ), field.length() );
1903  attributeElem.setAttribute( QStringLiteral( "precision" ), field.precision() );
1904  attributesElem.appendChild( attributeElem );
1905  }
1906 
1907  //displayfield
1908  layerElem.setAttribute( QStringLiteral( "displayField" ), displayField );
1909 
1910  //primary key
1911  QgsAttributeList pkAttributes = vLayer->primaryKeyAttributes();
1912  if ( pkAttributes.size() > 0 )
1913  {
1914  QDomElement pkElem = doc.createElement( QStringLiteral( "PrimaryKey" ) );
1915  QgsAttributeList::const_iterator pkIt = pkAttributes.constBegin();
1916  for ( ; pkIt != pkAttributes.constEnd(); ++pkIt )
1917  {
1918  QDomElement pkAttributeElem = doc.createElement( QStringLiteral( "PrimaryKeyAttribute" ) );
1919  QDomText pkAttName = doc.createTextNode( layerFields.at( *pkIt ).name() );
1920  pkAttributeElem.appendChild( pkAttName );
1921  pkElem.appendChild( pkAttributeElem );
1922  }
1923  layerElem.appendChild( pkElem );
1924  }
1925 
1926  //geometry type
1927  layerElem.setAttribute( QStringLiteral( "geometryType" ), QgsWkbTypes::displayString( vLayer->wkbType() ) );
1928 
1929  //opacity
1930  layerElem.setAttribute( QStringLiteral( "opacity" ), QString::number( vLayer->opacity() ) );
1931 
1932  layerElem.appendChild( attributesElem );
1933  break;
1934  }
1935 
1937  {
1938  const QgsDataProvider *provider = currentLayer->dataProvider();
1939  if ( provider && provider->name() == "wms" )
1940  {
1941  //advertise as web map background layer
1942  QVariant wmsBackgroundLayer = currentLayer->customProperty( QStringLiteral( "WMSBackgroundLayer" ), false );
1943  QDomElement wmsBackgroundLayerElem = doc.createElement( "WMSBackgroundLayer" );
1944  QDomText wmsBackgroundLayerText = doc.createTextNode( wmsBackgroundLayer.toBool() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
1945  wmsBackgroundLayerElem.appendChild( wmsBackgroundLayerText );
1946  layerElem.appendChild( wmsBackgroundLayerElem );
1947 
1948  //publish datasource
1949  QVariant wmsPublishDataSourceUrl = currentLayer->customProperty( QStringLiteral( "WMSPublishDataSourceUrl" ), false );
1950  if ( wmsPublishDataSourceUrl.toBool() )
1951  {
1952  bool tiled = qobject_cast< const QgsRasterDataProvider * >( provider )
1953  ? !qobject_cast< const QgsRasterDataProvider * >( provider )->nativeResolutions().isEmpty()
1954  : false;
1955 
1956  QDomElement dataSourceElem = doc.createElement( tiled ? QStringLiteral( "WMTSDataSource" ) : QStringLiteral( "WMSDataSource" ) );
1957  QDomText dataSourceUri = doc.createTextNode( provider->dataSourceUri() );
1958  dataSourceElem.appendChild( dataSourceUri );
1959  layerElem.appendChild( dataSourceElem );
1960  }
1961  }
1962 
1963  QVariant wmsPrintLayer = currentLayer->customProperty( QStringLiteral( "WMSPrintLayer" ) );
1964  if ( wmsPrintLayer.isValid() )
1965  {
1966  QDomElement wmsPrintLayerElem = doc.createElement( "WMSPrintLayer" );
1967  QDomText wmsPrintLayerText = doc.createTextNode( wmsPrintLayer.toString() );
1968  wmsPrintLayerElem.appendChild( wmsPrintLayerText );
1969  layerElem.appendChild( wmsPrintLayerElem );
1970  }
1971 
1972  //opacity
1973  QgsRasterLayer *rl = static_cast<QgsRasterLayer *>( currentLayer );
1974  QgsRasterRenderer *rasterRenderer = rl->renderer();
1975  if ( rasterRenderer )
1976  {
1977  layerElem.setAttribute( QStringLiteral( "opacity" ), QString::number( rasterRenderer->opacity() ) );
1978  }
1979  break;
1980  }
1981 
1985  break;
1986  }
1987  }
1988 
1989  void addKeywordListElement( const QgsProject *project, QDomDocument &doc, QDomElement &parent )
1990  {
1991  bool sia2045 = QgsServerProjectUtils::wmsInfoFormatSia2045( *project );
1992 
1993  QDomElement keywordsElem = doc.createElement( QStringLiteral( "KeywordList" ) );
1994  //add default keyword
1995  QDomElement keywordElem = doc.createElement( QStringLiteral( "Keyword" ) );
1996  keywordElem.setAttribute( QStringLiteral( "vocabulary" ), QStringLiteral( "ISO" ) );
1997  QDomText keywordText = doc.createTextNode( QStringLiteral( "infoMapAccessService" ) );
1998  keywordElem.appendChild( keywordText );
1999  keywordsElem.appendChild( keywordElem );
2000  parent.appendChild( keywordsElem );
2001  QStringList keywords = QgsServerProjectUtils::owsServiceKeywords( *project );
2002  for ( const QString &keyword : qgis::as_const( keywords ) )
2003  {
2004  if ( !keyword.isEmpty() )
2005  {
2006  keywordElem = doc.createElement( QStringLiteral( "Keyword" ) );
2007  keywordText = doc.createTextNode( keyword );
2008  keywordElem.appendChild( keywordText );
2009  if ( sia2045 )
2010  {
2011  keywordElem.setAttribute( QStringLiteral( "vocabulary" ), QStringLiteral( "SIA_Geo405" ) );
2012  }
2013  keywordsElem.appendChild( keywordElem );
2014  }
2015  }
2016  parent.appendChild( keywordsElem );
2017  }
2018  }
2019 
2020  bool hasQueryableChildren( const QgsLayerTreeNode *childNode, const QStringList &wmsRestrictedLayers )
2021  {
2022  if ( childNode->nodeType() == QgsLayerTreeNode::NodeGroup )
2023  {
2024  for ( int j = 0; j < childNode->children().size(); ++j )
2025  {
2026  if ( hasQueryableChildren( childNode->children().at( j ), wmsRestrictedLayers ) )
2027  return true;
2028  }
2029  return false;
2030  }
2031  else if ( childNode->nodeType() == QgsLayerTreeNode::NodeLayer )
2032  {
2033  const auto treeLayer { static_cast<const QgsLayerTreeLayer *>( childNode ) };
2034  const auto l { treeLayer->layer() };
2035  return ! wmsRestrictedLayers.contains( l->name() ) && l->flags().testFlag( QgsMapLayer::Identifiable );
2036  }
2037  return false;
2038  }
2039 
2040 
2041 } // namespace QgsWms
2042 
2043 
2044 
2045 
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:88
QgsLayoutItem::id
QString id() const
Returns the item's ID name.
Definition: qgslayoutitem.h:354
QgsProject::title
QString title() const
Returns the project's title.
Definition: qgsproject.cpp:479
QgsLayerTreeNode
Definition: qgslayertreenode.h:74
qgsmaplayerstylemanager.h
QgsMapLayer::attributionUrl
QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:394
QgsVectorLayer::wkbType
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
Definition: qgsvectorlayer.cpp:664
qgsrasterlayer.h
QgsMapLayer::dataUrlFormat
QString dataUrlFormat() const
Returns the DataUrl format of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:356
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
Definition: qgsdataprovider.h:41
QgsPrintLayout::name
QString name
Definition: qgsprintlayout.h:33
QgsCapabilitiesCache
Definition: qgscapabilitiescache.h:34
QgsWms::hasQueryableChildren
bool hasQueryableChildren(const QgsLayerTreeNode *childNode, const QStringList &wmsRestrictedLayers)
Definition: qgswmsgetcapabilities.cpp:2037
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:812
QgsRectangle::setXMinimum
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:130
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
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:274
QgsLayerTreeNode::NodeLayer
@ NodeLayer
Leaf node pointing to a layer.
Definition: qgslayertreenode.h:102
crs
const QgsCoordinateReferenceSystem & crs
Definition: qgswfsgetfeature.cpp:105
QgsCoordinateReferenceSystem::fromOgcWmsCrs
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
Definition: qgscoordinatereferencesystem.cpp:200
QgsExpressionNodeColumnRef
An expression node which takes it value from a feature's field.
Definition: qgsexpressionnodeimpl.h:400
QgsRectangle::xMaximum
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
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
Definition: qgsfields.h:44
qgsserverprojectutils.h
qgslayoutmanager.h
QgsMapLayer::styleManager
QgsMapLayerStyleManager * styleManager() const
Gets access to the layer's style manager.
Definition: qgsmaplayer.cpp:1797
QgsLayoutItemHtml
Definition: qgslayoutitemhtml.h:36
QgsProject::transformContext
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:99
qgswmsgetcapabilities.h
geoEpsgCrsAuthId
CONSTLATIN1STRING geoEpsgCrsAuthId()
Geographic coord sys from EPSG authority.
Definition: qgis.h:666
QgsAccessControl::layerReadPermission
bool layerReadPermission(const QgsMapLayer *layer) const
Returns the layer read right.
Definition: qgsaccesscontrol.cpp:105
QgsField::typeName
QString typeName() const
Gets the field type.
Definition: qgsfield.cpp:139
QgsMapLayer::abstract
QString abstract() const
Returns the abstract of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:302
qgsrasterrenderer.h
QgsServerRequest
Definition: qgsserverrequest.h:38
QgsCoordinateReferenceSystem::hasAxisInverted
bool hasAxisInverted() const
Returns whether axis is inverted (e.g., for WMS 1.3) for the CRS.
Definition: qgscoordinatereferencesystem.cpp:808
QgsVectorLayerServerProperties::WmsDimensionInfo
Setting to define QGIS Server WMS Dimension.
Definition: qgsvectorlayerserverproperties.h:60
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
QgsProject::mapLayer
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
Definition: qgsproject.cpp:3124
QgsDataProvider::dataSourceUri
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
Definition: qgsdataprovider.h:142
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
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:458
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:668
QgsRectangle
Definition: qgsrectangle.h:41
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:338
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:26
QgsProject
Definition: qgsproject.h:92
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:2966
QgsServerRequest::Parameters
QMap< QString, QString > Parameters
Definition: qgsserverrequest.h:59
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:1434
QgsVectorLayer::excludeAttributesWms
QSet< QString > excludeAttributesWms() const
A set of attributes that are not advertised in WMS requests with QGIS server.
Definition: qgsvectorlayer.h:1819
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:5492
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:3280
QgsField::precision
int precision
Definition: qgsfield.h:56
QgsEditorWidgetSetup::type
QString type() const
Definition: qgseditorwidgetsetup.h:59
QgsServerProjectUtils::wmsInspireMetadataUrl
SERVER_EXPORT QString wmsInspireMetadataUrl(const QgsProject &project)
Returns the Inspire metadata URL.
Definition: qgsserverprojectutils.cpp:231
QgsServerProjectUtils::wmsInspireLanguage
SERVER_EXPORT QString wmsInspireLanguage(const QgsProject &project)
Returns the Inspire language.
Definition: qgsserverprojectutils.cpp:226
precision
int precision
Definition: qgswfsgetfeature.cpp:103
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:211
QgsProject::layerTreeRoot
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
Definition: qgsproject.cpp:3016
QgsServerInterface::configFilePath
virtual QString configFilePath()=0
Returns the configuration file path.
QgsCsException
Definition: qgsexception.h:65
QgsLayerTree
Definition: qgslayertree.h:32
QgsPrintLayout
Print layout, a QgsLayout subclass for static or atlas-based layouts.
Definition: qgsprintlayout.h:30
QgsServerProjectUtils::wmsMaxHeight
SERVER_EXPORT int wmsMaxHeight(const QgsProject &project)
Returns the maximum height for WMS images defined in a QGIS project.
Definition: qgsserverprojectutils.cpp: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:842
QgsMapLayer::dataProvider
virtual QgsDataProvider * dataProvider()
Returns the layer's data provider, it may be nullptr.
Definition: qgsmaplayer.cpp:169
QgsMapLayer::keywordList
QString keywordList() const
Returns the keyword list of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:318
QgsMapLayer::legendUrlFormat
QString legendUrlFormat() const
Returns the format for a URL based layer legend.
Definition: qgsmaplayer.h:989
QgsMapLayer::metadataUrlFormat
QString metadataUrlFormat() const
Returns the metadata format of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:450
QgsLayoutAtlas::enabled
bool enabled() const
Returns whether the atlas generation is enabled.
Definition: qgslayoutatlas.h:67
QgsRasterRenderer
Definition: qgsrasterrenderer.h:38
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:40
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
Definition: qgslayertreelayer.h:43
QgsLayoutAtlas::coverageLayer
QgsVectorLayer * coverageLayer() const
Returns the coverage layer used for the atlas features.
Definition: qgslayoutatlas.h:116
QgsServerProjectUtils::floorWithPrecision
SERVER_EXPORT double floorWithPrecision(double number, int places)
Returns a double less than number to the specified number of places.
Definition: qgsserverprojectutils.cpp: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:1299
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:3949
QgsField::comment
QString comment
Definition: qgsfield.h:58
QgsLayerTreeGroup
Definition: qgslayertreegroup.h:34
QgsVectorLayerServerProperties::WmsDimensionInfo::ReferenceValue
@ ReferenceValue
Remove from current selection.
Definition: qgsvectorlayerserverproperties.h:72
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:185
QgsCoordinateReferenceSystem::isValid
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Definition: qgscoordinatereferencesystem.cpp:902
QgsRectangle::setXMaximum
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:135
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:110
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:286
QgsLayoutPageCollection::page
QgsLayoutItemPage * page(int pageNumber)
Returns a specific page (by pageNumber) from the collection.
Definition: qgslayoutpagecollection.cpp:460
QgsVectorLayer::primaryKeyAttributes
QgsAttributeList primaryKeyAttributes() const
Returns the list of attributes which make up the layer's primary keys.
Definition: qgsvectorlayer.cpp:3285
QgsMapLayer::hasScaleBasedVisibility
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
Definition: qgsmaplayer.cpp:676
QgsRasterLayer
Definition: qgsrasterlayer.h:72
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:741
QgsMapLayer::maximumScale
double maximumScale() const
Returns the maximum map scale (i.e.
Definition: qgsmaplayer.cpp:725
QgsLayerTreeNode::isExpanded
bool isExpanded() const
Returns whether the node should be shown as expanded or collapsed in GUI.
Definition: qgslayertreenode.cpp:104
QgsCoordinateReferenceSystem
Definition: qgscoordinatereferencesystem.h:206
QgsLayoutManager::printLayouts
QList< QgsPrintLayout * > printLayouts() const
Returns a list of all print layouts contained in the manager.
Definition: qgslayoutmanager.cpp:109
QgsRectangle::yMaximum
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
QgsWms::getComposerTemplatesElement
QDomElement getComposerTemplatesElement(QDomDocument &doc, const QgsProject *project)
Create ComposerTemplates element for get capabilities document.
Definition: qgswmsgetcapabilities.cpp:697
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
qgsvectorlayer.h
QgsLayoutItemMap
Layout graphical items for displaying a map.
Definition: qgslayoutitemmap.h:39
QgsAccessControl
A helper class that centralizes restrictions given by all the access control filter plugins.
Definition: qgsaccesscontrol.h:36
QgsMapLayer::legendUrl
QString legendUrl() const
Returns the URL for the layer's legend.
Definition: qgsmaplayer.h:979
QgsWms
WMS implementation.
Definition: qgsdxfwriter.cpp:22
QgsVectorLayer::serverProperties
QgsVectorLayerServerProperties * serverProperties() const
Returns QGIS Server Properties of the vector layer.
Definition: qgsvectorlayer.h:732
QgsWms::getCapabilityElement
QDomElement getCapabilityElement(QDomDocument &doc, const QgsProject *project, const QString &version, const QgsServerRequest &request, bool projectSettings)
Create Capability element for get capabilities document.
Definition: qgswmsgetcapabilities.cpp:449
QgsMapLayer::Identifiable
@ Identifiable
If the layer is identifiable using the identify map tool and as a WMS layer.
Definition: qgsmaplayer.h:142
QgsWkbTypes::NoGeometry
@ NoGeometry
Definition: qgswkbtypes.h:84
QgsMapLayer::customProperty
Q_INVOKABLE QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
Definition: qgsmaplayer.cpp:1718
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
QgsVectorLayer::attributeDisplayName
QString attributeDisplayName(int index) const
Convenience function that returns the attribute alias if defined or the field name else.
Definition: qgsvectorlayer.cpp:3138
QgsWms::serviceUrl
QUrl serviceUrl(const QgsServerRequest &request, const QgsProject *project)
Returns WMS service URL.
Definition: qgswmsutils.cpp:49
QgsVectorLayer
Definition: qgsvectorlayer.h:385
QgsServerInterface::cacheManager
virtual QgsServerCacheManager * cacheManager() const =0
Gets the registered server cache filters.
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
Definition: qgsmaplayer.h:81
QgsWkbTypes::displayString
static QString displayString(Type type)
Returns a display string type for a WKB type, e.g., the geometry name used in WKT geometry representa...
Definition: qgswkbtypes.cpp:145
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:414
QgsPrintLayout::atlas
QgsLayoutAtlas * atlas()
Returns the print layout's atlas.
Definition: qgsprintlayout.cpp:58
QgsMapLayer::name
QString name
Definition: qgsmaplayer.h:85
QgsServerProjectUtils::owsServiceAccessConstraints
SERVER_EXPORT QString owsServiceAccessConstraints(const QgsProject &project)
Returns the owsService access constraints defined in project.
Definition: qgsserverprojectutils.cpp:110
QgsRectangle::yMinimum
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
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:40
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:247
QgsWms::getInspireCapabilitiesElement
QDomElement getInspireCapabilitiesElement(QDomDocument &doc, const QgsProject *project)
Create InspireCapabilities element for get capabilities document.
Definition: qgswmsgetcapabilities.cpp:591
qgsexception.h
QgsVectorLayerServerProperties::WmsDimensionInfo::MinValue
@ MinValue
Add selection to current selection.
Definition: qgsvectorlayerserverproperties.h:70
QgsVectorLayer::opacity
double opacity
Definition: qgsvectorlayer.h:394
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
Definition: qgslayoutitemlabel.h:34
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:324
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
Definition: qgsexpression.h:113
QgsVectorLayer::displayExpression
QString displayExpression
Definition: qgsvectorlayer.h:390
QgsCoordinateTransform
Definition: qgscoordinatetransform.h:52
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:376
QgsMapLayer::metadataUrlType
QString metadataUrlType() const
Returns the metadata type of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:432
QgsServerProjectUtils::owsServiceKeywords
SERVER_EXPORT QStringList owsServiceKeywords(const QgsProject &project)
Returns the owsService keywords defined in project.
Definition: qgsserverprojectutils.cpp:48
QgsLayoutAtlas
Definition: qgslayoutatlas.h:41
QgsProject::crs
QgsCoordinateReferenceSystem crs
Definition: qgsproject.h:98
QgsMapLayer::type
QgsMapLayerType type() const
Returns the type of the layer.
Definition: qgsmaplayer.cpp:129
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
Definition: qgsserverinterface.h:60
QgsMapLayerType::PluginLayer
@ PluginLayer
QgsRectangle::setYMinimum
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:140
QgsLayoutMeasurement
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
Definition: qgslayoutmeasurement.h:33
QgsLayoutMultiFrame::frameCount
int frameCount() const
Returns the number of frames associated with this multiframe.
Definition: qgslayoutmultiframe.h:265
QgsField::type
QVariant::Type type
Definition: qgsfield.h:57
QgsRectangle::setYMaximum
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:145
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:343
qgsvectorlayerserverproperties.h
QgsServerResponse
Definition: qgsserverresponse.h:43
QgsRectangle::xMinimum
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
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
Definition: qgsfield.h:49