28using namespace Qt::StringLiterals;
35 , mInterface( interface )
41 qDeleteAll( mExternalLayers );
42 mExternalLayers.clear();
49 initRestrictedLayers();
51 searchLayersToRender();
52 removeUnwantedLayers();
54 std::reverse( mLayersToRender.begin(), mLayersToRender.end() );
57bool QgsWmsRenderContext::addLayerToRender(
QgsMapLayer *layer )
59 const bool allowed = checkLayerReadPermissions(
layer );
62 mLayersToRender.append(
layer );
82 return mFlags.testFlag( flag );
92 return *mInterface->serverSettings();
105 if ( mSlds.contains( nickname ) )
107 sld = mSlds[nickname];
118 if ( mStyles.contains( nickname ) )
120 style = mStyles[nickname];
130 const QList<QgsWmsParametersLayer> cLayerParams { mParameters.layersParameters() };
132 for (
const auto ¶ms : std::as_const( cLayerParams ) )
148 if ( !mParameters.imageQuality().isEmpty() )
160 if ( mParameters.tiledAsBool() )
177 if ( mParameters.wmsPrecisionAsInt() > -1 )
179 precision = mParameters.wmsPrecisionAsInt();
192 if ( !mParameters.dpi().isEmpty() )
194 dpm = mParameters.dpiAsDouble() / 0.0254;
203 std::function<QStringList(
const QString &name )> findLeaves = [&](
const QString &name ) -> QStringList {
205 if ( mLayerGroups.contains( name ) )
207 const auto &
layers { mLayerGroups[name] };
208 for (
const auto &l :
layers )
211 if ( checkLayerReadPermissions( l ) )
215 if ( mLayerGroups.contains( nick ) )
217 _result.append( name );
221 _result.append( findLeaves( nick ) );
228 _result.append( name );
233 for (
const auto &name : std::as_const( layerNames ) )
235 result.append( findLeaves( name ) );
242 return mLayersToRender;
247 return mNicknameLayers.values();
252 return mAcceptableLayersToRender;
257 double denominator = -1;
259 if ( mScaleDenominator >= 0 )
261 denominator = mScaleDenominator;
265 denominator = mParameters.scaleAsDouble();
274 removeUnwantedLayers();
281 if ( mFlags &
UpdateExtent && !mParameters.bbox().isEmpty() )
291 QString name =
layer.serverProperties()->shortName();
294 && std::find_if( mExternalLayers.cbegin(), mExternalLayers.cend(), [&
layer](
const QgsMapLayer *l ) { return l->id() == layer.id(); } ) == mExternalLayers.cend() )
298 else if ( name.isEmpty() )
310 for (
auto layer : mLayersToRender )
324 return layer( nickname );
329 return mLayerGroups.value( nickname );
334 return mLayerGroups.contains( name );
337void QgsWmsRenderContext::initNicknameLayers()
346 const QgsLayerTreeGroup *root = mProject->layerTreeRoot();
348 initLayerGroupsRecursive( root, rootName.isEmpty() ? mProject->title() : rootName );
351void QgsWmsRenderContext::initLayerGroupsRecursive(
const QgsLayerTreeGroup *group,
const QString &groupName )
353 if ( !groupName.isEmpty() )
355 QList<QgsMapLayer *> layerGroup;
356 const auto projectLayerTreeRoot { mProject->layerTreeRoot() };
357 const auto treeGroupLayers { group->
findLayers() };
360 if ( !projectLayerTreeRoot->hasCustomLayerOrder() )
362 for (
const auto &tl : treeGroupLayers )
364 layerGroup.push_back( tl->layer() );
369 const auto projectLayerOrder { projectLayerTreeRoot->layerOrder() };
371 QList<QgsMapLayer *> groupLayersList;
372 for (
const auto &tl : treeGroupLayers )
374 groupLayersList << tl->layer();
376 for (
const auto &l : projectLayerOrder )
378 if ( groupLayersList.contains( l ) )
380 layerGroup.push_back( l );
385 if ( !layerGroup.empty() )
387 mLayerGroups[groupName] = layerGroup;
391 for (
const QgsLayerTreeNode *child : group->
children() )
395 auto group =
static_cast<const QgsLayerTreeGroup *
>( child );
398 if ( name.isEmpty() )
399 name = child->name();
401 initLayerGroupsRecursive( group, name );
406void QgsWmsRenderContext::initRestrictedLayers()
408 mRestrictedLayers.clear();
414 QStringList restrictedLayersNames;
415 QgsLayerTreeGroup *root = mProject->layerTreeRoot();
417 for (
const QString &l : std::as_const( restricted ) )
419 const QgsLayerTreeGroup *group = root->
findGroup( l );
422 const QList<QgsLayerTreeLayer *> groupLayers = group->
findLayers();
423 for ( QgsLayerTreeLayer *treeLayer : groupLayers )
425 restrictedLayersNames.append( treeLayer->name() );
430 restrictedLayersNames.append( l );
438 if ( restrictedLayersNames.contains(
layer->name() ) )
445void QgsWmsRenderContext::searchLayersToRender()
447 mLayersToRender.clear();
451 if ( !mParameters.sldBody().isEmpty() )
453 searchLayersToRenderSld();
457 searchLayersToRenderStyle();
460 QStringList nicknames;
462 nicknames << mParameters.queryLayersNickname();
465 nicknames << mParameters.allLayersNickname();
467 if ( !nicknames.isEmpty() )
470 mAcceptableLayersToRender = acceptableLayers( nicknames );
472 for (
const QString &layerName : queryLayerNames )
474 const QList<QgsMapLayer *>
layers = mNicknameLayers.values( layerName );
476 for ( QgsMapLayer *lyr :
layers )
478 if ( !mLayersToRender.contains( lyr ) )
480 if ( !mAcceptableLayersToRender.contains( lyr ) )
484 if ( !addLayerToRender( lyr ) )
486 throw QgsSecurityException( u
"You are not allowed to access the layer %1"_s.arg( lyr->name() ) );
494void QgsWmsRenderContext::searchLayersToRenderSld()
496 const QString
sld = mParameters.sldBody();
504 ( void ) doc.setContent(
sld,
true );
505 QDomElement docEl = doc.documentElement();
507 QDomElement root = doc.firstChildElement(
"StyledLayerDescriptor" );
508 QDomElement namedElem = root.firstChildElement(
"NamedLayer" );
510 if ( docEl.isNull() )
515 QDomNodeList named = docEl.elementsByTagName(
"NamedLayer" );
517 QStringList requestedSldLayerNames;
518 for (
int i = 0; i < named.size(); ++i )
520 requestedSldLayerNames.append( named.item( i ).firstChildElement( u
"Name"_s ).text() );
524 mAcceptableLayersToRender = acceptableLayers( requestedSldLayerNames );
526 for (
int i = 0; i < named.size(); ++i )
528 QDomNodeList names = named.item( i ).toElement().elementsByTagName(
"Name" );
529 if ( !names.isEmpty() )
531 QString lname = names.item( 0 ).toElement().text();
532 if ( mNicknameLayers.contains( lname ) )
534 mSlds[lname] = namedElem;
535 for (
const auto layer : mNicknameLayers.values( lname ) )
537 if ( !mAcceptableLayersToRender.contains(
layer ) )
541 if ( !addLayerToRender(
layer ) )
543 throw QgsSecurityException( u
"You are not allowed to access the layer %1"_s.arg(
layer->name() ) );
547 else if ( mLayerGroups.contains( lname ) )
552 param.mValue = lname;
556 bool layerAdded =
false;
557 for ( QgsMapLayer *
layer : mLayerGroups[lname] )
559 if ( !mAcceptableLayersToRender.contains(
layer ) )
564 if ( checkLayerReadPermissions(
layer ) )
567 mSlds[name] = namedElem;
568 mLayersToRender.insert( 0,
layer );
577 param.mValue = lname;
584 param.mValue = lname;
591void QgsWmsRenderContext::searchLayersToRenderStyle()
594 mAcceptableLayersToRender = acceptableLayers( mParameters.allLayersNickname() );
596 for (
const QgsWmsParametersLayer ¶m : mParameters.layersParameters() )
598 const QString nickname = param.mNickname;
599 const QString
style = param.mStyle;
603 std::unique_ptr<QgsMapLayer>
layer = std::make_unique<QgsRasterLayer>( param.mExternalUri, param.mNickname, u
"wms"_s );
605 if (
layer->isValid() )
608 mExternalLayers.append(
layer.release() );
609 auto lyr = mExternalLayers.last();
610 if ( !addLayerToRender( lyr ) )
612 throw QgsSecurityException( u
"You are not allowed to access the layer %1"_s.arg( lyr->name() ) );
616 else if ( mNicknameLayers.contains( nickname ) )
618 if ( !
style.isEmpty() )
620 mStyles[nickname] =
style;
623 for (
const auto layer : mNicknameLayers.values( nickname ) )
625 if ( !mAcceptableLayersToRender.contains(
layer ) )
629 if ( !addLayerToRender(
layer ) )
631 throw QgsSecurityException( u
"You are not allowed to access the layer %1"_s.arg(
layer->name() ) );
635 else if ( mLayerGroups.contains( nickname ) )
640 param.mValue = nickname;
645 for ( QgsMapLayer *
layer : mLayerGroups[nickname] )
648 if ( !
style.isEmpty() )
650 mStyles[nickname] =
style;
655 bool layerAdded =
false;
658 for (
const auto layer : mNicknameLayers.values( name ) )
660 if ( !mAcceptableLayersToRender.contains(
layer ) )
664 if ( addLayerToRender(
layer ) )
675 param.mValue = nickname;
682 param.mValue = nickname;
688bool QgsWmsRenderContext::layerScaleVisibility(
const QString &name )
const
690 bool visible =
false;
692 if ( !mNicknameLayers.contains( name ) )
697 const QList<QgsMapLayer *>
layers = mNicknameLayers.values( name );
700 bool scaleBasedVisibility =
layer->hasScaleBasedVisibility();
701 bool useScaleConstraint = (
scaleDenominator() > 0 && scaleBasedVisibility );
719 int width = mParameters.widthAsInt();
724 width = mParameters.srcWidthAsInt();
732 int height = mParameters.heightAsInt();
737 height = mParameters.srcHeightAsInt();
750 if ( width <= 0 || height <= 0 )
759 if ( wmsMaxWidthEnv != -1 && wmsMaxWidthProj != -1 )
762 wmsMaxWidth = std::min( wmsMaxWidthProj, wmsMaxWidthEnv );
767 wmsMaxWidth = std::max( wmsMaxWidthProj, wmsMaxWidthEnv );
770 if ( wmsMaxWidth != -1 && width > wmsMaxWidth )
779 if ( wmsMaxHeightEnv != -1 && wmsMaxHeightProj != -1 )
782 wmsMaxHeight = std::min( wmsMaxHeightProj, wmsMaxHeightEnv );
787 wmsMaxHeight = std::max( wmsMaxHeightProj, wmsMaxHeightEnv );
790 if ( wmsMaxHeight != -1 && height > wmsMaxHeight )
804 switch ( mParameters.format() )
812 if ( width > ( std::numeric_limits<int>::max() - 31 ) / depth )
815 const int bytes_per_line = ( ( width * depth + 31 ) >> 5 ) << 2;
817 if ( std::numeric_limits<int>::max() / bytes_per_line < height || std::numeric_limits<int>::max() /
sizeof( uchar * ) <
static_cast<uint
>( height ) )
830 const QgsRectangle extent = mParameters.bboxAsRectangle();
831 if ( !mParameters.bbox().isEmpty() && extent.
isEmpty() )
851 if ( aspectRatio && mParameters.versionAsNumber() >=
QgsProjectVersion( 1, 3, 0 ) )
854 if ( !mParameters.bbox().isEmpty() && extent.
isEmpty() )
859 QString crs = mParameters.crs();
860 if ( crs.compare(
"CRS:84", Qt::CaseInsensitive ) == 0 )
862 crs = QString(
"EPSG:4326" );
872 if ( !extent.
isEmpty() && height > 0 && width > 0 )
874 const double mapRatio = extent.
width() / extent.
height();
875 const double imageRatio =
static_cast<double>( width ) /
static_cast<double>( height );
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;
890 else if ( height <= 0 )
895 return QSize( width, height );
898void QgsWmsRenderContext::removeUnwantedLayers()
900 QList<QgsMapLayer *>
layers;
908 if ( !layerScaleVisibility( nickname ) )
911 if ( mRestrictedLayers.contains( nickname ) )
922 if ( !wfsLayers.contains(
layer->
id() ) )
935QHash<const QgsMapLayer *, QStringList> QgsWmsRenderContext::acceptableLayers(
const QStringList &requestedLayerNames )
const
937 QHash<const QgsMapLayer *, QStringList> acceptableLayersAndRequestNames;
941 if ( !projectIsRequested )
944 auto firstFoundInacceptableLayer = std::find_if( requestedLayerNames.cbegin(), requestedLayerNames.cend(), [&](
const QString &layerName ) {
946 return !std::any_of( acceptableLayersAndRequestNames.cbegin(), acceptableLayersAndRequestNames.cend(), [&]( const QStringList &requestedNames ) {
947 return requestedNames.contains( layerName ) || isExternalLayer( layerName );
950 if ( firstFoundInacceptableLayer != requestedLayerNames.cend() )
953 param.mValue = *firstFoundInacceptableLayer;
957 return acceptableLayersAndRequestNames;
962 for (
const auto &
layer : mExternalLayers )
971bool QgsWmsRenderContext::checkLayerReadPermissions(
QgsMapLayer *layer )
const
973#ifdef HAVE_SERVER_PYTHON_PLUGINS
974 if ( !accessControl()->layerReadPermission(
layer ) )
976 QString msg = u
"Checking forbidden access for layer: %1"_s.arg(
layer->
name() );
985#ifdef HAVE_SERVER_PYTHON_PLUGINS
986QgsAccessControl *QgsWmsRenderContext::accessControl()
const
988 return mInterface->accessControls();
994 mSocketFeedback = feedback;
999 return mSocketFeedback;
@ Info
Information message.
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.
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...
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).