QGIS API Documentation 4.1.0-Master (9af12b5a203)
Loading...
Searching...
No Matches
qgswmsrendercontext.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgswmsrendercontext.cpp
3 ---------------------
4 begin : March 22, 2019
5 copyright : (C) 2019 by Paul Blottiere
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgswmsrendercontext.h"
19
20#include "qgslayertree.h"
21#include "qgsrasterlayer.h"
24#include "qgswmsutils.h"
25
26#include <QString>
27
28using namespace Qt::StringLiterals;
29
30using namespace QgsWms;
31
32const double OGC_PX_M = 0.00028; // OGC reference pixel size in meter
34 : mProject( project )
35 , mInterface( interface )
36 , mFlags()
37{}
38
40{
41 qDeleteAll( mExternalLayers );
42 mExternalLayers.clear();
43}
44
46{
47 mParameters = parameters;
48
49 initRestrictedLayers();
50 initNicknameLayers();
51 searchLayersToRender();
52 removeUnwantedLayers();
53
54 std::reverse( mLayersToRender.begin(), mLayersToRender.end() );
55}
56
57bool QgsWmsRenderContext::addLayerToRender( QgsMapLayer *layer )
58{
59 const bool allowed = checkLayerReadPermissions( layer );
60 if ( allowed )
61 {
62 mLayersToRender.append( layer );
63 }
64 return allowed;
65}
66
67
68void QgsWmsRenderContext::setFlag( const Flag flag, const bool on )
69{
70 if ( on )
71 {
72 mFlags |= flag;
73 }
74 else
75 {
76 mFlags &= ~flag;
77 }
78}
79
81{
82 return mFlags.testFlag( flag );
83}
84
86{
87 return mParameters;
88}
89
91{
92 return *mInterface->serverSettings();
93}
94
96{
97 return mProject;
98}
99
100QDomElement QgsWmsRenderContext::sld( const QgsMapLayer &layer ) const
101{
102 QDomElement sld;
103
104 const QString nickname = layerNickname( layer );
105 if ( mSlds.contains( nickname ) )
106 {
107 sld = mSlds[nickname];
108 }
109
110 return sld;
111}
112
114{
115 QString style;
116
117 const QString nickname = layerNickname( layer );
118 if ( mStyles.contains( nickname ) )
119 {
120 style = mStyles[nickname];
121 }
122
123 return style;
124}
125
127{
129
130 const QList<QgsWmsParametersLayer> cLayerParams { mParameters.layersParameters() };
131
132 for ( const auto &params : std::as_const( cLayerParams ) )
133 {
134 if ( params.mNickname == layerNickname( layer ) )
135 {
136 parameters = params;
137 break;
138 }
139 }
140
141 return parameters;
142}
143
145{
147
148 if ( !mParameters.imageQuality().isEmpty() )
149 {
150 imageQuality = mParameters.imageQualityAsInt();
151 }
152
153 return imageQuality;
154}
155
157{
158 int tileBuffer = 0;
159
160 if ( mParameters.tiledAsBool() )
161 {
163 }
164
165 return tileBuffer;
166}
167
172
174{
176
177 if ( mParameters.wmsPrecisionAsInt() > -1 )
178 {
179 precision = mParameters.wmsPrecisionAsInt();
180 }
181
182 return precision;
183}
184
186{
187 // Apply DPI parameter if present. This is an extension of QGIS Server
188 // compared to WMS 1.3.
189 // Because of backwards compatibility, this parameter is optional
190 qreal dpm = 1 / OGC_PX_M;
191
192 if ( !mParameters.dpi().isEmpty() )
193 {
194 dpm = mParameters.dpiAsDouble() / 0.0254;
195 }
196
197 return dpm / 1000.0;
198}
199
200QStringList QgsWmsRenderContext::flattenedQueryLayers( const QStringList &layerNames ) const
201{
202 QStringList result;
203 std::function<QStringList( const QString &name )> findLeaves = [&]( const QString &name ) -> QStringList {
204 QStringList _result;
205 if ( mLayerGroups.contains( name ) )
206 {
207 const auto &layers { mLayerGroups[name] };
208 for ( const auto &l : layers )
209 {
210 // Only add allowed layers
211 if ( checkLayerReadPermissions( l ) )
212 {
213 const auto nick { layerNickname( *l ) };
214 // This handles the case for root (fake) group
215 if ( mLayerGroups.contains( nick ) )
216 {
217 _result.append( name );
218 }
219 else
220 {
221 _result.append( findLeaves( nick ) );
222 }
223 }
224 }
225 }
226 else
227 {
228 _result.append( name );
229 }
230 return _result;
231 };
232
233 for ( const auto &name : std::as_const( layerNames ) )
234 {
235 result.append( findLeaves( name ) );
236 }
237 return result;
238}
239
240QList<QgsMapLayer *> QgsWmsRenderContext::layersToRender() const
241{
242 return mLayersToRender;
243}
244
245QList<QgsMapLayer *> QgsWmsRenderContext::layers() const
246{
247 return mNicknameLayers.values();
248}
249
250QHash<const QgsMapLayer *, QStringList> QgsWmsRenderContext::acceptableLayersToRender() const
251{
252 return mAcceptableLayersToRender;
253}
254
256{
257 double denominator = -1;
258
259 if ( mScaleDenominator >= 0 )
260 {
261 denominator = mScaleDenominator;
262 }
263 else if ( mFlags & UseScaleDenominator && !mParameters.scale().isEmpty() )
264 {
265 denominator = mParameters.scaleAsDouble();
266 }
267
268 return denominator;
269}
270
272{
273 mScaleDenominator = scaleDenominator;
274 removeUnwantedLayers();
275}
276
278{
279 bool update = false;
280
281 if ( mFlags & UpdateExtent && !mParameters.bbox().isEmpty() )
282 {
283 update = true;
284 }
285
286 return update;
287}
288
290{
291 QString name = layer.serverProperties()->shortName();
292 // For external layers we cannot use the layer id because it's not known to the client, use layer name instead.
294 && std::find_if( mExternalLayers.cbegin(), mExternalLayers.cend(), [&layer]( const QgsMapLayer *l ) { return l->id() == layer.id(); } ) == mExternalLayers.cend() )
295 {
296 name = layer.id();
297 }
298 else if ( name.isEmpty() )
299 {
300 name = layer.name();
301 }
302
303 return name;
304}
305
306QgsMapLayer *QgsWmsRenderContext::layer( const QString &nickname ) const
307{
308 QgsMapLayer *mlayer = nullptr;
309
310 for ( auto layer : mLayersToRender )
311 {
312 if ( layerNickname( *layer ).compare( nickname ) == 0 )
313 {
314 mlayer = layer;
315 break;
316 }
317 }
318
319 return mlayer;
320}
321
322bool QgsWmsRenderContext::isValidLayer( const QString &nickname ) const
323{
324 return layer( nickname );
325}
326
327QList<QgsMapLayer *> QgsWmsRenderContext::layersFromGroup( const QString &nickname ) const
328{
329 return mLayerGroups.value( nickname );
330}
331
332bool QgsWmsRenderContext::isValidGroup( const QString &name ) const
333{
334 return mLayerGroups.contains( name );
335}
336
337void QgsWmsRenderContext::initNicknameLayers()
338{
339 for ( QgsMapLayer *ml : mProject->mapLayers() )
340 {
341 mNicknameLayers.insert( layerNickname( *ml ), ml );
342 }
343
344 // init groups
345 const QString rootName { QgsServerProjectUtils::wmsRootName( *mProject ) };
346 const QgsLayerTreeGroup *root = mProject->layerTreeRoot();
347
348 initLayerGroupsRecursive( root, rootName.isEmpty() ? mProject->title() : rootName );
349}
350
351void QgsWmsRenderContext::initLayerGroupsRecursive( const QgsLayerTreeGroup *group, const QString &groupName )
352{
353 if ( !groupName.isEmpty() )
354 {
355 QList<QgsMapLayer *> layerGroup;
356 const auto projectLayerTreeRoot { mProject->layerTreeRoot() };
357 const auto treeGroupLayers { group->findLayers() };
358 // Fast track if there is no custom layer order,
359 // otherwise reorder layers.
360 if ( !projectLayerTreeRoot->hasCustomLayerOrder() )
361 {
362 for ( const auto &tl : treeGroupLayers )
363 {
364 layerGroup.push_back( tl->layer() );
365 }
366 }
367 else
368 {
369 const auto projectLayerOrder { projectLayerTreeRoot->layerOrder() };
370 // Flat list containing the layers from the tree nodes
371 QList<QgsMapLayer *> groupLayersList;
372 for ( const auto &tl : treeGroupLayers )
373 {
374 groupLayersList << tl->layer();
375 }
376 for ( const auto &l : projectLayerOrder )
377 {
378 if ( groupLayersList.contains( l ) )
379 {
380 layerGroup.push_back( l );
381 }
382 }
383 }
384
385 if ( !layerGroup.empty() )
386 {
387 mLayerGroups[groupName] = layerGroup;
388 }
389 }
390
391 for ( const QgsLayerTreeNode *child : group->children() )
392 {
393 if ( child->nodeType() == QgsLayerTreeNode::NodeGroup )
394 {
395 auto group = static_cast<const QgsLayerTreeGroup *>( child );
396 QString name = group ? group->serverProperties()->shortName() : QString();
397
398 if ( name.isEmpty() )
399 name = child->name();
400
401 initLayerGroupsRecursive( group, name );
402 }
403 }
404}
405
406void QgsWmsRenderContext::initRestrictedLayers()
407{
408 mRestrictedLayers.clear();
409
410 // get name of restricted layers/groups in project
411 const QStringList restricted = QgsServerProjectUtils::wmsRestrictedLayers( *mProject );
412
413 // extract restricted layers from excluded groups
414 QStringList restrictedLayersNames;
415 QgsLayerTreeGroup *root = mProject->layerTreeRoot();
416
417 for ( const QString &l : std::as_const( restricted ) )
418 {
419 const QgsLayerTreeGroup *group = root->findGroup( l );
420 if ( group )
421 {
422 const QList<QgsLayerTreeLayer *> groupLayers = group->findLayers();
423 for ( QgsLayerTreeLayer *treeLayer : groupLayers )
424 {
425 restrictedLayersNames.append( treeLayer->name() );
426 }
427 }
428 else
429 {
430 restrictedLayersNames.append( l );
431 }
432 }
433
434 // build output with names, ids or short name according to the configuration
435 const QList<QgsLayerTreeLayer *> layers = root->findLayers();
436 for ( QgsLayerTreeLayer *layer : layers )
437 {
438 if ( restrictedLayersNames.contains( layer->name() ) )
439 {
440 mRestrictedLayers.append( layerNickname( *layer->layer() ) );
441 }
442 }
443}
444
445void QgsWmsRenderContext::searchLayersToRender()
446{
447 mLayersToRender.clear();
448 mStyles.clear();
449 mSlds.clear();
450
451 if ( !mParameters.sldBody().isEmpty() )
452 {
453 searchLayersToRenderSld();
454 }
455 else
456 {
457 searchLayersToRenderStyle();
458 }
459
460 QStringList nicknames;
461 if ( mFlags & AddQueryLayers )
462 nicknames << mParameters.queryLayersNickname();
463
464 if ( mFlags & AddAllLayers )
465 nicknames << mParameters.allLayersNickname();
466
467 if ( !nicknames.isEmpty() )
468 {
469 // Throw a LayerNotDefined when one of the requested layers or groups is not leading to a result, otherwise return the layers to render
470 mAcceptableLayersToRender = acceptableLayers( nicknames );
471 const QStringList queryLayerNames = flattenedQueryLayers( nicknames );
472 for ( const QString &layerName : queryLayerNames )
473 {
474 const QList<QgsMapLayer *> layers = mNicknameLayers.values( layerName );
475
476 for ( QgsMapLayer *lyr : layers )
477 {
478 if ( !mLayersToRender.contains( lyr ) )
479 {
480 if ( !mAcceptableLayersToRender.contains( lyr ) )
481 {
482 continue;
483 }
484 if ( !addLayerToRender( lyr ) )
485 {
486 throw QgsSecurityException( u"You are not allowed to access the layer %1"_s.arg( lyr->name() ) );
487 }
488 }
489 }
490 }
491 }
492}
493
494void QgsWmsRenderContext::searchLayersToRenderSld()
495{
496 const QString sld = mParameters.sldBody();
497
498 if ( sld.isEmpty() )
499 {
500 return;
501 }
502
503 QDomDocument doc;
504 ( void ) doc.setContent( sld, true );
505 QDomElement docEl = doc.documentElement();
506
507 QDomElement root = doc.firstChildElement( "StyledLayerDescriptor" );
508 QDomElement namedElem = root.firstChildElement( "NamedLayer" );
509
510 if ( docEl.isNull() )
511 {
512 return;
513 }
514
515 QDomNodeList named = docEl.elementsByTagName( "NamedLayer" );
516
517 QStringList requestedSldLayerNames;
518 for ( int i = 0; i < named.size(); ++i )
519 {
520 requestedSldLayerNames.append( named.item( i ).firstChildElement( u"Name"_s ).text() );
521 }
522
523 // Throw a LayerNotDefined when one of the requested layers or groups is not leading to a result, otherwise return the layers to render
524 mAcceptableLayersToRender = acceptableLayers( requestedSldLayerNames );
525
526 for ( int i = 0; i < named.size(); ++i )
527 {
528 QDomNodeList names = named.item( i ).toElement().elementsByTagName( "Name" );
529 if ( !names.isEmpty() )
530 {
531 QString lname = names.item( 0 ).toElement().text();
532 if ( mNicknameLayers.contains( lname ) )
533 {
534 mSlds[lname] = namedElem;
535 for ( const auto layer : mNicknameLayers.values( lname ) )
536 {
537 if ( !mAcceptableLayersToRender.contains( layer ) )
538 {
539 continue;
540 }
541 if ( !addLayerToRender( layer ) )
542 {
543 throw QgsSecurityException( u"You are not allowed to access the layer %1"_s.arg( layer->name() ) );
544 }
545 }
546 }
547 else if ( mLayerGroups.contains( lname ) )
548 {
550 {
551 QgsWmsParameter param( QgsWmsParameter::LAYER );
552 param.mValue = lname;
553 throw QgsBadRequestException( QgsServiceException::OGC_LayerNotDefined, param );
554 }
555
556 bool layerAdded = false;
557 for ( QgsMapLayer *layer : mLayerGroups[lname] )
558 {
559 if ( !mAcceptableLayersToRender.contains( layer ) )
560 {
561 continue;
562 }
563 // Insert only allowed layers
564 if ( checkLayerReadPermissions( layer ) )
565 {
566 const QString name = layerNickname( *layer );
567 mSlds[name] = namedElem;
568 mLayersToRender.insert( 0, layer );
569 layerAdded = true;
570 }
571 }
572 // No layers have been added, consider the group
573 // as non-existent.
574 if ( !layerAdded )
575 {
576 QgsWmsParameter param( QgsWmsParameter::LAYER );
577 param.mValue = lname;
578 throw QgsBadRequestException( QgsServiceException::OGC_LayerNotDefined, param );
579 }
580 }
581 else
582 {
583 QgsWmsParameter param( QgsWmsParameter::LAYER );
584 param.mValue = lname;
585 throw QgsBadRequestException( QgsServiceException::OGC_LayerNotDefined, param );
586 }
587 }
588 }
589}
590
591void QgsWmsRenderContext::searchLayersToRenderStyle()
592{
593 // Throw a LayerNotDefined when one of the requested layers or groups is not leading to a result, otherwise return the layers to render
594 mAcceptableLayersToRender = acceptableLayers( mParameters.allLayersNickname() );
595
596 for ( const QgsWmsParametersLayer &param : mParameters.layersParameters() )
597 {
598 const QString nickname = param.mNickname;
599 const QString style = param.mStyle;
600
601 if ( !param.mExternalUri.isEmpty() && ( mFlags & AddExternalLayers ) )
602 {
603 std::unique_ptr<QgsMapLayer> layer = std::make_unique<QgsRasterLayer>( param.mExternalUri, param.mNickname, u"wms"_s );
604
605 if ( layer->isValid() )
606 {
607 // to delete later
608 mExternalLayers.append( layer.release() );
609 auto lyr = mExternalLayers.last();
610 if ( !addLayerToRender( lyr ) )
611 {
612 throw QgsSecurityException( u"You are not allowed to access the layer %1"_s.arg( lyr->name() ) );
613 }
614 }
615 }
616 else if ( mNicknameLayers.contains( nickname ) )
617 {
618 if ( !style.isEmpty() )
619 {
620 mStyles[nickname] = style;
621 }
622
623 for ( const auto layer : mNicknameLayers.values( nickname ) )
624 {
625 if ( !mAcceptableLayersToRender.contains( layer ) )
626 {
627 continue;
628 }
629 if ( !addLayerToRender( layer ) )
630 {
631 throw QgsSecurityException( u"You are not allowed to access the layer %1"_s.arg( layer->name() ) );
632 }
633 }
634 }
635 else if ( mLayerGroups.contains( nickname ) )
636 {
638 {
639 QgsWmsParameter param( QgsWmsParameter::LAYER );
640 param.mValue = nickname;
641 throw QgsBadRequestException( QgsServiceException::OGC_LayerNotDefined, param );
642 }
643 // Reverse order of layers from a group
644 QList<QString> layersFromGroup;
645 for ( QgsMapLayer *layer : mLayerGroups[nickname] )
646 {
647 const QString nickname = layerNickname( *layer );
648 if ( !style.isEmpty() )
649 {
650 mStyles[nickname] = style;
651 }
652 layersFromGroup.push_front( nickname );
653 }
654
655 bool layerAdded = false;
656 for ( const auto &name : layersFromGroup )
657 {
658 for ( const auto layer : mNicknameLayers.values( name ) )
659 {
660 if ( !mAcceptableLayersToRender.contains( layer ) )
661 {
662 continue;
663 }
664 if ( addLayerToRender( layer ) )
665 {
666 layerAdded = true;
667 }
668 }
669 }
670 // No layers have been added, consider the group
671 // as non-existent.
672 if ( !layerAdded )
673 {
674 QgsWmsParameter param( QgsWmsParameter::LAYER );
675 param.mValue = nickname;
676 throw QgsBadRequestException( QgsServiceException::OGC_LayerNotDefined, param );
677 }
678 }
679 else
680 {
681 QgsWmsParameter param( QgsWmsParameter::LAYER );
682 param.mValue = nickname;
683 throw QgsBadRequestException( QgsServiceException::OGC_LayerNotDefined, param );
684 }
685 }
686}
687
688bool QgsWmsRenderContext::layerScaleVisibility( const QString &name ) const
689{
690 bool visible = false;
691
692 if ( !mNicknameLayers.contains( name ) )
693 {
694 return visible;
695 }
696
697 const QList<QgsMapLayer *> layers = mNicknameLayers.values( name );
698 for ( QgsMapLayer *layer : layers )
699 {
700 bool scaleBasedVisibility = layer->hasScaleBasedVisibility();
701 bool useScaleConstraint = ( scaleDenominator() > 0 && scaleBasedVisibility );
702
703 if ( !useScaleConstraint || layer->isInScaleRange( scaleDenominator() ) )
704 {
705 visible = true;
706 }
707 }
708
709 return visible;
710}
711
712QMap<QString, QList<QgsMapLayer *>> QgsWmsRenderContext::layerGroups() const
713{
714 return mLayerGroups;
715}
716
718{
719 int width = mParameters.widthAsInt();
720
721 // May use SRCWIDTH to define image map size
722 if ( ( mFlags & UseSrcWidthHeight ) && mParameters.srcWidthAsInt() > 0 )
723 {
724 width = mParameters.srcWidthAsInt();
725 }
726
727 return width;
728}
729
731{
732 int height = mParameters.heightAsInt();
733
734 // May use SRCHEIGHT to define image map size
735 if ( ( mFlags & UseSrcWidthHeight ) && mParameters.srcHeightAsInt() > 0 )
736 {
737 height = mParameters.srcHeightAsInt();
738 }
739
740 return height;
741}
742
747
748bool QgsWmsRenderContext::isValidWidthHeight( int width, int height ) const
749{
750 if ( width <= 0 || height <= 0 )
751 return false;
752
753 //test if maxWidth / maxHeight are set in the project or as an env variable
754 //and WIDTH / HEIGHT parameter is in the range allowed range
755 //WIDTH
756 const int wmsMaxWidthProj = QgsServerProjectUtils::wmsMaxWidth( *mProject );
757 const int wmsMaxWidthEnv = settings().wmsMaxWidth();
758 int wmsMaxWidth;
759 if ( wmsMaxWidthEnv != -1 && wmsMaxWidthProj != -1 )
760 {
761 // both are set, so we take the more conservative one
762 wmsMaxWidth = std::min( wmsMaxWidthProj, wmsMaxWidthEnv );
763 }
764 else
765 {
766 // none or one are set, so we take the bigger one which is the one set or -1
767 wmsMaxWidth = std::max( wmsMaxWidthProj, wmsMaxWidthEnv );
768 }
769
770 if ( wmsMaxWidth != -1 && width > wmsMaxWidth )
771 {
772 return false;
773 }
774
775 //HEIGHT
776 const int wmsMaxHeightProj = QgsServerProjectUtils::wmsMaxHeight( *mProject );
777 const int wmsMaxHeightEnv = settings().wmsMaxHeight();
778 int wmsMaxHeight;
779 if ( wmsMaxHeightEnv != -1 && wmsMaxHeightProj != -1 )
780 {
781 // both are set, so we take the more conservative one
782 wmsMaxHeight = std::min( wmsMaxHeightProj, wmsMaxHeightEnv );
783 }
784 else
785 {
786 // none or one are set, so we take the bigger one which is the one set or -1
787 wmsMaxHeight = std::max( wmsMaxHeightProj, wmsMaxHeightEnv );
788 }
789
790 if ( wmsMaxHeight != -1 && height > wmsMaxHeight )
791 {
792 return false;
793 }
794
795 // Sanity check from internal QImage checks
796 // (see QImageData::calculateImageParameters() in qimage_p.h)
797 // this is to report a meaningful error message in case of
798 // image creation failure and to differentiate it from out
799 // of memory conditions.
800
801 // depth for now it cannot be anything other than 32, but I don't like
802 // to hardcode it: I hope we will support other depths in the future.
803 int depth = 32;
804 switch ( mParameters.format() )
805 {
808 default:
809 depth = 32;
810 }
811
812 if ( width > ( std::numeric_limits<int>::max() - 31 ) / depth )
813 return false;
814
815 const int bytes_per_line = ( ( width * depth + 31 ) >> 5 ) << 2; // bytes per scanline (must be multiple of 4)
816
817 if ( std::numeric_limits<int>::max() / bytes_per_line < height || std::numeric_limits<int>::max() / sizeof( uchar * ) < static_cast<uint>( height ) )
818 {
819 return false;
820 }
821
822 return true;
823}
824
826{
827 double buffer;
828 if ( mFlags & UseTileBuffer )
829 {
830 const QgsRectangle extent = mParameters.bboxAsRectangle();
831 if ( !mParameters.bbox().isEmpty() && extent.isEmpty() )
832 {
834 }
835 buffer = tileBuffer() * ( extent.width() / mapWidth );
836 }
837 else
838 {
839 buffer = 0;
840 }
841 return buffer;
842}
843
844QSize QgsWmsRenderContext::mapSize( const bool aspectRatio ) const
845{
846 int width = mapWidth();
847 int height = mapHeight();
848
849 // Adapt width / height if the aspect ratio does not correspond with the BBOX.
850 // Required by WMS spec. 1.3.
851 if ( aspectRatio && mParameters.versionAsNumber() >= QgsProjectVersion( 1, 3, 0 ) )
852 {
853 QgsRectangle extent = mParameters.bboxAsRectangle();
854 if ( !mParameters.bbox().isEmpty() && extent.isEmpty() )
855 {
857 }
858
859 QString crs = mParameters.crs();
860 if ( crs.compare( "CRS:84", Qt::CaseInsensitive ) == 0 )
861 {
862 crs = QString( "EPSG:4326" );
863 extent.invert();
864 }
865
867 if ( outputCrs.hasAxisInverted() )
868 {
869 extent.invert();
870 }
871
872 if ( !extent.isEmpty() && height > 0 && width > 0 )
873 {
874 const double mapRatio = extent.width() / extent.height();
875 const double imageRatio = static_cast<double>( width ) / static_cast<double>( height );
876 if ( !qgsDoubleNear( mapRatio, imageRatio, 0.0001 ) )
877 {
878 // inspired by MapServer, mapdraw.c L115
879 const double cellsize = ( extent.width() / static_cast<double>( width ) ) * 0.5 + ( extent.height() / static_cast<double>( height ) ) * 0.5;
880 width = extent.width() / cellsize;
881 height = extent.height() / cellsize;
882 }
883 }
884 }
885
886 if ( width <= 0 )
887 {
889 }
890 else if ( height <= 0 )
891 {
893 }
894
895 return QSize( width, height );
896}
897
898void QgsWmsRenderContext::removeUnwantedLayers()
899{
900 QList<QgsMapLayer *> layers;
901
902 for ( QgsMapLayer *layer : mLayersToRender )
903 {
904 const QString nickname = layerNickname( *layer );
905
906 if ( !isExternalLayer( nickname ) )
907 {
908 if ( !layerScaleVisibility( nickname ) )
909 continue;
910
911 if ( mRestrictedLayers.contains( nickname ) )
912 continue;
913
914 if ( mFlags & UseWfsLayersOnly )
915 {
917 {
918 continue;
919 }
920
921 const QStringList wfsLayers = QgsServerProjectUtils::wfsLayerIds( *mProject );
922 if ( !wfsLayers.contains( layer->id() ) )
923 {
924 continue;
925 }
926 }
927 }
928
929 layers.append( layer );
930 }
931
932 mLayersToRender = layers;
933}
934
935QHash<const QgsMapLayer *, QStringList> QgsWmsRenderContext::acceptableLayers( const QStringList &requestedLayerNames ) const
936{
937 QHash<const QgsMapLayer *, QStringList> acceptableLayersAndRequestNames;
938 collectAcceptableLayersAndRequestNames( acceptableLayersAndRequestNames, *mProject, requestedLayerNames );
939 bool projectIsRequested = ( requestedLayerNames.contains( QgsServerProjectUtils::wmsRootName( *mProject ) ) || requestedLayerNames.contains( mProject->title() ) );
940
941 if ( !projectIsRequested )
942 {
943 // Throw a LayerNotDefined when one of the requested layers or groups is not leading to a result
944 auto firstFoundInacceptableLayer = std::find_if( requestedLayerNames.cbegin(), requestedLayerNames.cend(), [&]( const QString &layerName ) {
945 //return when the requested layer has not been found as a acceptable layer
946 return !std::any_of( acceptableLayersAndRequestNames.cbegin(), acceptableLayersAndRequestNames.cend(), [&]( const QStringList &requestedNames ) {
947 return requestedNames.contains( layerName ) || isExternalLayer( layerName );
948 } );
949 } );
950 if ( firstFoundInacceptableLayer != requestedLayerNames.cend() )
951 {
952 QgsWmsParameter param( QgsWmsParameter::LAYER );
953 param.mValue = *firstFoundInacceptableLayer;
954 throw QgsBadRequestException( QgsServiceException::OGC_LayerNotDefined, param );
955 }
956 }
957 return acceptableLayersAndRequestNames;
958}
959
960bool QgsWmsRenderContext::isExternalLayer( const QString &name ) const
961{
962 for ( const auto &layer : mExternalLayers )
963 {
964 if ( layerNickname( *layer ).compare( name ) == 0 )
965 return true;
966 }
967
968 return false;
969}
970
971bool QgsWmsRenderContext::checkLayerReadPermissions( QgsMapLayer *layer ) const
972{
973#ifdef HAVE_SERVER_PYTHON_PLUGINS
974 if ( !accessControl()->layerReadPermission( layer ) )
975 {
976 QString msg = u"Checking forbidden access for layer: %1"_s.arg( layer->name() );
978 return false;
979 }
980#endif
981 Q_UNUSED( layer )
982 return true;
983}
984
985#ifdef HAVE_SERVER_PYTHON_PLUGINS
986QgsAccessControl *QgsWmsRenderContext::accessControl() const
987{
988 return mInterface->accessControls();
989}
990#endif
991
993{
994 mSocketFeedback = feedback;
995}
996
998{
999 return mSocketFeedback;
1000}
@ Info
Information message.
Definition qgis.h:161
@ Vector
Vector layer.
Definition qgis.h:207
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 hasAxisInverted() const
Returns whether the axis order is inverted for the CRS compared to the order east/north (longitude/la...
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
QgsLayerTreeGroup * findGroup(const QString &name)
Find group node with specified name.
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the layer tree group.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
@ NodeGroup
Container of other groups and layers.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
QString shortName() const
Returns the short name of the layer used by QGIS Server to identify the layer.
Base class for all map layer types.
Definition qgsmaplayer.h:83
QString name
Definition qgsmaplayer.h:87
QString id
Definition qgsmaplayer.h:86
Qgis::LayerType type
Definition qgsmaplayer.h:93
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
Describes the version of a project.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:114
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
A rectangle specified with double values.
void invert()
Swap x/y coordinates in the rectangle.
Defines interfaces exposed by QGIS Server and made available to plugins.
static int wmsTileBuffer(const QgsProject &project)
Returns the tile buffer in pixels for WMS images defined in a QGIS project.
static QString wmsRootName(const QgsProject &project)
Returns the WMS root layer name defined in a QGIS project.
static bool wmsSkipNameForGroup(const QgsProject &project)
Returns if name attribute should be skipped for groups in WMS capabilities document.
static int wmsFeatureInfoPrecision(const QgsProject &project)
Returns the geometry precision for GetFeatureInfo request.
static bool wmsUseLayerIds(const QgsProject &project)
Returns if layer ids are used as name in WMS.
static QStringList wfsLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published in WFS.
static bool wmsRenderMapTiles(const QgsProject &project)
Returns true if WMS requests should use the QgsMapSettings::RenderMapTile flag, so that no visible ar...
static QStringList wmsRestrictedLayers(const QgsProject &project)
Returns the restricted layer name list.
static int wmsImageQuality(const QgsProject &project)
Returns the quality for WMS images defined in a QGIS project.
static int wmsMaxWidth(const QgsProject &project)
Returns the maximum width for WMS images defined in a QGIS project.
static int wmsMaxHeight(const QgsProject &project)
Returns the maximum height for WMS images defined in a QGIS project.
Provides a way to retrieve settings by prioritizing according to environment variables,...
int wmsMaxWidth() const
Returns the server-wide max width of a WMS GetMap request.
int wmsMaxHeight() const
Returns the server-wide max height of a WMS GetMap request.
Exception thrown in case of malformed request.
Provides an interface to retrieve and manipulate WMS parameters received from the client.
QSize mapSize(bool aspectRatio=true) const
Returns the size (in pixels) of the map to render, according to width and height WMS parameters as we...
bool isExternalLayer(const QString &name) const
Returns true if the layer is an external layer, false otherwise.
bool isValidGroup(const QString &name) const
Returns true if name is a group.
QStringList flattenedQueryLayers(const QStringList &layerNames) const
Returns a list of query layer names where group names are replaced by the names of their layer compon...
QgsFeedback * socketFeedback() const
Returns the response feedback if any.
QgsWmsRenderContext(const QgsProject *project, QgsServerInterface *interface)
Constructor for QgsWmsRenderContext.
QList< QgsMapLayer * > layers() const
Returns a list of all layers read from the project.
void setParameters(const QgsWmsParameters &parameters)
Sets WMS parameters.
QList< QgsMapLayer * > layersToRender() const
Returns a list of all layers to actually render according to the current configuration.
int mapWidth() const
Returns WIDTH or SRCWIDTH according to UseSrcWidthHeight flag.
QgsMapLayer * layer(const QString &nickname) const
Returns the layer corresponding to the nickname, or a nullptr if not found or if the layer do not nee...
int tileBuffer() const
Returns the tile buffer value to use for rendering according to the current configuration.
bool updateExtent() const
Returns true if the extent has to be updated before the rendering, false otherwise.
const QgsServerSettings & settings() const
Returns settings of the server.
void setFlag(Flag flag, bool on=true)
Sets or unsets a rendering flag according to the on value.
bool isValidWidthHeight() const
Returns true if width and height are valid according to the maximum values defined within the project...
QList< QgsMapLayer * > layersFromGroup(const QString &nickname) const
Returns the group's layers list corresponding to the nickname, or an empty list if not found.
QgsWmsParameters parameters() const
Returns WMS parameters.
void setScaleDenominator(double scaleDenominator)
Sets a custom scale denominator.
QString style(const QgsMapLayer &layer) const
Returns a style's name for a specific layer.
QMap< QString, QList< QgsMapLayer * > > layerGroups() const
Returns a map having layer group names as keys and a list of layers as values.
double mapTileBuffer(int mapWidth) const
Returns the tile buffer in geographical units for the given map width in pixels.
QString layerNickname(const QgsMapLayer &layer) const
Returns the nickname (short name, id or name) of the layer according to the current configuration.
void setSocketFeedback(QgsFeedback *feedback)
Sets the response feedback.
double scaleDenominator() const
Returns the scale denominator to use for rendering according to the current configuration.
qreal dotsPerMm() const
Returns default dots per mm according to the current configuration.
bool testFlag(Flag flag) const
Returns the status of a rendering flag.
QDomElement sld(const QgsMapLayer &layer) const
Returns a SLD document for a specific layer.
bool isValidLayer(const QString &nickname) const
Returns true if the layer has to be rendered, false otherwise.
const QgsProject * project() const
Returns the project.
int precision() const
Returns the precision to use according to the current configuration.
QHash< const QgsMapLayer *, QStringList > acceptableLayersToRender() const
Returns a hash of all the layers that can be rendered and for each a list of the layer names,...
int imageQuality() const
Returns the image quality to use for rendering according to the current configuration.
int mapHeight() const
Returns HEIGHT or SRCHEIGHT according to UseSrcWidthHeight flag.
bool renderMapTiles() const
Returns true if WMS requests should use the QgsMapSettings::RenderMapTile flag, so that no visible ar...
Flag
Available rendering options.
@ AddAllLayers
For GetPrint: add layers from LAYER(S) parameter.
Median cut implementation.
void collectAcceptableLayersAndRequestNames(QHash< const QgsMapLayer *, QStringList > &acceptableLayersAndRequestNames, const QgsProject &project, const QStringList &requestedLayerNames)
Collects the acceptableLayersAndRequestNames, a hash of all the layers that can be rendered and for e...
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7222
const double OGC_PX_M