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