31using namespace Qt::StringLiterals;
38 , mInterface( interface )
44 qDeleteAll( mExternalLayers );
45 mExternalLayers.clear();
52 initRestrictedLayers();
54 searchLayersToRender();
55 removeUnwantedLayers();
57 std::reverse( mLayersToRender.begin(), mLayersToRender.end() );
60bool QgsWmsRenderContext::addLayerToRender(
QgsMapLayer *layer )
62 const bool allowed = checkLayerReadPermissions(
layer );
65 mLayersToRender.append(
layer );
85 return mFlags.testFlag( flag );
95 return *mInterface->serverSettings();
108 if ( mSlds.contains( nickname ) )
110 sld = mSlds[nickname];
121 if ( mStyles.contains( nickname ) )
123 style = mStyles[nickname];
133 const QList<QgsWmsParametersLayer> cLayerParams { mParameters.layersParameters() };
135 for (
const auto ¶ms : std::as_const( cLayerParams ) )
151 if ( !mParameters.imageQuality().isEmpty() )
163 if ( mParameters.tiledAsBool() )
180 if ( mParameters.wmsPrecisionAsInt() > -1 )
182 precision = mParameters.wmsPrecisionAsInt();
195 if ( !mParameters.dpi().isEmpty() )
197 dpm = mParameters.dpiAsDouble() / 0.0254;
206 std::function<QStringList(
const QString &name )> findLeaves = [&](
const QString &name ) -> QStringList {
208 if ( mLayerGroups.contains( name ) )
210 const auto &
layers { mLayerGroups[name] };
211 for (
const auto &l :
layers )
214 if ( checkLayerReadPermissions( l ) )
218 if ( mLayerGroups.contains( nick ) )
220 _result.append( name );
224 _result.append( findLeaves( nick ) );
231 _result.append( name );
236 for (
const auto &name : std::as_const( layerNames ) )
238 result.append( findLeaves( name ) );
245 return mLayersToRender;
250 return mNicknameLayers.values();
255 return mAcceptableLayersToRender;
260 double denominator = -1;
262 if ( mScaleDenominator >= 0 )
264 denominator = mScaleDenominator;
268 denominator = mParameters.scaleAsDouble();
277 removeUnwantedLayers();
284 if ( mFlags &
UpdateExtent && !mParameters.bbox().isEmpty() )
294 QString name =
layer.serverProperties()->shortName();
297 && std::find_if( mExternalLayers.cbegin(), mExternalLayers.cend(), [&
layer](
const QgsMapLayer *l ) { return l->id() == layer.id(); } ) == mExternalLayers.cend() )
301 else if ( name.isEmpty() )
313 for (
auto layer : mLayersToRender )
327 return layer( nickname );
332 return mLayerGroups.value( nickname );
337 return mLayerGroups.contains( name );
340void QgsWmsRenderContext::initNicknameLayers()
349 const QgsLayerTreeGroup *root = mProject->layerTreeRoot();
351 initLayerGroupsRecursive( root, rootName.isEmpty() ? mProject->title() : rootName );
354void QgsWmsRenderContext::initLayerGroupsRecursive(
const QgsLayerTreeGroup *group,
const QString &groupName )
356 if ( !groupName.isEmpty() )
358 QList<QgsMapLayer *> layerGroup;
359 const auto projectLayerTreeRoot { mProject->layerTreeRoot() };
360 const auto treeGroupLayers { group->
findLayers() };
363 if ( !projectLayerTreeRoot->hasCustomLayerOrder() )
365 for (
const auto &tl : treeGroupLayers )
367 layerGroup.push_back( tl->layer() );
372 const auto projectLayerOrder { projectLayerTreeRoot->layerOrder() };
374 QList<QgsMapLayer *> groupLayersList;
375 for (
const auto &tl : treeGroupLayers )
377 groupLayersList << tl->layer();
379 for (
const auto &l : projectLayerOrder )
381 if ( groupLayersList.contains( l ) )
383 layerGroup.push_back( l );
388 if ( !layerGroup.empty() )
390 mLayerGroups[groupName] = layerGroup;
394 for (
const QgsLayerTreeNode *child : group->
children() )
398 auto group =
static_cast<const QgsLayerTreeGroup *
>( child );
401 if ( name.isEmpty() )
402 name = child->name();
404 initLayerGroupsRecursive( group, name );
412 initPerLayerTemporalRange( group, groupName );
415void QgsWmsRenderContext::initPerLayerTemporalRange(
const QgsLayerTreeGroup *group,
const QString &groupName )
417 if ( groupName.isEmpty() )
421 const QString timeString { mParameters.dimensionValues().value( u
"TIME"_s, QString() ) };
422 if ( !timeString.isEmpty() )
425 const QgsMapLayerServerProperties *serverProperties = group->
serverProperties();
426 const QList<QgsMapLayerServerProperties::WmsDimensionInfo> wmsDimensions = serverProperties->
wmsDimensions();
427 auto it = std::find_if( wmsDimensions.constBegin(), wmsDimensions.constEnd(), [](
const QgsMapLayerServerProperties::WmsDimensionInfo &dim ) {
428 return dim.name == QgsServerWmsDimensionProperties::TIME_DIMENSION_NAME;
431 if ( it != wmsDimensions.constEnd() )
433 bool wmsLayerInfosInitialized =
false;
434 QMap<QString, QgsWmsLayerInfos> wmsLayerInfos;
435 QList<QgsDateTimeRange> childrenDateRanges;
436 QDateTime defaultDateTime;
437 switch ( it->defaultDisplayType )
442 if ( !wmsLayerInfosInitialized )
445 wmsLayerInfosInitialized =
true;
454 defaultDateTime = it->referenceValue().toDateTime();
462 if ( defaultDateTime.isValid() )
464 for ( QgsMapLayer *
layer : mLayerGroups[groupName] )
470void QgsWmsRenderContext::initRestrictedLayers()
472 mRestrictedLayers.clear();
478 QStringList restrictedLayersNames;
479 QgsLayerTreeGroup *root = mProject->layerTreeRoot();
481 for (
const QString &l : std::as_const( restricted ) )
483 const QgsLayerTreeGroup *group = root->
findGroup( l );
486 const QList<QgsLayerTreeLayer *> groupLayers = group->
findLayers();
487 for ( QgsLayerTreeLayer *treeLayer : groupLayers )
489 restrictedLayersNames.append( treeLayer->name() );
494 restrictedLayersNames.append( l );
502 if ( restrictedLayersNames.contains(
layer->name() ) )
509void QgsWmsRenderContext::searchLayersToRender()
511 mLayersToRender.clear();
515 if ( !mParameters.sldBody().isEmpty() )
517 searchLayersToRenderSld();
521 searchLayersToRenderStyle();
524 QStringList nicknames;
526 nicknames << mParameters.queryLayersNickname();
529 nicknames << mParameters.allLayersNickname();
531 if ( !nicknames.isEmpty() )
534 mAcceptableLayersToRender = acceptableLayers( nicknames );
536 for (
const QString &layerName : queryLayerNames )
538 const QList<QgsMapLayer *>
layers = mNicknameLayers.values( layerName );
540 for ( QgsMapLayer *lyr :
layers )
542 if ( !mLayersToRender.contains( lyr ) )
544 if ( !mAcceptableLayersToRender.contains( lyr ) )
548 if ( !addLayerToRender( lyr ) )
550 throw QgsSecurityException( u
"You are not allowed to access the layer %1"_s.arg( lyr->name() ) );
558void QgsWmsRenderContext::searchLayersToRenderSld()
560 const QString
sld = mParameters.sldBody();
568 ( void ) doc.setContent(
sld,
true );
569 QDomElement docEl = doc.documentElement();
571 QDomElement root = doc.firstChildElement(
"StyledLayerDescriptor" );
572 QDomElement namedElem = root.firstChildElement(
"NamedLayer" );
574 if ( docEl.isNull() )
579 QDomNodeList named = docEl.elementsByTagName(
"NamedLayer" );
581 QStringList requestedSldLayerNames;
582 for (
int i = 0; i < named.size(); ++i )
584 requestedSldLayerNames.append( named.item( i ).firstChildElement( u
"Name"_s ).text() );
588 mAcceptableLayersToRender = acceptableLayers( requestedSldLayerNames );
590 for (
int i = 0; i < named.size(); ++i )
592 QDomNodeList names = named.item( i ).toElement().elementsByTagName(
"Name" );
593 if ( !names.isEmpty() )
595 QString lname = names.item( 0 ).toElement().text();
596 if ( mNicknameLayers.contains( lname ) )
598 mSlds[lname] = namedElem;
599 for (
const auto layer : mNicknameLayers.values( lname ) )
601 if ( !mAcceptableLayersToRender.contains(
layer ) )
605 if ( !addLayerToRender(
layer ) )
607 throw QgsSecurityException( u
"You are not allowed to access the layer %1"_s.arg(
layer->name() ) );
611 else if ( mLayerGroups.contains( lname ) )
616 param.mValue = lname;
620 bool layerAdded =
false;
621 for ( QgsMapLayer *
layer : mLayerGroups[lname] )
623 if ( !mAcceptableLayersToRender.contains(
layer ) )
628 if ( checkLayerReadPermissions(
layer ) )
631 mSlds[name] = namedElem;
632 mLayersToRender.insert( 0,
layer );
641 param.mValue = lname;
648 param.mValue = lname;
655void QgsWmsRenderContext::searchLayersToRenderStyle()
658 mAcceptableLayersToRender = acceptableLayers( mParameters.allLayersNickname() );
660 for (
const QgsWmsParametersLayer ¶m : mParameters.layersParameters() )
662 const QString nickname = param.mNickname;
663 const QString
style = param.mStyle;
667 std::unique_ptr<QgsMapLayer>
layer = std::make_unique<QgsRasterLayer>( param.mExternalUri, param.mNickname, u
"wms"_s );
669 if (
layer->isValid() )
672 mExternalLayers.append(
layer.release() );
673 auto lyr = mExternalLayers.last();
674 if ( !addLayerToRender( lyr ) )
676 throw QgsSecurityException( u
"You are not allowed to access the layer %1"_s.arg( lyr->name() ) );
680 else if ( mNicknameLayers.contains( nickname ) )
682 if ( !
style.isEmpty() )
684 mStyles[nickname] =
style;
687 for (
const auto layer : mNicknameLayers.values( nickname ) )
689 if ( !mAcceptableLayersToRender.contains(
layer ) )
693 if ( !addLayerToRender(
layer ) )
695 throw QgsSecurityException( u
"You are not allowed to access the layer %1"_s.arg(
layer->name() ) );
699 else if ( mLayerGroups.contains( nickname ) )
704 param.mValue = nickname;
709 for ( QgsMapLayer *
layer : mLayerGroups[nickname] )
712 if ( !
style.isEmpty() )
714 mStyles[nickname] =
style;
719 bool layerAdded =
false;
722 for (
const auto layer : mNicknameLayers.values( name ) )
724 if ( !mAcceptableLayersToRender.contains(
layer ) )
728 if ( addLayerToRender(
layer ) )
739 param.mValue = nickname;
746 param.mValue = nickname;
752bool QgsWmsRenderContext::layerScaleVisibility(
const QString &name )
const
754 bool visible =
false;
756 if ( !mNicknameLayers.contains( name ) )
761 const QList<QgsMapLayer *>
layers = mNicknameLayers.values( name );
764 bool scaleBasedVisibility =
layer->hasScaleBasedVisibility();
765 bool useScaleConstraint = (
scaleDenominator() > 0 && scaleBasedVisibility );
783 int width = mParameters.widthAsInt();
788 width = mParameters.srcWidthAsInt();
796 int height = mParameters.heightAsInt();
801 height = mParameters.srcHeightAsInt();
814 if ( width <= 0 || height <= 0 )
823 if ( wmsMaxWidthEnv != -1 && wmsMaxWidthProj != -1 )
826 wmsMaxWidth = std::min( wmsMaxWidthProj, wmsMaxWidthEnv );
831 wmsMaxWidth = std::max( wmsMaxWidthProj, wmsMaxWidthEnv );
834 if ( wmsMaxWidth != -1 && width > wmsMaxWidth )
843 if ( wmsMaxHeightEnv != -1 && wmsMaxHeightProj != -1 )
846 wmsMaxHeight = std::min( wmsMaxHeightProj, wmsMaxHeightEnv );
851 wmsMaxHeight = std::max( wmsMaxHeightProj, wmsMaxHeightEnv );
854 if ( wmsMaxHeight != -1 && height > wmsMaxHeight )
868 switch ( mParameters.format() )
876 if ( width > ( std::numeric_limits<int>::max() - 31 ) / depth )
879 const int bytes_per_line = ( ( width * depth + 31 ) >> 5 ) << 2;
881 if ( std::numeric_limits<int>::max() / bytes_per_line < height || std::numeric_limits<int>::max() /
sizeof( uchar * ) <
static_cast<uint
>( height ) )
894 const QgsRectangle extent = mParameters.bboxAsRectangle();
895 if ( !mParameters.bbox().isEmpty() && extent.
isEmpty() )
915 if ( aspectRatio && mParameters.versionAsNumber() >=
QgsProjectVersion( 1, 3, 0 ) )
918 if ( !mParameters.bbox().isEmpty() && extent.
isEmpty() )
923 QString crs = mParameters.crs();
924 if ( crs.compare(
"CRS:84", Qt::CaseInsensitive ) == 0 )
926 crs = QString(
"EPSG:4326" );
936 if ( !extent.
isEmpty() && height > 0 && width > 0 )
938 const double mapRatio = extent.
width() / extent.
height();
939 const double imageRatio =
static_cast<double>( width ) /
static_cast<double>( height );
943 const double cellsize = ( extent.
width() /
static_cast<double>( width ) ) * 0.5 + ( extent.
height() /
static_cast<double>( height ) ) * 0.5;
944 width = extent.
width() / cellsize;
945 height = extent.
height() / cellsize;
954 else if ( height <= 0 )
959 return QSize( width, height );
962void QgsWmsRenderContext::removeUnwantedLayers()
964 QList<QgsMapLayer *>
layers;
972 if ( !layerScaleVisibility( nickname ) )
975 if ( mRestrictedLayers.contains( nickname ) )
986 if ( !wfsLayers.contains(
layer->
id() ) )
999QHash<const QgsMapLayer *, QStringList> QgsWmsRenderContext::acceptableLayers(
const QStringList &requestedLayerNames )
const
1001 QHash<const QgsMapLayer *, QStringList> acceptableLayersAndRequestNames;
1005 if ( !projectIsRequested )
1008 auto firstFoundInacceptableLayer = std::find_if( requestedLayerNames.cbegin(), requestedLayerNames.cend(), [&](
const QString &layerName ) {
1010 return !std::any_of( acceptableLayersAndRequestNames.cbegin(), acceptableLayersAndRequestNames.cend(), [&]( const QStringList &requestedNames ) {
1011 return requestedNames.contains( layerName ) || mParameters.isExternalLayer( layerName );
1014 if ( firstFoundInacceptableLayer != requestedLayerNames.cend() )
1017 param.mValue = *firstFoundInacceptableLayer;
1021 return acceptableLayersAndRequestNames;
1026 for (
const auto &
layer : mExternalLayers )
1035bool QgsWmsRenderContext::checkLayerReadPermissions(
QgsMapLayer *layer )
const
1037#ifdef HAVE_SERVER_PYTHON_PLUGINS
1038 if ( !accessControl()->layerReadPermission(
layer ) )
1040 QString msg = u
"Checking forbidden access for layer: %1"_s.arg(
layer->
name() );
1049#ifdef HAVE_SERVER_PYTHON_PLUGINS
1050QgsAccessControl *QgsWmsRenderContext::accessControl()
const
1052 return mInterface->accessControls();
1058 mSocketFeedback = feedback;
1063 return mSocketFeedback;
@ Info
Information message.
@ MinValue
Display minimum value of the dimension.
@ AllValues
Display all values of the dimension.
@ MaxValue
Display maximum value of the dimension.
@ ReferenceValue
Display a reference value.
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.
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.
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,...
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.
const QList< QgsServerWmsDimensionProperties::WmsDimensionInfo > wmsDimensions() const
Returns the QGIS Server WMS Dimension list.
static QDateTime min(const QList< QgsTemporalRange< QDateTime > > &ranges)
static QMap< QString, QgsWmsLayerInfos > buildWmsLayerInfos(QgsServerInterface *serverIface, const QgsProject *project, const QList< QgsCoordinateReferenceSystem > &outputCrsList)
Returns the WMS layers definition to build WMS capabilities.
Exception thrown in case of malformed request.
@ QGIS_InvalidParameterValue
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 ¶meters)
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...
void getChildRanges(const QgsLayerTreeGroup *layerTreeGroup, const QMap< QString, QgsWmsLayerInfos > &wmsLayerInfos, const QStringList &restrictedLayers, QList< QgsDateTimeRange > &dateRanges)
Update recursively dateRanges with all layerTreeGroup children date ranges.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
QgsTemporalRange< QDateTime > QgsDateTimeRange
QgsRange which stores a range of date times.