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