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