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