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