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