QGIS API Documentation 3.32.0-Lima (311a8cb8a6)
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 elem.appendChild( dcpTypeElem.cloneNode().toElement() ); //this is the same as for 'GetCapabilities'
460 requestElem.appendChild( elem );
461
462 //wms:GetFeatureInfo
463 elem = doc.createElement( QStringLiteral( "GetFeatureInfo" ) );
464 appendFormat( elem, QStringLiteral( "text/plain" ) );
465 appendFormat( elem, QStringLiteral( "text/html" ) );
466 appendFormat( elem, QStringLiteral( "text/xml" ) );
467 appendFormat( elem, QStringLiteral( "application/vnd.ogc.gml" ) );
468 appendFormat( elem, QStringLiteral( "application/vnd.ogc.gml/3.1.1" ) );
469 appendFormat( elem, QStringLiteral( "application/json" ) );
470 appendFormat( elem, QStringLiteral( "application/geo+json" ) );
471 elem.appendChild( dcpTypeElem.cloneNode().toElement() ); //this is the same as for 'GetCapabilities'
472 requestElem.appendChild( elem );
473
474 //wms:GetLegendGraphic
475 elem = doc.createElement( ( version == QLatin1String( "1.1.1" ) ? "GetLegendGraphic" : "sld:GetLegendGraphic" )/*wms:GetLegendGraphic*/ );
476 appendFormat( elem, QStringLiteral( "image/jpeg" ) );
477 appendFormat( elem, QStringLiteral( "image/png" ) );
478 appendFormat( elem, QStringLiteral( "application/json" ) );
479 elem.appendChild( dcpTypeElem.cloneNode().toElement() ); //this is the same as for 'GetCapabilities'
480 requestElem.appendChild( elem );
481
482 //wms:DescribeLayer
483 elem = doc.createElement( ( version == QLatin1String( "1.1.1" ) ? "DescribeLayer" : "sld:DescribeLayer" )/*wms:GetLegendGraphic*/ );
484 appendFormat( elem, QStringLiteral( "text/xml" ) );
485 elem.appendChild( dcpTypeElem.cloneNode().toElement() ); //this is the same as for 'GetCapabilities'
486 requestElem.appendChild( elem );
487
488 //wms:GetStyles
489 elem = doc.createElement( ( version == QLatin1String( "1.1.1" ) ? "GetStyles" : "qgs:GetStyles" )/*wms:GetStyles*/ );
490 appendFormat( elem, QStringLiteral( "text/xml" ) );
491 elem.appendChild( dcpTypeElem.cloneNode().toElement() ); //this is the same as for 'GetCapabilities'
492 requestElem.appendChild( elem );
493
494 if ( ( !serverIface->serverSettings() || !serverIface->serverSettings()->getPrintDisabled() ) &&
495 projectSettings ) //remove composer templates from GetCapabilities in the long term
496 {
497 //wms:GetPrint
498 elem = doc.createElement( QStringLiteral( "GetPrint" ) /*wms:GetPrint*/ );
499 appendFormat( elem, QStringLiteral( "svg" ) );
500 appendFormat( elem, QStringLiteral( "png" ) );
501 appendFormat( elem, QStringLiteral( "pdf" ) );
502 elem.appendChild( dcpTypeElem.cloneNode().toElement() ); //this is the same as for 'GetCapabilities'
503 requestElem.appendChild( elem );
504 }
505
506 //Exception element is mandatory
507 elem = doc.createElement( QStringLiteral( "Exception" ) );
508 appendFormat( elem, ( version == QLatin1String( "1.1.1" ) ? "application/vnd.ogc.se_xml" : "XML" ) );
509 capabilityElem.appendChild( elem );
510
511 //UserDefinedSymbolization element
512 if ( version == QLatin1String( "1.3.0" ) )
513 {
514 elem = doc.createElement( QStringLiteral( "sld:UserDefinedSymbolization" ) );
515 elem.setAttribute( QStringLiteral( "SupportSLD" ), QStringLiteral( "1" ) );
516 elem.setAttribute( QStringLiteral( "UserLayer" ), QStringLiteral( "0" ) );
517 elem.setAttribute( QStringLiteral( "UserStyle" ), QStringLiteral( "1" ) );
518 elem.setAttribute( QStringLiteral( "RemoteWFS" ), QStringLiteral( "0" ) );
519 elem.setAttribute( QStringLiteral( "InlineFeature" ), QStringLiteral( "0" ) );
520 elem.setAttribute( QStringLiteral( "RemoteWCS" ), QStringLiteral( "0" ) );
521 capabilityElem.appendChild( elem );
522
524 {
525 capabilityElem.appendChild( getInspireCapabilitiesElement( doc, project ) );
526 }
527 }
528
529 return capabilityElem;
530 }
531
532 QDomElement getInspireCapabilitiesElement( QDomDocument &doc, const QgsProject *project )
533 {
534 QDomElement inspireCapabilitiesElem;
535
537 return inspireCapabilitiesElem;
538
539 inspireCapabilitiesElem = doc.createElement( QStringLiteral( "inspire_vs:ExtendedCapabilities" ) );
540
541 QString inspireMetadataUrl = QgsServerProjectUtils::wmsInspireMetadataUrl( *project );
542 // inspire scenario 1
543 if ( !inspireMetadataUrl.isEmpty() )
544 {
545 QDomElement inspireCommonMetadataUrlElem = doc.createElement( QStringLiteral( "inspire_common:MetadataUrl" ) );
546 inspireCommonMetadataUrlElem.setAttribute( QStringLiteral( "xsi:type" ), QStringLiteral( "inspire_common:resourceLocatorType" ) );
547
548 QDomElement inspireCommonMetadataUrlUrlElem = doc.createElement( QStringLiteral( "inspire_common:URL" ) );
549 inspireCommonMetadataUrlUrlElem.appendChild( doc.createTextNode( inspireMetadataUrl ) );
550 inspireCommonMetadataUrlElem.appendChild( inspireCommonMetadataUrlUrlElem );
551
552 QString inspireMetadataUrlType = QgsServerProjectUtils::wmsInspireMetadataUrlType( *project );
553 if ( !inspireMetadataUrlType.isNull() )
554 {
555 QDomElement inspireCommonMetadataUrlMediaTypeElem = doc.createElement( QStringLiteral( "inspire_common:MediaType" ) );
556 inspireCommonMetadataUrlMediaTypeElem.appendChild( doc.createTextNode( inspireMetadataUrlType ) );
557 inspireCommonMetadataUrlElem.appendChild( inspireCommonMetadataUrlMediaTypeElem );
558 }
559
560 inspireCapabilitiesElem.appendChild( inspireCommonMetadataUrlElem );
561 }
562 else
563 {
564 QDomElement inspireCommonResourceTypeElem = doc.createElement( QStringLiteral( "inspire_common:ResourceType" ) );
565 inspireCommonResourceTypeElem.appendChild( doc.createTextNode( QStringLiteral( "service" ) ) );
566 inspireCapabilitiesElem.appendChild( inspireCommonResourceTypeElem );
567
568 QDomElement inspireCommonSpatialDataServiceTypeElem = doc.createElement( QStringLiteral( "inspire_common:SpatialDataServiceType" ) );
569 inspireCommonSpatialDataServiceTypeElem.appendChild( doc.createTextNode( QStringLiteral( "view" ) ) );
570 inspireCapabilitiesElem.appendChild( inspireCommonSpatialDataServiceTypeElem );
571
572 QString inspireTemporalReference = QgsServerProjectUtils::wmsInspireTemporalReference( *project );
573 if ( !inspireTemporalReference.isNull() )
574 {
575 QDomElement inspireCommonTemporalReferenceElem = doc.createElement( QStringLiteral( "inspire_common:TemporalReference" ) );
576 QDomElement inspireCommonDateOfLastRevisionElem = doc.createElement( QStringLiteral( "inspire_common:DateOfLastRevision" ) );
577 inspireCommonDateOfLastRevisionElem.appendChild( doc.createTextNode( inspireTemporalReference ) );
578 inspireCommonTemporalReferenceElem.appendChild( inspireCommonDateOfLastRevisionElem );
579 inspireCapabilitiesElem.appendChild( inspireCommonTemporalReferenceElem );
580 }
581
582 QDomElement inspireCommonMetadataPointOfContactElem = doc.createElement( QStringLiteral( "inspire_common:MetadataPointOfContact" ) );
583
584 QString contactOrganization = QgsServerProjectUtils::owsServiceContactOrganization( *project );
585 QDomElement inspireCommonOrganisationNameElem = doc.createElement( QStringLiteral( "inspire_common:OrganisationName" ) );
586 if ( !contactOrganization.isNull() )
587 {
588 inspireCommonOrganisationNameElem.appendChild( doc.createTextNode( contactOrganization ) );
589 }
590 inspireCommonMetadataPointOfContactElem.appendChild( inspireCommonOrganisationNameElem );
591
592 QString contactMail = QgsServerProjectUtils::owsServiceContactMail( *project );
593 QDomElement inspireCommonEmailAddressElem = doc.createElement( QStringLiteral( "inspire_common:EmailAddress" ) );
594 if ( !contactMail.isNull() )
595 {
596 inspireCommonEmailAddressElem.appendChild( doc.createTextNode( contactMail ) );
597 }
598 inspireCommonMetadataPointOfContactElem.appendChild( inspireCommonEmailAddressElem );
599
600 inspireCapabilitiesElem.appendChild( inspireCommonMetadataPointOfContactElem );
601
602 QString inspireMetadataDate = QgsServerProjectUtils::wmsInspireMetadataDate( *project );
603 if ( !inspireMetadataDate.isNull() )
604 {
605 QDomElement inspireCommonMetadataDateElem = doc.createElement( QStringLiteral( "inspire_common:MetadataDate" ) );
606 inspireCommonMetadataDateElem.appendChild( doc.createTextNode( inspireMetadataDate ) );
607 inspireCapabilitiesElem.appendChild( inspireCommonMetadataDateElem );
608 }
609 }
610
611 // Supported languages
612 QDomElement inspireCommonSupportedLanguagesElem = doc.createElement( QStringLiteral( "inspire_common:SupportedLanguages" ) );
613 inspireCommonSupportedLanguagesElem.setAttribute( QStringLiteral( "xsi:type" ), QStringLiteral( "inspire_common:supportedLanguagesType" ) );
614
615 QDomElement inspireCommonLanguageElem = doc.createElement( QStringLiteral( "inspire_common:Language" ) );
616 inspireCommonLanguageElem.appendChild( doc.createTextNode( QgsServerProjectUtils::wmsInspireLanguage( *project ) ) );
617
618 QDomElement inspireCommonDefaultLanguageElem = doc.createElement( QStringLiteral( "inspire_common:DefaultLanguage" ) );
619 inspireCommonDefaultLanguageElem.appendChild( inspireCommonLanguageElem );
620 inspireCommonSupportedLanguagesElem.appendChild( inspireCommonDefaultLanguageElem );
621
622#if 0
623 /* Supported language has to be different from default one */
624 QDomElement inspireCommonSupportedLanguageElem = doc.createElement( "inspire_common:SupportedLanguage" );
625 inspireCommonSupportedLanguageElem.appendChild( inspireCommonLanguageElem.cloneNode().toElement() );
626 inspireCommonSupportedLanguagesElem.appendChild( inspireCommonSupportedLanguageElem );
627#endif
628
629 inspireCapabilitiesElem.appendChild( inspireCommonSupportedLanguagesElem );
630
631 QDomElement inspireCommonResponseLanguageElem = doc.createElement( QStringLiteral( "inspire_common:ResponseLanguage" ) );
632 inspireCommonResponseLanguageElem.appendChild( inspireCommonLanguageElem.cloneNode().toElement() );
633 inspireCapabilitiesElem.appendChild( inspireCommonResponseLanguageElem );
634
635 return inspireCapabilitiesElem;
636 }
637
638 QDomElement getComposerTemplatesElement( QDomDocument &doc, const QgsProject *project )
639 {
640 QList< QgsPrintLayout * > projectComposers = project->layoutManager()->printLayouts();
641 if ( projectComposers.size() == 0 )
642 return QDomElement();
643
644 QStringList restrictedComposers = QgsServerProjectUtils::wmsRestrictedComposers( *project );
645
646 QDomElement composerTemplatesElem = doc.createElement( QStringLiteral( "ComposerTemplates" ) );
647 QList<QgsPrintLayout *>::const_iterator cIt = projectComposers.constBegin();
648 for ( ; cIt != projectComposers.constEnd(); ++cIt )
649 {
650 QgsPrintLayout *layout = *cIt;
651 if ( restrictedComposers.contains( layout->name() ) )
652 continue;
653
654 // Check that we have at least one page
655 if ( layout->pageCollection()->pageCount() < 1 )
656 continue;
657
658 // Get width and height from first page of the collection
659 QgsLayoutSize layoutSize( layout->pageCollection()->page( 0 )->sizeWithUnits() );
660 QgsLayoutMeasurement width( layout->convertFromLayoutUnits( layoutSize.width(), Qgis::LayoutUnit::Millimeters ) );
661 QgsLayoutMeasurement height( layout->convertFromLayoutUnits( layoutSize.height(), Qgis::LayoutUnit::Millimeters ) );
662
663 QDomElement composerTemplateElem = doc.createElement( QStringLiteral( "ComposerTemplate" ) );
664 composerTemplateElem.setAttribute( QStringLiteral( "name" ), layout->name() );
665
666 //get paper width and height in mm from composition
667 composerTemplateElem.setAttribute( QStringLiteral( "width" ), width.length() );
668 composerTemplateElem.setAttribute( QStringLiteral( "height" ), height.length() );
669
670 //atlas enabled and atlas covering layer
671 QgsLayoutAtlas *atlas = layout->atlas();
672 if ( atlas && atlas->enabled() )
673 {
674 composerTemplateElem.setAttribute( QStringLiteral( "atlasEnabled" ), QStringLiteral( "1" ) );
675 QgsVectorLayer *cLayer = atlas->coverageLayer();
676 if ( cLayer )
677 {
678 QString layerName = cLayer->shortName();
680 {
681 layerName = cLayer->id();
682 }
683 else if ( layerName.isEmpty() )
684 {
685 layerName = cLayer->name();
686 }
687 composerTemplateElem.setAttribute( QStringLiteral( "atlasCoverageLayer" ), layerName );
688 }
689 }
690
691 //add available composer maps and their size in mm
692 QList<QgsLayoutItemMap *> layoutMapList;
693 layout->layoutItems<QgsLayoutItemMap>( layoutMapList );
694 QList<QgsLayoutItemMap *>::const_iterator cmIt = layoutMapList.constBegin();
695 // Add map id
696 int mapId = 0;
697 for ( ; cmIt != layoutMapList.constEnd(); ++cmIt )
698 {
699 const QgsLayoutItemMap *composerMap = *cmIt;
700
701 QDomElement composerMapElem = doc.createElement( QStringLiteral( "ComposerMap" ) );
702 composerMapElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "map%1" ).arg( mapId ) );
703 composerMapElem.setAttribute( QStringLiteral( "itemName" ), composerMap->displayName() );
704 mapId++;
705 composerMapElem.setAttribute( QStringLiteral( "width" ), composerMap->rect().width() );
706 composerMapElem.setAttribute( QStringLiteral( "height" ), composerMap->rect().height() );
707 composerTemplateElem.appendChild( composerMapElem );
708 }
709
710 //add available composer labels
711 QList<QgsLayoutItemLabel *> composerLabelList;
712 layout->layoutItems<QgsLayoutItemLabel>( composerLabelList );
713 QList<QgsLayoutItemLabel *>::const_iterator clIt = composerLabelList.constBegin();
714 for ( ; clIt != composerLabelList.constEnd(); ++clIt )
715 {
716 QgsLayoutItemLabel *composerLabel = *clIt;
717 QString id = composerLabel->id();
718 if ( id.isEmpty() )
719 continue;
720
721 QDomElement composerLabelElem = doc.createElement( QStringLiteral( "ComposerLabel" ) );
722 composerLabelElem.setAttribute( QStringLiteral( "name" ), id );
723 composerTemplateElem.appendChild( composerLabelElem );
724 }
725
726 //add available composer HTML
727 QList<QgsLayoutItemHtml *> composerHtmlList;
728 layout->layoutObjects<QgsLayoutItemHtml>( composerHtmlList );
729 QList<QgsLayoutItemHtml *>::const_iterator chIt = composerHtmlList.constBegin();
730 for ( ; chIt != composerHtmlList.constEnd(); ++chIt )
731 {
732 QgsLayoutItemHtml *composerHtml = *chIt;
733 if ( composerHtml->frameCount() == 0 )
734 continue;
735
736 QString id = composerHtml->frame( 0 )->id();
737 if ( id.isEmpty() )
738 continue;
739
740 QDomElement composerHtmlElem = doc.createElement( QStringLiteral( "ComposerHtml" ) );
741 composerHtmlElem.setAttribute( QStringLiteral( "name" ), id );
742 composerTemplateElem.appendChild( composerHtmlElem );
743 }
744
745 composerTemplatesElem.appendChild( composerTemplateElem );
746 }
747
748 if ( composerTemplatesElem.childNodes().size() == 0 )
749 return QDomElement();
750
751 return composerTemplatesElem;
752 }
753
754 QDomElement getWFSLayersElement( QDomDocument &doc, const QgsProject *project )
755 {
756 QStringList wfsLayerIds = QgsServerProjectUtils::wfsLayerIds( *project );
757 if ( wfsLayerIds.size() == 0 )
758 return QDomElement();
759
760 QDomElement wfsLayersElem = doc.createElement( QStringLiteral( "WFSLayers" ) );
761 for ( int i = 0; i < wfsLayerIds.size(); ++i )
762 {
763 QgsMapLayer *layer = project->mapLayer( wfsLayerIds.at( i ) );
764 if ( ! layer || layer->type() != Qgis::LayerType::Vector )
765 {
766 continue;
767 }
768
769 QDomElement wfsLayerElem = doc.createElement( QStringLiteral( "WFSLayer" ) );
771 {
772 wfsLayerElem.setAttribute( QStringLiteral( "name" ), layer->id() );
773 }
774 else
775 {
776 wfsLayerElem.setAttribute( QStringLiteral( "name" ), layer->name() );
777 }
778 wfsLayersElem.appendChild( wfsLayerElem );
779 }
780
781 return wfsLayersElem;
782 }
783
784 void handleLayersFromTreeGroup( QDomDocument &doc,
785 QDomElement &parentLayer,
786 QgsServerInterface *serverIface,
787 const QgsProject *project,
788 const QgsWmsRequest &request,
789 const QgsLayerTreeGroup *layerTreeGroup,
790 const QMap< QString, QgsWmsLayerInfos > &wmsLayerInfos,
791 bool projectSettings )
792 {
793 const auto layerIds = layerTreeGroup->findLayerIds();
794
795 parentLayer.setAttribute(
796 QStringLiteral( "queryable" ),
797 hasQueryableLayers( layerIds, wmsLayerInfos ) ? QStringLiteral( "1" ) : QStringLiteral( "0" )
798 );
799
800 const QgsRectangle wgs84BoundingRect = combineWgs84BoundingRect( layerIds, wmsLayerInfos );
801 QMap<QString, QgsRectangle> crsExtents = combineCrsExtents( layerIds, wmsLayerInfos );
802
803 appendCrsElementsToLayer( doc, parentLayer, crsExtents.keys(), QStringList() );
804 appendLayerWgs84BoundingRect( doc, parentLayer, wgs84BoundingRect );
805 appendLayerCrsExtents( doc, parentLayer, crsExtents );
806
807 appendLayersFromTreeGroup( doc, parentLayer, serverIface, project, request, layerTreeGroup, wmsLayerInfos, projectSettings );
808 }
809
810 QDomElement getLayersAndStylesCapabilitiesElement( QDomDocument &doc, QgsServerInterface *serverIface,
811 const QgsProject *project,
812 const QgsWmsRequest &request, bool projectSettings )
813 {
814 const QgsLayerTree *projectLayerTreeRoot = project->layerTreeRoot();
815
816 QDomElement layerParentElem = doc.createElement( QStringLiteral( "Layer" ) );
817
818 // Root Layer name
819 QString rootLayerName = QgsServerProjectUtils::wmsRootName( *project );
820 if ( rootLayerName.isEmpty() && !project->title().isEmpty() )
821 {
822 rootLayerName = project->title();
823 }
824
825 if ( !rootLayerName.isEmpty() )
826 {
827 QDomElement layerParentNameElem = doc.createElement( QStringLiteral( "Name" ) );
828 QDomText layerParentNameText = doc.createTextNode( rootLayerName );
829 layerParentNameElem.appendChild( layerParentNameText );
830 layerParentElem.appendChild( layerParentNameElem );
831 }
832
833 // Root Layer title
834 QDomElement layerParentTitleElem = doc.createElement( QStringLiteral( "Title" ) );
835 QDomText layerParentTitleText = doc.createTextNode( QgsServerProjectUtils::owsServiceTitle( *project ) );
836 layerParentTitleElem.appendChild( layerParentTitleText );
837 layerParentElem.appendChild( layerParentTitleElem );
838
839 // Root Layer abstract
840 const QString rootLayerAbstract = QgsServerProjectUtils::owsServiceAbstract( *project );
841 if ( !rootLayerAbstract.isEmpty() )
842 {
843 QDomElement layerParentAbstElem = doc.createElement( QStringLiteral( "Abstract" ) );
844 QDomText layerParentAbstText = doc.createCDATASection( rootLayerAbstract );
845 layerParentAbstElem.appendChild( layerParentAbstText );
846 layerParentElem.appendChild( layerParentAbstElem );
847 }
848
849 // Keyword list
850 addKeywordListElement( project, doc, layerParentElem );
851
852 // Root Layer tree name
853 if ( projectSettings )
854 {
855 QDomElement treeNameElem = doc.createElement( QStringLiteral( "TreeName" ) );
856 QDomText treeNameText = doc.createTextNode( project->title() );
857 treeNameElem.appendChild( treeNameText );
858 layerParentElem.appendChild( treeNameElem );
859 }
860
861 // Instantiate CRS's from the project's crs list
862 // This will prevent us to re-instantiate all the crs's each
863 // time we will need to rebuild a bounding box.
864 auto outputCrsList = QList<QgsCoordinateReferenceSystem>();
865 for ( const QString &crsDef : QgsServerProjectUtils::wmsOutputCrsList( *project ) )
866 {
868 if ( crs.isValid() )
869 {
870 outputCrsList.append( crs );
871 }
872 }
873
874 // Get WMS layer infos
875 const QMap< QString, QgsWmsLayerInfos > wmsLayerInfos = QgsWmsLayerInfos::buildWmsLayerInfos( serverIface, project, outputCrsList );
876
878
879 if ( !wmsExtent.isEmpty() )
880 {
882
883 // Get WMS WGS84 bounding rectangle
884 QgsRectangle wmsWgs84BoundingRect;
885 try
886 {
887 wmsWgs84BoundingRect = QgsWmsLayerInfos::transformExtent(
888 wmsExtent, project->crs(), wgs84, project->transformContext(), true
889 );
890 }
891 catch ( QgsCsException &cse )
892 {
894 QStringLiteral( "Error transforming extent: %1" ).arg( cse.what() ),
895 QStringLiteral( "Server" ),
896 Qgis::MessageLevel::Warning
897 );
898 }
899
900 // Get WMS extents in output CRSes
901 QMap< QString, QgsRectangle > wmsCrsExtents;
902 try
903 {
905 wmsExtent, project->crs(), outputCrsList, project->transformContext()
906 );
907 }
908 catch ( QgsCsException &cse )
909 {
910 QgsMessageLog::logMessage( QStringLiteral( "Error transforming extent: %1" ).arg( cse.what() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
911 }
912
913 layerParentElem.setAttribute(
914 QStringLiteral( "queryable" ),
915 hasQueryableLayers( projectLayerTreeRoot->findLayerIds(), wmsLayerInfos ) ? QStringLiteral( "1" ) : QStringLiteral( "0" )
916 );
917
918 appendCrsElementsToLayer( doc, layerParentElem, wmsCrsExtents.keys(), QStringList() );
919 appendLayerWgs84BoundingRect( doc, layerParentElem, wmsWgs84BoundingRect );
920 appendLayerCrsExtents( doc, layerParentElem, wmsCrsExtents );
921
922 appendLayersFromTreeGroup( doc, layerParentElem, serverIface, project, request, projectLayerTreeRoot, wmsLayerInfos, projectSettings );
923 }
924 else
925 {
926 handleLayersFromTreeGroup( doc, layerParentElem, serverIface, project, request, projectLayerTreeRoot, wmsLayerInfos, projectSettings );
927 }
928
929 return layerParentElem;
930 }
931
932 namespace
933 {
934
935 void appendLayersFromTreeGroup( QDomDocument &doc,
936 QDomElement &parentLayer,
937 QgsServerInterface *serverIface,
938 const QgsProject *project,
939 const QgsWmsRequest &request,
940 const QgsLayerTreeGroup *layerTreeGroup,
941 const QMap< QString, QgsWmsLayerInfos > &wmsLayerInfos,
942 bool projectSettings )
943 {
944 const QString version = request.wmsParameters().version();
945
946 bool siaFormat = QgsServerProjectUtils::wmsInfoFormatSia2045( *project );
947 const QStringList restrictedLayers = QgsServerProjectUtils::wmsRestrictedLayers( *project );
948
949 QList< QgsLayerTreeNode * > layerTreeGroupChildren = layerTreeGroup->children();
950 for ( int i = 0; i < layerTreeGroupChildren.size(); ++i )
951 {
952 QgsLayerTreeNode *treeNode = layerTreeGroupChildren.at( i );
953 QDomElement layerElem = doc.createElement( QStringLiteral( "Layer" ) );
954
955 if ( projectSettings )
956 {
957 layerElem.setAttribute( QStringLiteral( "visible" ), treeNode->isVisible() );
958 layerElem.setAttribute( QStringLiteral( "visibilityChecked" ), treeNode->itemVisibilityChecked() );
959 layerElem.setAttribute( QStringLiteral( "expanded" ), treeNode->isExpanded() );
960 }
961
962 if ( treeNode->nodeType() == QgsLayerTreeNode::NodeGroup )
963 {
964 QgsLayerTreeGroup *treeGroupChild = static_cast<QgsLayerTreeGroup *>( treeNode );
965
966 QString name = treeGroupChild->name();
967 if ( restrictedLayers.contains( name ) ) //unpublished group
968 {
969 continue;
970 }
971
972 if ( projectSettings )
973 {
974 layerElem.setAttribute( QStringLiteral( "mutuallyExclusive" ), treeGroupChild->isMutuallyExclusive() );
975 }
976
977 QString shortName = treeGroupChild->customProperty( QStringLiteral( "wmsShortName" ) ).toString();
978 QString title = treeGroupChild->customProperty( QStringLiteral( "wmsTitle" ) ).toString();
979
980 QDomElement nameElem = doc.createElement( QStringLiteral( "Name" ) );
981 QDomText nameText;
982 if ( !shortName.isEmpty() )
983 nameText = doc.createTextNode( shortName );
984 else
985 nameText = doc.createTextNode( name );
986 nameElem.appendChild( nameText );
987 layerElem.appendChild( nameElem );
988
989 QDomElement titleElem = doc.createElement( QStringLiteral( "Title" ) );
990 QDomText titleText;
991 if ( !title.isEmpty() )
992 titleText = doc.createTextNode( title );
993 else
994 titleText = doc.createTextNode( name );
995 titleElem.appendChild( titleText );
996 layerElem.appendChild( titleElem );
997
998 QString abstract = treeGroupChild->customProperty( QStringLiteral( "wmsAbstract" ) ).toString();
999 if ( !abstract.isEmpty() )
1000 {
1001 QDomElement abstractElem = doc.createElement( QStringLiteral( "Abstract" ) );
1002 QDomText abstractText = doc.createTextNode( abstract );
1003 abstractElem.appendChild( abstractText );
1004 layerElem.appendChild( abstractElem );
1005 }
1006
1007 // Layer tree name
1008 if ( projectSettings )
1009 {
1010 QDomElement treeNameElem = doc.createElement( QStringLiteral( "TreeName" ) );
1011 QDomText treeNameText = doc.createTextNode( name );
1012 treeNameElem.appendChild( treeNameText );
1013 layerElem.appendChild( treeNameElem );
1014 }
1015
1016 handleLayersFromTreeGroup( doc, layerElem, serverIface, project, request, treeGroupChild, wmsLayerInfos, projectSettings );
1017
1018 // Check if child layer elements have been added
1019 if ( layerElem.elementsByTagName( QStringLiteral( "Layer" ) ).length() == 0 )
1020 {
1021 continue;
1022 }
1023 }
1024 else
1025 {
1026 QgsLayerTreeLayer *treeLayer = static_cast<QgsLayerTreeLayer *>( treeNode );
1027 QgsMapLayer *l = treeLayer->layer();
1028 if ( !wmsLayerInfos.contains( treeLayer->layerId() ) ) //unpublished layer
1029 {
1030 continue;
1031 }
1032
1033 const QgsWmsLayerInfos &layerInfos = wmsLayerInfos[ treeLayer->layerId() ];
1034
1035 layerElem.setAttribute(
1036 QStringLiteral( "queryable" ),
1037 layerInfos.queryable ? QStringLiteral( "1" ) : QStringLiteral( "0" )
1038 );
1039
1040 QDomElement nameElem = doc.createElement( QStringLiteral( "Name" ) );
1041 QDomText nameText = doc.createTextNode( layerInfos.name );
1042 nameElem.appendChild( nameText );
1043 layerElem.appendChild( nameElem );
1044
1045 QDomElement titleElem = doc.createElement( QStringLiteral( "Title" ) );
1046 QDomText titleText = doc.createTextNode( layerInfos.title );
1047 titleElem.appendChild( titleText );
1048 layerElem.appendChild( titleElem );
1049
1050 if ( ! layerInfos.abstract.isEmpty() )
1051 {
1052 QDomElement abstractElem = doc.createElement( QStringLiteral( "Abstract" ) );
1053 QDomText abstractText = doc.createTextNode( layerInfos.abstract );
1054 abstractElem.appendChild( abstractText );
1055 layerElem.appendChild( abstractElem );
1056 }
1057
1058 //keyword list
1059 if ( ! layerInfos.keywords.isEmpty() )
1060 {
1061 QDomElement keywordListElem = doc.createElement( QStringLiteral( "KeywordList" ) );
1062 for ( const QString &keyword : std::as_const( layerInfos.keywords ) )
1063 {
1064 QDomElement keywordElem = doc.createElement( QStringLiteral( "Keyword" ) );
1065 QDomText keywordText = doc.createTextNode( keyword.trimmed() );
1066 keywordElem.appendChild( keywordText );
1067 if ( siaFormat )
1068 {
1069 keywordElem.setAttribute( QStringLiteral( "vocabulary" ), QStringLiteral( "SIA_Geo405" ) );
1070 }
1071 keywordListElem.appendChild( keywordElem );
1072 }
1073 layerElem.appendChild( keywordListElem );
1074 }
1075
1076 // Append not null Bounding rectangles
1077 if ( ! layerInfos.wgs84BoundingRect.isNull() )
1078 {
1079 appendCrsElementsToLayer( doc, layerElem, layerInfos.crsExtents.keys(), QStringList() );
1080
1081 appendLayerWgs84BoundingRect( doc, layerElem, layerInfos.wgs84BoundingRect );
1082
1083 appendLayerCrsExtents( doc, layerElem, layerInfos.crsExtents );
1084 }
1085
1086 // add details about supported styles of the layer
1087 appendLayerStyles( doc, layerElem, layerInfos, project, request, serverIface->serverSettings() );
1088
1089 //min/max scale denominatorScaleBasedVisibility
1090 if ( layerInfos.hasScaleBasedVisibility )
1091 {
1092 if ( version == QLatin1String( "1.1.1" ) )
1093 {
1094 double OGC_PX_M = 0.00028; // OGC reference pixel size in meter, also used by qgis
1095 double SCALE_TO_SCALEHINT = OGC_PX_M * M_SQRT2;
1096
1097 QDomElement scaleHintElem = doc.createElement( QStringLiteral( "ScaleHint" ) );
1098 scaleHintElem.setAttribute( QStringLiteral( "min" ), QString::number( layerInfos.maxScale * SCALE_TO_SCALEHINT ) );
1099 scaleHintElem.setAttribute( QStringLiteral( "max" ), QString::number( layerInfos.minScale * SCALE_TO_SCALEHINT ) );
1100 layerElem.appendChild( scaleHintElem );
1101 }
1102 else
1103 {
1104 QDomElement minScaleElem = doc.createElement( QStringLiteral( "MinScaleDenominator" ) );
1105 QDomText minScaleText = doc.createTextNode( QString::number( layerInfos.maxScale ) );
1106 minScaleElem.appendChild( minScaleText );
1107 layerElem.appendChild( minScaleElem );
1108
1109 QDomElement maxScaleElem = doc.createElement( QStringLiteral( "MaxScaleDenominator" ) );
1110 QDomText maxScaleText = doc.createTextNode( QString::number( layerInfos.minScale ) );
1111 maxScaleElem.appendChild( maxScaleText );
1112 layerElem.appendChild( maxScaleElem );
1113 }
1114 }
1115
1116 // layer data URL
1117 if ( !layerInfos.dataUrl.isEmpty() )
1118 {
1119 QDomElement dataUrlElem = doc.createElement( QStringLiteral( "DataURL" ) );
1120 QDomElement dataUrlFormatElem = doc.createElement( QStringLiteral( "Format" ) );
1121 QString dataUrlFormat = l->dataUrlFormat();
1122 QDomText dataUrlFormatText = doc.createTextNode( dataUrlFormat );
1123 dataUrlFormatElem.appendChild( dataUrlFormatText );
1124 dataUrlElem.appendChild( dataUrlFormatElem );
1125 QDomElement dataORElem = doc.createElement( QStringLiteral( "OnlineResource" ) );
1126 dataORElem.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
1127 dataORElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) );
1128 dataORElem.setAttribute( QStringLiteral( "xlink:href" ), layerInfos.dataUrl );
1129 dataUrlElem.appendChild( dataORElem );
1130 layerElem.appendChild( dataUrlElem );
1131 }
1132
1133 // layer attribution
1134 if ( ! layerInfos.attribution.isEmpty() )
1135 {
1136 QDomElement attribElem = doc.createElement( QStringLiteral( "Attribution" ) );
1137 QDomElement attribTitleElem = doc.createElement( QStringLiteral( "Title" ) );
1138 QDomText attribText = doc.createTextNode( layerInfos.attribution );
1139 attribTitleElem.appendChild( attribText );
1140 attribElem.appendChild( attribTitleElem );
1141 if ( ! layerInfos.attributionUrl.isEmpty() )
1142 {
1143 QDomElement attribORElem = doc.createElement( QStringLiteral( "OnlineResource" ) );
1144 attribORElem.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
1145 attribORElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) );
1146 attribORElem.setAttribute( QStringLiteral( "xlink:href" ), layerInfos.attributionUrl );
1147 attribElem.appendChild( attribORElem );
1148 }
1149 layerElem.appendChild( attribElem );
1150 }
1151
1152 // layer metadata URL
1153 for ( const QgsMapLayerServerProperties::MetadataUrl &metadataUrl : std::as_const( layerInfos.metadataUrls ) )
1154 {
1155 QDomElement metaUrlElem = doc.createElement( QStringLiteral( "MetadataURL" ) );
1156 const QString metadataUrlType = metadataUrl.type;
1157 if ( version == QLatin1String( "1.1.1" ) )
1158 {
1159 metaUrlElem.setAttribute( QStringLiteral( "type" ), metadataUrlType );
1160 }
1161 else if ( metadataUrlType == QLatin1String( "FGDC" ) )
1162 {
1163 metaUrlElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "FGDC:1998" ) );
1164 }
1165 else if ( metadataUrlType == QLatin1String( "TC211" ) )
1166 {
1167 metaUrlElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "ISO19115:2003" ) );
1168 }
1169 else
1170 {
1171 metaUrlElem.setAttribute( QStringLiteral( "type" ), metadataUrlType );
1172 }
1173 const QString metadataUrlFormat = metadataUrl.format;
1174 if ( !metadataUrlFormat.isEmpty() )
1175 {
1176 QDomElement metaUrlFormatElem = doc.createElement( QStringLiteral( "Format" ) );
1177 QDomText metaUrlFormatText = doc.createTextNode( metadataUrlFormat );
1178 metaUrlFormatElem.appendChild( metaUrlFormatText );
1179 metaUrlElem.appendChild( metaUrlFormatElem );
1180 }
1181 QDomElement metaUrlORElem = doc.createElement( QStringLiteral( "OnlineResource" ) );
1182 metaUrlORElem.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
1183 metaUrlORElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) );
1184 metaUrlORElem.setAttribute( QStringLiteral( "xlink:href" ), metadataUrl.url );
1185 metaUrlElem.appendChild( metaUrlORElem );
1186 layerElem.appendChild( metaUrlElem );
1187 }
1188
1189 bool timeDimensionAdded { false };
1190
1191 // Add dimensions
1192 if ( l->type() == Qgis::LayerType::Vector )
1193 {
1194 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( l );
1195 QgsMapLayerServerProperties *serverProperties = static_cast<QgsMapLayerServerProperties *>( vl->serverProperties() );
1196 const QList<QgsMapLayerServerProperties::WmsDimensionInfo> wmsDims = serverProperties->wmsDimensions();
1197 for ( const QgsMapLayerServerProperties::WmsDimensionInfo &dim : wmsDims )
1198 {
1199
1200 int fieldIndex = vl->fields().indexOf( dim.fieldName );
1201 // Check field index
1202 if ( fieldIndex == -1 )
1203 {
1204 continue;
1205 }
1206 // get unique values
1207 QSet<QVariant> uniqueValues = vl->uniqueValues( fieldIndex );
1208
1209 // get unique values from endfield name if define
1210 if ( !dim.endFieldName.isEmpty() )
1211 {
1212 int endFieldIndex = vl->fields().indexOf( dim.endFieldName );
1213 // Check end field index
1214 if ( endFieldIndex == -1 )
1215 {
1216 continue;
1217 }
1218 uniqueValues.unite( vl->uniqueValues( endFieldIndex ) );
1219 }
1220 // sort unique values
1221 QList<QVariant> values = qgis::setToList( uniqueValues );
1222 std::sort( values.begin(), values.end() );
1223
1224 QDomElement dimElem = doc.createElement( QStringLiteral( "Dimension" ) );
1225 dimElem.setAttribute( QStringLiteral( "name" ), dim.name );
1226
1227 if ( dim.name.toUpper() == QLatin1String( "TIME" ) )
1228 {
1229 timeDimensionAdded = true;
1230 }
1231
1232 if ( !dim.units.isEmpty() )
1233 {
1234 dimElem.setAttribute( QStringLiteral( "units" ), dim.units );
1235 }
1236 if ( !dim.unitSymbol.isEmpty() )
1237 {
1238 dimElem.setAttribute( QStringLiteral( "unitSymbol" ), dim.unitSymbol );
1239 }
1240 if ( !values.isEmpty() && dim.defaultDisplayType == QgsMapLayerServerProperties::WmsDimensionInfo::MinValue )
1241 {
1242 dimElem.setAttribute( QStringLiteral( "default" ), values.first().toString() );
1243 }
1244 else if ( !values.isEmpty() && dim.defaultDisplayType == QgsMapLayerServerProperties::WmsDimensionInfo::MaxValue )
1245 {
1246 dimElem.setAttribute( QStringLiteral( "default" ), values.last().toString() );
1247 }
1248 else if ( dim.defaultDisplayType == QgsMapLayerServerProperties::WmsDimensionInfo::ReferenceValue )
1249 {
1250 dimElem.setAttribute( QStringLiteral( "default" ), dim.referenceValue.toString() );
1251 }
1252 dimElem.setAttribute( QStringLiteral( "multipleValues" ), QStringLiteral( "1" ) );
1253 dimElem.setAttribute( QStringLiteral( "nearestValue" ), QStringLiteral( "0" ) );
1254 if ( projectSettings )
1255 {
1256 dimElem.setAttribute( QStringLiteral( "fieldName" ), dim.fieldName );
1257 dimElem.setAttribute( QStringLiteral( "endFieldName" ), dim.endFieldName );
1258 }
1259 // values list
1260 QStringList strValues;
1261 for ( const QVariant &v : values )
1262 {
1263 strValues << v.toString();
1264 }
1265 QDomText dimValuesText = doc.createTextNode( strValues.join( QLatin1String( ", " ) ) );
1266 dimElem.appendChild( dimValuesText );
1267 layerElem.appendChild( dimElem );
1268 }
1269 }
1270
1271 // Add WMS time dimension if not already added
1272 if ( ! timeDimensionAdded
1273 && l->temporalProperties()
1274 && l->temporalProperties()->isActive() )
1275 {
1276
1277 QDomElement dimElem = doc.createElement( QStringLiteral( "Dimension" ) );
1278 dimElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "TIME" ) );
1279 dimElem.setAttribute( QStringLiteral( "units" ), QStringLiteral( "ISO8601" ) );
1280
1281 // TODO: set "default" (reference value)
1282
1283 // Add all values
1284 const QList<QgsDateTimeRange> allRanges { l->temporalProperties()->allTemporalRanges( l ) };
1285
1286 // Apparently, for vectors allTemporalRanges is always empty :/
1287 // there is no way to know the type of range or the individual instants
1288
1289 bool isDateList { true };
1290 bool isInstantList { true };
1291
1292 QList<QDateTime> values;
1293 for ( const auto &r : std::as_const( allRanges ) )
1294 {
1295 if ( r.isInstant() )
1296 {
1297 if ( r.begin().time() != QTime( 0, 0, 0, 0 ) )
1298 {
1299 isDateList = false;
1300 }
1301 values.append( r.begin() );
1302 }
1303 else
1304 {
1305 isInstantList = false;
1306 break;
1307 }
1308 }
1309
1310 // Only list individual values for list of instants,
1311 // otherwise only the extent will be shown
1312 if ( isInstantList )
1313 {
1314 // values list
1315 QStringList strValues;
1316 for ( const auto &v : values )
1317 {
1318 if ( isDateList )
1319 {
1320 strValues << v.date().toString( Qt::DateFormat::ISODate );
1321 }
1322 else
1323 {
1324 strValues << v.toString( Qt::DateFormat::ISODate );
1325 }
1326 }
1327 QDomText dimValuesText = doc.createTextNode( strValues.join( QChar( ',' ) ) );
1328 dimElem.appendChild( dimValuesText );
1329 }
1330
1331 layerElem.appendChild( dimElem );
1332
1333 QDomElement timeExtentElem = doc.createElement( QStringLiteral( "Extent" ) );
1334 timeExtentElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "TIME" ) );
1335
1336 const QgsDateTimeRange timeExtent { l->temporalProperties()->calculateTemporalExtent( l ) };
1337 QString extent;
1338 if ( isDateList )
1339 {
1340 extent = QStringLiteral( "%1/%2" ).arg( timeExtent.begin().date().toString( Qt::DateFormat::ISODate ), timeExtent.end().date().toString( Qt::DateFormat::ISODate ) );
1341 }
1342 else
1343 {
1344 extent = QStringLiteral( "%1/%2" ).arg( timeExtent.begin().toString( Qt::DateFormat::ISODate ), timeExtent.end().toString( Qt::DateFormat::ISODate ) );
1345 }
1346 QDomText extentValueText = doc.createTextNode( extent );
1347 timeExtentElem.appendChild( extentValueText );
1348 layerElem.appendChild( timeExtentElem );
1349
1350 }
1351
1352 if ( projectSettings )
1353 {
1354 appendLayerProjectSettings( doc, layerElem, l );
1355 }
1356 }
1357
1358 parentLayer.appendChild( layerElem );
1359 }
1360 }
1361
1362 void appendLayerStyles( QDomDocument &doc, QDomElement &layerElem, const QgsWmsLayerInfos &layerInfos,
1363 const QgsProject *project, const QgsWmsRequest &request, const QgsServerSettings *settings )
1364 {
1365 // Get service URL
1366 QUrl href = serviceUrl( request, project, *settings );
1367
1368 //href needs to be a prefix
1369 QString hrefString = href.toString();
1370 hrefString.append( href.hasQuery() ? "&" : "?" );
1371 for ( const QString &styleName : std::as_const( layerInfos.styles ) )
1372 {
1373 QDomElement styleElem = doc.createElement( QStringLiteral( "Style" ) );
1374 QDomElement styleNameElem = doc.createElement( QStringLiteral( "Name" ) );
1375 QDomText styleNameText = doc.createTextNode( styleName );
1376 styleNameElem.appendChild( styleNameText );
1377 QDomElement styleTitleElem = doc.createElement( QStringLiteral( "Title" ) );
1378 QDomText styleTitleText = doc.createTextNode( styleName );
1379 styleTitleElem.appendChild( styleTitleText );
1380 styleElem.appendChild( styleNameElem );
1381 styleElem.appendChild( styleTitleElem );
1382
1383 // QString LegendURL for explicit layerbased GetLegendGraphic request
1384 QDomElement getLayerLegendGraphicElem = doc.createElement( QStringLiteral( "LegendURL" ) );
1385
1386 QString customHrefString = layerInfos.legendUrl;
1387
1388 QStringList getLayerLegendGraphicFormats;
1389 if ( !customHrefString.isEmpty() )
1390 {
1391 getLayerLegendGraphicFormats << layerInfos.legendUrlFormat;
1392 }
1393 else
1394 {
1395 getLayerLegendGraphicFormats << QStringLiteral( "image/png" ); // << "jpeg" << "image/jpeg"
1396 }
1397
1398 for ( const QString &getLayerLegendGraphicFormat : std::as_const( getLayerLegendGraphicFormats ) )
1399 {
1400 QDomElement getLayerLegendGraphicFormatElem = doc.createElement( QStringLiteral( "Format" ) );
1401 QDomText getLayerLegendGraphicFormatText = doc.createTextNode( getLayerLegendGraphicFormat );
1402 getLayerLegendGraphicFormatElem.appendChild( getLayerLegendGraphicFormatText );
1403 getLayerLegendGraphicElem.appendChild( getLayerLegendGraphicFormatElem );
1404 }
1405
1406 // no parameters on custom hrefUrl, because should link directly to graphic
1407 if ( customHrefString.isEmpty() )
1408 {
1409 QUrl mapUrl( hrefString );
1410 QUrlQuery mapUrlQuery( mapUrl.query() );
1411 mapUrlQuery.addQueryItem( QStringLiteral( "SERVICE" ), QStringLiteral( "WMS" ) );
1412 mapUrlQuery.addQueryItem( QStringLiteral( "VERSION" ), request.wmsParameters().version() );
1413 mapUrlQuery.addQueryItem( QStringLiteral( "REQUEST" ), QStringLiteral( "GetLegendGraphic" ) );
1414 mapUrlQuery.addQueryItem( QStringLiteral( "LAYER" ), layerInfos.name );
1415 mapUrlQuery.addQueryItem( QStringLiteral( "FORMAT" ), QStringLiteral( "image/png" ) );
1416 mapUrlQuery.addQueryItem( QStringLiteral( "STYLE" ), styleName );
1417 if ( request.wmsParameters().version() == QLatin1String( "1.3.0" ) )
1418 {
1419 mapUrlQuery.addQueryItem( QStringLiteral( "SLD_VERSION" ), QStringLiteral( "1.1.0" ) );
1420 }
1421 mapUrl.setQuery( mapUrlQuery );
1422 customHrefString = mapUrl.toString();
1423 }
1424
1425 QDomElement getLayerLegendGraphicORElem = doc.createElement( QStringLiteral( "OnlineResource" ) );
1426 getLayerLegendGraphicORElem.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
1427 getLayerLegendGraphicORElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) );
1428 getLayerLegendGraphicORElem.setAttribute( QStringLiteral( "xlink:href" ), customHrefString );
1429 getLayerLegendGraphicElem.appendChild( getLayerLegendGraphicORElem );
1430 styleElem.appendChild( getLayerLegendGraphicElem );
1431
1432 layerElem.appendChild( styleElem );
1433 }
1434 }
1435
1436 void appendCrsElementsToLayer( QDomDocument &doc, QDomElement &layerElement,
1437 const QStringList &crsList, const QStringList &constrainedCrsList )
1438 {
1439 if ( layerElement.isNull() )
1440 {
1441 return;
1442 }
1443
1444 const QString version = doc.documentElement().attribute( QStringLiteral( "version" ) );
1445
1446 //insert the CRS elements after the title element to be in accordance with the WMS 1.3 specification
1447 QDomElement titleElement = layerElement.firstChildElement( QStringLiteral( "Title" ) );
1448 QDomElement abstractElement = layerElement.firstChildElement( QStringLiteral( "Abstract" ) );
1449 QDomElement keywordListElement = layerElement.firstChildElement( QStringLiteral( "KeywordList" ) );
1450 QDomElement CRSPrecedingElement = !keywordListElement.isNull() ? keywordListElement : !abstractElement.isNull() ? abstractElement : titleElement;
1451
1452 if ( CRSPrecedingElement.isNull() )
1453 {
1454 // keyword list element is never empty
1455 const QDomElement keyElement = layerElement.firstChildElement( QStringLiteral( "KeywordList" ) );
1456 CRSPrecedingElement = keyElement;
1457 }
1458
1459 //In case the number of advertised CRS is constrained
1460 if ( !constrainedCrsList.isEmpty() )
1461 {
1462 for ( int i = constrainedCrsList.size() - 1; i >= 0; --i )
1463 {
1464 appendCrsElementToLayer( doc, layerElement, CRSPrecedingElement, constrainedCrsList.at( i ) );
1465 }
1466 }
1467 else //no crs constraint
1468 {
1469 for ( const QString &crs : crsList )
1470 {
1471 appendCrsElementToLayer( doc, layerElement, CRSPrecedingElement, crs );
1472 }
1473 }
1474
1475 // Support for CRS:84 is mandatory (equals EPSG:4326 with reversed axis)
1476 // https://github.com/opengeospatial/ets-wms13/blob/47155399c09b200cb21382874fdb21d5fae4ab6e/src/site/markdown/index.md
1477 if ( version == QLatin1String( "1.3.0" ) )
1478 {
1479 appendCrsElementToLayer( doc, layerElement, CRSPrecedingElement, QString( "CRS:84" ) );
1480 }
1481 }
1482
1483 void appendCrsElementToLayer( QDomDocument &doc, QDomElement &layerElement, const QDomElement &precedingElement,
1484 const QString &crsText )
1485 {
1486 if ( crsText.isEmpty() )
1487 return;
1488 const QString version = doc.documentElement().attribute( QStringLiteral( "version" ) );
1489 QDomElement crsElement = doc.createElement( version == QLatin1String( "1.1.1" ) ? "SRS" : "CRS" );
1490 QDomText crsTextNode = doc.createTextNode( crsText );
1491 crsElement.appendChild( crsTextNode );
1492 layerElement.insertAfter( crsElement, precedingElement );
1493 }
1494
1495 void appendLayerWgs84BoundingRect( QDomDocument &doc, QDomElement &layerElem, const QgsRectangle &wgs84BoundingRect )
1496 {
1497 //LatLonBoundingBox / Ex_GeographicBounding box is optional
1498 if ( wgs84BoundingRect.isNull() )
1499 {
1500 return;
1501 }
1502
1503 //Ex_GeographicBoundingBox
1504 QDomElement ExGeoBBoxElement;
1505 const int wgs84precision = 6;
1506 const QString version = doc.documentElement().attribute( QStringLiteral( "version" ) );
1507 if ( version == QLatin1String( "1.1.1" ) ) // WMS Version 1.1.1
1508 {
1509 ExGeoBBoxElement = doc.createElement( QStringLiteral( "LatLonBoundingBox" ) );
1510 ExGeoBBoxElement.setAttribute( QStringLiteral( "minx" ), qgsDoubleToString( QgsServerProjectUtils::floorWithPrecision( wgs84BoundingRect.xMinimum(), wgs84precision ), wgs84precision ) );
1511 ExGeoBBoxElement.setAttribute( QStringLiteral( "miny" ), qgsDoubleToString( QgsServerProjectUtils::floorWithPrecision( wgs84BoundingRect.yMinimum(), wgs84precision ), wgs84precision ) );
1512 ExGeoBBoxElement.setAttribute( QStringLiteral( "maxx" ), qgsDoubleToString( QgsServerProjectUtils::ceilWithPrecision( wgs84BoundingRect.xMaximum(), wgs84precision ), wgs84precision ) );
1513 ExGeoBBoxElement.setAttribute( QStringLiteral( "maxy" ), qgsDoubleToString( QgsServerProjectUtils::ceilWithPrecision( wgs84BoundingRect.yMaximum(), wgs84precision ), wgs84precision ) );
1514 }
1515 else // WMS Version 1.3.0
1516 {
1517 ExGeoBBoxElement = doc.createElement( QStringLiteral( "EX_GeographicBoundingBox" ) );
1518 QDomElement wBoundLongitudeElement = doc.createElement( QStringLiteral( "westBoundLongitude" ) );
1519 QDomText wBoundLongitudeText = doc.createTextNode( qgsDoubleToString( QgsServerProjectUtils::floorWithPrecision( wgs84BoundingRect.xMinimum(), wgs84precision ), wgs84precision ) );
1520 wBoundLongitudeElement.appendChild( wBoundLongitudeText );
1521 ExGeoBBoxElement.appendChild( wBoundLongitudeElement );
1522 QDomElement eBoundLongitudeElement = doc.createElement( QStringLiteral( "eastBoundLongitude" ) );
1523 QDomText eBoundLongitudeText = doc.createTextNode( qgsDoubleToString( QgsServerProjectUtils::ceilWithPrecision( wgs84BoundingRect.xMaximum(), wgs84precision ), wgs84precision ) );
1524 eBoundLongitudeElement.appendChild( eBoundLongitudeText );
1525 ExGeoBBoxElement.appendChild( eBoundLongitudeElement );
1526 QDomElement sBoundLatitudeElement = doc.createElement( QStringLiteral( "southBoundLatitude" ) );
1527 QDomText sBoundLatitudeText = doc.createTextNode( qgsDoubleToString( QgsServerProjectUtils::floorWithPrecision( wgs84BoundingRect.yMinimum(), wgs84precision ), wgs84precision ) );
1528 sBoundLatitudeElement.appendChild( sBoundLatitudeText );
1529 ExGeoBBoxElement.appendChild( sBoundLatitudeElement );
1530 QDomElement nBoundLatitudeElement = doc.createElement( QStringLiteral( "northBoundLatitude" ) );
1531 QDomText nBoundLatitudeText = doc.createTextNode( qgsDoubleToString( QgsServerProjectUtils::ceilWithPrecision( wgs84BoundingRect.yMaximum(), wgs84precision ), wgs84precision ) );
1532 nBoundLatitudeElement.appendChild( nBoundLatitudeText );
1533 ExGeoBBoxElement.appendChild( nBoundLatitudeElement );
1534 }
1535
1536 const QDomElement lastCRSElem = layerElem.lastChildElement( version == QLatin1String( "1.1.1" ) ? "SRS" : "CRS" );
1537 if ( !lastCRSElem.isNull() )
1538 {
1539 layerElem.insertAfter( ExGeoBBoxElement, lastCRSElem );
1540 }
1541 else
1542 {
1543 layerElem.appendChild( ExGeoBBoxElement );
1544 }
1545 }
1546
1547 void appendLayerCrsExtents( QDomDocument &doc, QDomElement &layerElem, const QMap<QString, QgsRectangle> &crsExtents )
1548 {
1549 const QString version = doc.documentElement().attribute( QStringLiteral( "version" ) );
1550
1551 const auto &keys = crsExtents.keys();
1552 for ( const QString &crsText : std::as_const( keys ) )
1553 {
1555 QgsRectangle crsExtent( crsExtents[ crsText ] );
1556
1557 if ( crsExtent.isNull() )
1558 {
1559 continue;
1560 }
1561
1562 int precision = 3;
1563 if ( crs.isGeographic() )
1564 {
1565 precision = 6;
1566 }
1567
1568 //BoundingBox element
1569 QDomElement bBoxElement = doc.createElement( QStringLiteral( "BoundingBox" ) );
1570 if ( crs.isValid() )
1571 {
1572 bBoxElement.setAttribute( version == QLatin1String( "1.1.1" ) ? "SRS" : "CRS", crs.authid() );
1573 }
1574
1575 if ( version != QLatin1String( "1.1.1" ) && crs.hasAxisInverted() )
1576 {
1577 crsExtent.invert();
1578 }
1579
1580 bBoxElement.setAttribute( QStringLiteral( "minx" ), qgsDoubleToString( QgsServerProjectUtils::floorWithPrecision( crsExtent.xMinimum(), precision ), precision ) );
1581 bBoxElement.setAttribute( QStringLiteral( "miny" ), qgsDoubleToString( QgsServerProjectUtils::floorWithPrecision( crsExtent.yMinimum(), precision ), precision ) );
1582 bBoxElement.setAttribute( QStringLiteral( "maxx" ), qgsDoubleToString( QgsServerProjectUtils::ceilWithPrecision( crsExtent.xMaximum(), precision ), precision ) );
1583 bBoxElement.setAttribute( QStringLiteral( "maxy" ), qgsDoubleToString( QgsServerProjectUtils::ceilWithPrecision( crsExtent.yMaximum(), precision ), precision ) );
1584
1585 QDomElement lastBBoxElem = layerElem.lastChildElement( QStringLiteral( "BoundingBox" ) );
1586 if ( !lastBBoxElem.isNull() )
1587 {
1588 layerElem.insertAfter( bBoxElement, lastBBoxElem );
1589 }
1590 else
1591 {
1592 lastBBoxElem = layerElem.lastChildElement( version == QLatin1String( "1.1.1" ) ? "LatLonBoundingBox" : "EX_GeographicBoundingBox" );
1593 if ( !lastBBoxElem.isNull() )
1594 {
1595 layerElem.insertAfter( bBoxElement, lastBBoxElem );
1596 }
1597 else
1598 {
1599 layerElem.appendChild( bBoxElement );
1600 }
1601 }
1602 }
1603 }
1604
1605 void appendDrawingOrder( QDomDocument &doc, QDomElement &parentElem, QgsServerInterface *serverIface,
1606 const QgsProject *project )
1607 {
1608#ifdef HAVE_SERVER_PYTHON_PLUGINS
1609 QgsAccessControl *accessControl = serverIface->accessControls();
1610#else
1611 ( void )serverIface;
1612#endif
1613 bool useLayerIds = QgsServerProjectUtils::wmsUseLayerIds( *project );
1614 QStringList restrictedLayers = QgsServerProjectUtils::wmsRestrictedLayers( *project );
1615
1616 QStringList layerList;
1617
1618 const QgsLayerTree *projectLayerTreeRoot = project->layerTreeRoot();
1619 QList< QgsMapLayer * > projectLayerOrder = projectLayerTreeRoot->layerOrder();
1620 for ( int i = 0; i < projectLayerOrder.size(); ++i )
1621 {
1622 QgsMapLayer *l = projectLayerOrder.at( i );
1623
1624 if ( restrictedLayers.contains( l->name() ) ) //unpublished layer
1625 {
1626 continue;
1627 }
1628#ifdef HAVE_SERVER_PYTHON_PLUGINS
1629 if ( accessControl && !accessControl->layerReadPermission( l ) )
1630 {
1631 continue;
1632 }
1633#endif
1634 QString wmsName = l->name();
1635 if ( useLayerIds )
1636 {
1637 wmsName = l->id();
1638 }
1639 else if ( !l->shortName().isEmpty() )
1640 {
1641 wmsName = l->shortName();
1642 }
1643
1644 layerList << wmsName;
1645 }
1646
1647 if ( !layerList.isEmpty() )
1648 {
1649 QStringList reversedList;
1650 reversedList.reserve( layerList.size() );
1651 for ( int i = layerList.size() - 1; i >= 0; --i )
1652 reversedList << layerList[ i ];
1653
1654 QDomElement layerDrawingOrderElem = doc.createElement( QStringLiteral( "LayerDrawingOrder" ) );
1655 QDomText drawingOrderText = doc.createTextNode( reversedList.join( ',' ) );
1656 layerDrawingOrderElem.appendChild( drawingOrderText );
1657 parentElem.appendChild( layerDrawingOrderElem );
1658 }
1659 }
1660
1661 void appendLayerProjectSettings( QDomDocument &doc, QDomElement &layerElem, QgsMapLayer *currentLayer )
1662 {
1663 if ( !currentLayer )
1664 {
1665 return;
1666 }
1667
1668 // Layer tree name
1669 QDomElement treeNameElem = doc.createElement( QStringLiteral( "TreeName" ) );
1670 QDomText treeNameText = doc.createTextNode( currentLayer->name() );
1671 treeNameElem.appendChild( treeNameText );
1672 layerElem.appendChild( treeNameElem );
1673
1674 switch ( currentLayer->type() )
1675 {
1676 case Qgis::LayerType::Vector:
1677 {
1678 QgsVectorLayer *vLayer = static_cast<QgsVectorLayer *>( currentLayer );
1679
1680 int displayFieldIdx = -1;
1681 QString displayField = QStringLiteral( "maptip" );
1682 QgsExpression exp( vLayer->displayExpression() );
1683 if ( exp.isField() )
1684 {
1685 displayField = static_cast<const QgsExpressionNodeColumnRef *>( exp.rootNode() )->name();
1686 displayFieldIdx = vLayer->fields().lookupField( displayField );
1687 }
1688
1689 //attributes
1690 QDomElement attributesElem = doc.createElement( QStringLiteral( "Attributes" ) );
1691 const QgsFields layerFields = vLayer->fields();
1692 for ( int idx = 0; idx < layerFields.count(); ++idx )
1693 {
1694 QgsField field = layerFields.at( idx );
1696 {
1697 continue;
1698 }
1699 // field alias in case of displayField
1700 if ( idx == displayFieldIdx )
1701 {
1702 displayField = vLayer->attributeDisplayName( idx );
1703 }
1704 QDomElement attributeElem = doc.createElement( QStringLiteral( "Attribute" ) );
1705 attributeElem.setAttribute( QStringLiteral( "name" ), field.name() );
1706 attributeElem.setAttribute( QStringLiteral( "type" ), QVariant::typeToName( field.type() ) );
1707 attributeElem.setAttribute( QStringLiteral( "typeName" ), field.typeName() );
1708 QString alias = field.alias();
1709 if ( !alias.isEmpty() )
1710 {
1711 attributeElem.setAttribute( QStringLiteral( "alias" ), alias );
1712 }
1713
1714 //edit type to text
1715 attributeElem.setAttribute( QStringLiteral( "editType" ), vLayer->editorWidgetSetup( idx ).type() );
1716 attributeElem.setAttribute( QStringLiteral( "comment" ), field.comment() );
1717 attributeElem.setAttribute( QStringLiteral( "length" ), field.length() );
1718 attributeElem.setAttribute( QStringLiteral( "precision" ), field.precision() );
1719 attributesElem.appendChild( attributeElem );
1720 }
1721
1722 //displayfield
1723 layerElem.setAttribute( QStringLiteral( "displayField" ), displayField );
1724
1725 //primary key
1726 QgsAttributeList pkAttributes = vLayer->primaryKeyAttributes();
1727 if ( pkAttributes.size() > 0 )
1728 {
1729 QDomElement pkElem = doc.createElement( QStringLiteral( "PrimaryKey" ) );
1730 QgsAttributeList::const_iterator pkIt = pkAttributes.constBegin();
1731 for ( ; pkIt != pkAttributes.constEnd(); ++pkIt )
1732 {
1733 QDomElement pkAttributeElem = doc.createElement( QStringLiteral( "PrimaryKeyAttribute" ) );
1734 QDomText pkAttName = doc.createTextNode( layerFields.at( *pkIt ).name() );
1735 pkAttributeElem.appendChild( pkAttName );
1736 pkElem.appendChild( pkAttributeElem );
1737 }
1738 layerElem.appendChild( pkElem );
1739 }
1740
1741 //geometry type
1742 layerElem.setAttribute( QStringLiteral( "geometryType" ), QgsWkbTypes::displayString( vLayer->wkbType() ) );
1743
1744 //opacity
1745 layerElem.setAttribute( QStringLiteral( "opacity" ), QString::number( vLayer->opacity() ) );
1746
1747 layerElem.appendChild( attributesElem );
1748 break;
1749 }
1750
1751 case Qgis::LayerType::Raster:
1752 {
1753 const QgsDataProvider *provider = currentLayer->dataProvider();
1754 if ( provider && provider->name() == "wms" )
1755 {
1756 //advertise as web map background layer
1757 QVariant wmsBackgroundLayer = currentLayer->customProperty( QStringLiteral( "WMSBackgroundLayer" ), false );
1758 QDomElement wmsBackgroundLayerElem = doc.createElement( "WMSBackgroundLayer" );
1759 QDomText wmsBackgroundLayerText = doc.createTextNode( wmsBackgroundLayer.toBool() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
1760 wmsBackgroundLayerElem.appendChild( wmsBackgroundLayerText );
1761 layerElem.appendChild( wmsBackgroundLayerElem );
1762
1763 //publish datasource
1764 QVariant wmsPublishDataSourceUrl = currentLayer->customProperty( QStringLiteral( "WMSPublishDataSourceUrl" ), false );
1765 if ( wmsPublishDataSourceUrl.toBool() )
1766 {
1767 bool tiled = qobject_cast< const QgsRasterDataProvider * >( provider )
1768 ? !qobject_cast< const QgsRasterDataProvider * >( provider )->nativeResolutions().isEmpty()
1769 : false;
1770
1771 QDomElement dataSourceElem = doc.createElement( tiled ? QStringLiteral( "WMTSDataSource" ) : QStringLiteral( "WMSDataSource" ) );
1772 QDomText dataSourceUri = doc.createTextNode( provider->dataSourceUri() );
1773 dataSourceElem.appendChild( dataSourceUri );
1774 layerElem.appendChild( dataSourceElem );
1775 }
1776 }
1777
1778 QVariant wmsPrintLayer = currentLayer->customProperty( QStringLiteral( "WMSPrintLayer" ) );
1779 if ( wmsPrintLayer.isValid() )
1780 {
1781 QDomElement wmsPrintLayerElem = doc.createElement( "WMSPrintLayer" );
1782 QDomText wmsPrintLayerText = doc.createTextNode( wmsPrintLayer.toString() );
1783 wmsPrintLayerElem.appendChild( wmsPrintLayerText );
1784 layerElem.appendChild( wmsPrintLayerElem );
1785 }
1786
1787 //opacity
1788 QgsRasterLayer *rl = static_cast<QgsRasterLayer *>( currentLayer );
1789 QgsRasterRenderer *rasterRenderer = rl->renderer();
1790 if ( rasterRenderer )
1791 {
1792 layerElem.setAttribute( QStringLiteral( "opacity" ), QString::number( rasterRenderer->opacity() ) );
1793 }
1794 break;
1795 }
1796
1797 case Qgis::LayerType::Mesh:
1798 case Qgis::LayerType::VectorTile:
1799 case Qgis::LayerType::Plugin:
1800 case Qgis::LayerType::Annotation:
1801 case Qgis::LayerType::PointCloud:
1802 case Qgis::LayerType::Group:
1803 break;
1804 }
1805 }
1806
1807 void addKeywordListElement( const QgsProject *project, QDomDocument &doc, QDomElement &parent )
1808 {
1809 bool sia2045 = QgsServerProjectUtils::wmsInfoFormatSia2045( *project );
1810
1811 QDomElement keywordsElem = doc.createElement( QStringLiteral( "KeywordList" ) );
1812 //add default keyword
1813 QDomElement keywordElem = doc.createElement( QStringLiteral( "Keyword" ) );
1814 keywordElem.setAttribute( QStringLiteral( "vocabulary" ), QStringLiteral( "ISO" ) );
1815 QDomText keywordText = doc.createTextNode( QStringLiteral( "infoMapAccessService" ) );
1816 keywordElem.appendChild( keywordText );
1817 keywordsElem.appendChild( keywordElem );
1818 parent.appendChild( keywordsElem );
1819 QStringList keywords = QgsServerProjectUtils::owsServiceKeywords( *project );
1820 for ( const QString &keyword : std::as_const( keywords ) )
1821 {
1822 if ( !keyword.isEmpty() )
1823 {
1824 keywordElem = doc.createElement( QStringLiteral( "Keyword" ) );
1825 keywordText = doc.createTextNode( keyword );
1826 keywordElem.appendChild( keywordText );
1827 if ( sia2045 )
1828 {
1829 keywordElem.setAttribute( QStringLiteral( "vocabulary" ), QStringLiteral( "SIA_Geo405" ) );
1830 }
1831 keywordsElem.appendChild( keywordElem );
1832 }
1833 }
1834 parent.appendChild( keywordsElem );
1835 }
1836 }
1837
1838 bool hasQueryableLayers( const QStringList &layerIds, const QMap< QString, QgsWmsLayerInfos > &wmsLayerInfos )
1839 {
1840 for ( const QString &id : std::as_const( layerIds ) )
1841 {
1842 if ( !wmsLayerInfos.contains( id ) )
1843 {
1844 continue;
1845 }
1846 if ( wmsLayerInfos[id].queryable )
1847 {
1848 return true;
1849 }
1850 }
1851 return false;
1852 }
1853
1854 QgsRectangle combineWgs84BoundingRect( const QStringList &layerIds, const QMap< QString, QgsWmsLayerInfos > &wmsLayerInfos )
1855 {
1856 QgsRectangle combined;
1857 bool empty = true;
1858
1859 for ( const QString &id : std::as_const( layerIds ) )
1860 {
1861 if ( !wmsLayerInfos.contains( id ) )
1862 {
1863 continue;
1864 }
1865
1866 QgsRectangle rect = wmsLayerInfos[ id ].wgs84BoundingRect;
1867 if ( rect.isNull() )
1868 {
1869 continue;
1870 }
1871
1872 if ( rect.isEmpty() )
1873 {
1874 continue;
1875 }
1876
1877 if ( empty )
1878 {
1879 combined = rect;
1880 empty = false;
1881 }
1882 else
1883 {
1884 combined.combineExtentWith( rect );
1885 }
1886 }
1887
1888 return combined;
1889 }
1890
1891 QMap<QString, QgsRectangle> combineCrsExtents( const QStringList &layerIds, const QMap< QString, QgsWmsLayerInfos > &wmsLayerInfos )
1892 {
1893 QMap<QString, QgsRectangle> combined;
1894
1895 for ( const QString &id : std::as_const( layerIds ) )
1896 {
1897 if ( !wmsLayerInfos.contains( id ) )
1898 {
1899 continue;
1900 }
1901
1902 const QgsWmsLayerInfos &layerInfos = wmsLayerInfos[ id ];
1903 const auto keys = layerInfos.crsExtents.keys();
1904 for ( const QString &crs : std::as_const( keys ) )
1905 {
1906 const QgsRectangle rect = layerInfos.crsExtents[ crs ];
1907 if ( rect.isNull() )
1908 {
1909 continue;
1910 }
1911
1912 if ( rect.isEmpty() )
1913 {
1914 continue;
1915 }
1916
1917 if ( !combined.contains( crs ) )
1918 {
1919 combined[ crs ] = rect;
1920 }
1921 else
1922 {
1923 combined[ crs ].combineExtentWith( rect );
1924 }
1925 }
1926 }
1927
1928 return combined;
1929 }
1930
1931} // namespace QgsWms
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.
Definition: qgsexception.h:67
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
Definition: qgsexception.h:49
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
QString typeName() const
Gets the field type.
Definition: qgsfield.cpp:150
QString name
Definition: qgsfield.h:62
int precision
Definition: qgsfield.h:59
int length
Definition: qgsfield.h:58
ConfigurationFlags configurationFlags
Definition: qgsfield.h:66
QVariant::Type type
Definition: qgsfield.h:60
QString alias
Definition: qgsfield.h:63
QString comment
Definition: qgsfield.h:61
@ HideFromWms
Field is not available if layer is served as WMS from QGIS server.
Container of fields for a vector layer.
Definition: qgsfields.h:45
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:207
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:359
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.
Definition: qgslayertree.h:33
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...
Definition: qgslayoutsize.h:41
double height() const
Returns the height of the size.
Definition: qgslayoutsize.h:90
double width() const
Returns the width of the size.
Definition: qgslayoutsize.h:76
QgsLayoutPageCollection * pageCollection()
Returns a pointer to the layout's page collection, which stores and manages page items in the layout.
Definition: qgslayout.cpp:467
void layoutItems(QList< T * > &itemList) const
Returns a list of layout items of a specific type.
Definition: qgslayout.h:121
void layoutObjects(QList< T * > &objectList) const
Returns a list of layout objects (items and multiframes) of a specific type.
Definition: qgslayout.h:140
QgsLayoutMeasurement convertFromLayoutUnits(double length, Qgis::LayoutUnit unit) const
Converts a length measurement from the layout's native units to a specified target unit.
Definition: qgslayout.cpp:352
Manages QGIS Server properties for a map 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:73
QString name
Definition: qgsmaplayer.h:76
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.
Definition: qgsmaplayer.h:421
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Qgis::LayerType type
Definition: qgsmaplayer.h:80
QString dataUrlFormat() const
Returns the DataUrl format of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:377
QString shortName() const
Returns the short name of the layer used by QGIS Server to identify the layer.
virtual QgsMapLayerTemporalProperties * temporalProperties()
Returns the layer's temporal properties.
Definition: qgsmaplayer.h:1526
double opacity
Definition: qgsmaplayer.h:82
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.
Definition: qgsproject.cpp:509
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.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:479
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Definition: qgsrectangle.h:391
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:469
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.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Q_INVOKABLE Qgis::WkbType wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
QString displayExpression
QgsEditorWidgetSetup editorWidgetSetup(int index) const
The editor widget setup defines which QgsFieldFormatter and editor widget will be used for the field ...
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) SIP_HOLDGIL
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 styles
WMS layer styles.
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.
QList< QgsMapLayerServerProperties::MetadataUrl > metadataUrls
WMS layer metadata URLs.
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.
Definition: qgswmsrequest.h:35
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 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.
Definition: qgswmsutils.cpp:38
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:3927
CONSTLATIN1STRING geoEpsgCrsAuthId()
Geographic coord sys from EPSG authority.
Definition: qgis.h:4484
QList< int > QgsAttributeList
Definition: qgsfield.h:27
const QgsField & field
Definition: qgsfield.h:554
const QgsCoordinateReferenceSystem & crs
int precision
const double OGC_PX_M
Setting to define QGIS Server WMS Dimension.
@ MaxValue
Modify current selection to include only select features which match.