From f926033699d8dd52e3aacbf2b4d449157d0e9938 Mon Sep 17 00:00:00 2001 From: rldhont Date: Mon, 3 Jul 2017 17:58:47 +0200 Subject: [PATCH] [Server] WMS GetFeatureInfo refactoring --- python/server/qgsserverprojectutils.sip | 56 +++ src/server/qgsserverprojectutils.cpp | 71 +++ src/server/qgsserverprojectutils.h | 42 ++ src/server/services/wms/qgswmsparameters.cpp | 163 ++++++- src/server/services/wms/qgswmsparameters.h | 115 ++++- src/server/services/wms/qgswmsrenderer.cpp | 438 +++++++++--------- src/server/services/wms/qgswmsrenderer.h | 2 +- .../python/test_qgsserver_accesscontrol.py | 54 +++ tests/src/python/test_qgsserver_wms.py | 15 +- .../wms_getfeatureinfo-text-xml.txt | 14 + 10 files changed, 735 insertions(+), 235 deletions(-) create mode 100644 tests/testdata/qgis_server/wms_getfeatureinfo-text-xml.txt diff --git a/python/server/qgsserverprojectutils.sip b/python/server/qgsserverprojectutils.sip index 6c3b35eceeb..4fcdc58e1e5 100644 --- a/python/server/qgsserverprojectutils.sip +++ b/python/server/qgsserverprojectutils.sip @@ -145,6 +145,62 @@ namespace QgsServerProjectUtils :rtype: bool %End + bool wmsFeatureInfoAddWktGeometry( const QgsProject &project ); +%Docstring + Returns if the geometry is displayed as Well Known Text in GetFeatureInfo request. + \param project the QGIS project + :return: if the geometry is displayed as Well Known Text in GetFeatureInfo request. + :rtype: bool +%End + + bool wmsFeatureInfoSegmentizeWktGeometry( const QgsProject &project ); +%Docstring + Returns if the geometry has to be segmentize in GetFeatureInfo request. + \param project the QGIS project + :return: if the geometry has to be segmentize in GetFeatureInfo request. + :rtype: bool +%End + + int wmsFeatureInfoPrecision( const QgsProject &project ); +%Docstring + Returns the geometry precision for GetFeatureInfo request. + \param project the QGIS project + :return: the geometry precision for GetFeatureInfo request. + :rtype: int +%End + + QString wmsFeatureInfoDocumentElement( const QgsProject &project ); +%Docstring + Returns the document element name for XML GetFeatureInfo request. + \param project the QGIS project + :return: the document element name for XML GetFeatureInfo request. + :rtype: str +%End + + QString wmsFeatureInfoDocumentElementNs( const QgsProject &project ); +%Docstring + Returns the document element namespace for XML GetFeatureInfo request. + \param project the QGIS project + :return: the document element namespace for XML GetFeatureInfo request. + :rtype: str +%End + + QString wmsFeatureInfoSchema( const QgsProject &project ); +%Docstring + Returns the schema URL for XML GetFeatureInfo request. + \param project the QGIS project + :return: the schema URL for XML GetFeatureInfo request. + :rtype: str +%End + + QHash wmsFeatureInfoLayerAliasMap( const QgsProject &project ); +%Docstring + Returns the mapping between layer name and wms layer name for GetFeatureInfo request. + \param project the QGIS project + :return: the mapping between layer name and wms layer name for GetFeatureInfo request. + :rtype: QHash +%End + bool wmsInspireActivate( const QgsProject &project ); %Docstring Returns if Inspire is activated. diff --git a/src/server/qgsserverprojectutils.cpp b/src/server/qgsserverprojectutils.cpp index de00e8a3cea..e5a71034d29 100644 --- a/src/server/qgsserverprojectutils.cpp +++ b/src/server/qgsserverprojectutils.cpp @@ -117,6 +117,77 @@ bool QgsServerProjectUtils::wmsInfoFormatSia2045( const QgsProject &project ) return false; } +bool QgsServerProjectUtils::wmsFeatureInfoAddWktGeometry( const QgsProject &project ) +{ + QString wktGeom = project.readEntry( QStringLiteral( "WMSAddWktGeometry" ), QStringLiteral( "/" ), "" ); + + if ( wktGeom.compare( QLatin1String( "enabled" ), Qt::CaseInsensitive ) == 0 + || wktGeom.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 ) + { + return true; + } + return false; +} + +bool QgsServerProjectUtils::wmsFeatureInfoSegmentizeWktGeometry( const QgsProject &project ) +{ + QString segmGeom = project.readEntry( QStringLiteral( "WMSSegmentizeFeatureInfoGeometry" ), QStringLiteral( "/" ), "" ); + + if ( segmGeom.compare( QLatin1String( "enabled" ), Qt::CaseInsensitive ) == 0 + || segmGeom.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 ) + { + return true; + } + return false; +} + +int QgsServerProjectUtils::wmsFeatureInfoPrecision( const QgsProject &project ) +{ + return project.readNumEntry( QStringLiteral( "WMSPrecision" ), QStringLiteral( "/" ), 6 ); +} + +QString QgsServerProjectUtils::wmsFeatureInfoDocumentElement( const QgsProject &project ) +{ + return project.readEntry( QStringLiteral( "WMSFeatureInfoDocumentElement" ), QStringLiteral( "/" ), "" ); +} + +QString QgsServerProjectUtils::wmsFeatureInfoDocumentElementNs( const QgsProject &project ) +{ + return project.readEntry( QStringLiteral( "WMSFeatureInfoDocumentElementNS" ), QStringLiteral( "/" ), "" ); +} + +QString QgsServerProjectUtils::wmsFeatureInfoSchema( const QgsProject &project ) +{ + return project.readEntry( QStringLiteral( "WMSFeatureInfoSchema" ), QStringLiteral( "/" ), "" ); +} + +QHash QgsServerProjectUtils::wmsFeatureInfoLayerAliasMap( const QgsProject &project ) +{ + QHash aliasMap; + + //WMSFeatureInfoAliasLayers + QStringList aliasLayerStringList = project.readListEntry( QStringLiteral( "WMSFeatureInfoAliasLayers" ), QStringLiteral( "/value" ), QStringList() ); + if ( aliasLayerStringList.isEmpty() ) + { + return aliasMap; + } + + //WMSFeatureInfoLayerAliases + QStringList layerAliasStringList = project.readListEntry( QStringLiteral( "WMSFeatureInfoLayerAliases" ), QStringLiteral( "/value" ), QStringList() ); + if ( layerAliasStringList.isEmpty() ) + { + return aliasMap; + } + + int nMapEntries = qMin( aliasLayerStringList.size(), layerAliasStringList.size() ); + for ( int i = 0; i < nMapEntries; ++i ) + { + aliasMap.insert( aliasLayerStringList.at( i ), layerAliasStringList.at( i ) ); + } + + return aliasMap; +} + bool QgsServerProjectUtils::wmsInspireActivate( const QgsProject &project ) { return project.readBoolEntry( QStringLiteral( "WMSInspire" ), QStringLiteral( "/activated" ) ); diff --git a/src/server/qgsserverprojectutils.h b/src/server/qgsserverprojectutils.h index 8eaf1074f4b..f550a44dad0 100644 --- a/src/server/qgsserverprojectutils.h +++ b/src/server/qgsserverprojectutils.h @@ -132,6 +132,48 @@ namespace QgsServerProjectUtils */ SERVER_EXPORT bool wmsInfoFormatSia2045( const QgsProject &project ); + /** Returns if the geometry is displayed as Well Known Text in GetFeatureInfo request. + * \param project the QGIS project + * \returns if the geometry is displayed as Well Known Text in GetFeatureInfo request. + */ + SERVER_EXPORT bool wmsFeatureInfoAddWktGeometry( const QgsProject &project ); + + /** Returns if the geometry has to be segmentize in GetFeatureInfo request. + * \param project the QGIS project + * \returns if the geometry has to be segmentize in GetFeatureInfo request. + */ + SERVER_EXPORT bool wmsFeatureInfoSegmentizeWktGeometry( const QgsProject &project ); + + /** Returns the geometry precision for GetFeatureInfo request. + * \param project the QGIS project + * \returns the geometry precision for GetFeatureInfo request. + */ + SERVER_EXPORT int wmsFeatureInfoPrecision( const QgsProject &project ); + + /** Returns the document element name for XML GetFeatureInfo request. + * \param project the QGIS project + * \returns the document element name for XML GetFeatureInfo request. + */ + SERVER_EXPORT QString wmsFeatureInfoDocumentElement( const QgsProject &project ); + + /** Returns the document element namespace for XML GetFeatureInfo request. + * \param project the QGIS project + * \returns the document element namespace for XML GetFeatureInfo request. + */ + SERVER_EXPORT QString wmsFeatureInfoDocumentElementNs( const QgsProject &project ); + + /** Returns the schema URL for XML GetFeatureInfo request. + * \param project the QGIS project + * \returns the schema URL for XML GetFeatureInfo request. + */ + SERVER_EXPORT QString wmsFeatureInfoSchema( const QgsProject &project ); + + /** Returns the mapping between layer name and wms layer name for GetFeatureInfo request. + * \param project the QGIS project + * \returns the mapping between layer name and wms layer name for GetFeatureInfo request. + */ + SERVER_EXPORT QHash wmsFeatureInfoLayerAliasMap( const QgsProject &project ); + /** Returns if Inspire is activated. * \param project the QGIS project * \returns if Inspire is activated. diff --git a/src/server/services/wms/qgswmsparameters.cpp b/src/server/services/wms/qgswmsparameters.cpp index 90549ea7844..bcd905e2b27 100644 --- a/src/server/services/wms/qgswmsparameters.cpp +++ b/src/server/services/wms/qgswmsparameters.cpp @@ -191,6 +191,41 @@ namespace QgsWms }; save( pFormat ); + const Parameter pInfoFormat = { ParameterName::INFO_FORMAT, + QVariant::String, + QVariant( "" ), + QVariant() + }; + save( pInfoFormat ); + + const Parameter pI = { ParameterName::I, + QVariant::Int, + QVariant( -1 ), + QVariant() + }; + save( pI ); + + const Parameter pJ = { ParameterName::J, + QVariant::Int, + QVariant( -1 ), + QVariant() + }; + save( pJ ); + + const Parameter pX = { ParameterName::X, + QVariant::Int, + QVariant( -1 ), + QVariant() + }; + save( pX ); + + const Parameter pY = { ParameterName::Y, + QVariant::Int, + QVariant( -1 ), + QVariant() + }; + save( pY ); + const Parameter pRule = { ParameterName::RULE, QVariant::String, QVariant( "" ), @@ -254,6 +289,20 @@ namespace QgsWms }; save( pLayers ); + const Parameter pQueryLayers = { ParameterName::QUERY_LAYERS, + QVariant::String, + QVariant( "" ), + QVariant() + }; + save( pQueryLayers ); + + const Parameter pFeatureCount = { ParameterName::FEATURE_COUNT, + QVariant::Int, + QVariant( 1 ), + QVariant() + }; + save( pFeatureCount ); + const Parameter pLayerTitle = { ParameterName::LAYERTITLE, QVariant::Bool, QVariant( true ), @@ -324,12 +373,26 @@ namespace QgsWms }; save( pFilter ); + const Parameter pFilterGeom = { ParameterName::FILTER_GEOM, + QVariant::String, + QVariant( "" ), + QVariant() + }; + save( pFilterGeom ); + const Parameter pSelection = { ParameterName::SELECTION, QVariant::String, QVariant( "" ), QVariant() }; save( pSelection ); + + const Parameter pWmsPrecision = { ParameterName::WMS_PRECISION, + QVariant::Int, + QVariant( -1 ), + QVariant() + }; + save( pWmsPrecision ); } QgsWmsParameters::QgsWmsParameters( const QgsServerRequest::Parameters ¶meters ) @@ -341,6 +404,7 @@ namespace QgsWms { mRequestParameters = parameters; + log( "load WMS Request parameters:" ); const QMetaEnum metaEnum( QMetaEnum::fromType() ); foreach ( QString key, parameters.keys() ) { @@ -351,6 +415,7 @@ namespace QgsWms if ( value.canConvert( mParameters[name].mType ) ) { mParameters[name].mValue = value; + log( " - " + key + " : " + parameters[key] ); } else { @@ -628,9 +693,12 @@ namespace QgsWms QgsWmsParameters::Format QgsWmsParameters::format() const { - Format f = Format::PNG; QString fStr = formatAsString(); + if ( fStr.isEmpty() ) + return Format::NONE; + + Format f = Format::PNG; if ( fStr.compare( QLatin1String( "jpg" ), Qt::CaseInsensitive ) == 0 || fStr.compare( QLatin1String( "jpeg" ), Qt::CaseInsensitive ) == 0 || fStr.compare( QLatin1String( "image/jpeg" ), Qt::CaseInsensitive ) == 0 ) @@ -639,6 +707,69 @@ namespace QgsWms return f; } + QString QgsWmsParameters::infoFormatAsString() const + { + return value( ParameterName::INFO_FORMAT ).toString(); + } + + QgsWmsParameters::Format QgsWmsParameters::infoFormat() const + { + QString fStr = infoFormatAsString(); + + if ( fStr.isEmpty() ) + return Format::NONE; + + Format f = Format::TEXT; + if ( fStr.startsWith( QLatin1String( "text/xml" ), Qt::CaseInsensitive ) ) + f = Format::XML; + else if ( fStr.startsWith( QLatin1String( "text/html" ), Qt::CaseInsensitive ) ) + f = Format::HTML; + else if ( fStr.startsWith( QLatin1String( "application/vnd.ogc.gml" ), Qt::CaseInsensitive ) ) + f = Format::GML; + + return f; + } + + QString QgsWmsParameters::i() const + { + return value( ParameterName::I ).toString(); + } + + QString QgsWmsParameters::j() const + { + return value( ParameterName::J ).toString(); + } + + int QgsWmsParameters::iAsInt() const + { + return toInt( ParameterName::I ); + } + + int QgsWmsParameters::jAsInt() const + { + return toInt( ParameterName::J ); + } + + QString QgsWmsParameters::x() const + { + return value( ParameterName::X ).toString(); + } + + QString QgsWmsParameters::y() const + { + return value( ParameterName::Y ).toString(); + } + + int QgsWmsParameters::xAsInt() const + { + return toInt( ParameterName::X ); + } + + int QgsWmsParameters::yAsInt() const + { + return toInt( ParameterName::Y ); + } + QString QgsWmsParameters::rule() const { return value( ParameterName::RULE ).toString(); @@ -674,6 +805,16 @@ namespace QgsWms return toBool( ParameterName::SHOWFEATURECOUNT ); } + QString QgsWmsParameters::featureCount() const + { + return value( ParameterName::FEATURE_COUNT ).toString(); + } + + int QgsWmsParameters::featureCountAsInt() const + { + return toInt( ParameterName::FEATURE_COUNT ); + } + QString QgsWmsParameters::boxSpace() const { return value( ParameterName::BOXSPACE ).toString(); @@ -960,6 +1101,16 @@ namespace QgsWms return toFloatList( highlightLabelBufferSize(), ParameterName::HIGHLIGHT_LABELBUFFERSIZE ); } + QString QgsWmsParameters::wmsPrecision() const + { + return value( ParameterName::WMS_PRECISION ).toString(); + } + + int QgsWmsParameters::wmsPrecisionAsInt() const + { + return toInt( ParameterName::WMS_PRECISION ); + } + QString QgsWmsParameters::sld() const { return value( ParameterName::SLD ).toString(); @@ -970,6 +1121,11 @@ namespace QgsWms return toStringList( ParameterName::FILTER, ';' ); } + QString QgsWmsParameters::filterGeom() const + { + return value( ParameterName::FILTER_GEOM ).toString(); + } + QStringList QgsWmsParameters::selections() const { return toStringList( ParameterName::SELECTION ); @@ -992,6 +1148,11 @@ namespace QgsWms return layer << layers; } + QStringList QgsWmsParameters::queryLayersNickname() const + { + return toStringList( ParameterName::QUERY_LAYERS ); + } + QStringList QgsWmsParameters::allStyles() const { QStringList style = value( ParameterName::STYLE ).toString().split( ",", QString::SkipEmptyParts ); diff --git a/src/server/services/wms/qgswmsparameters.h b/src/server/services/wms/qgswmsparameters.h index 57bb800f4fa..ecb49e8dadd 100644 --- a/src/server/services/wms/qgswmsparameters.h +++ b/src/server/services/wms/qgswmsparameters.h @@ -86,6 +86,8 @@ namespace QgsWms LAYERS, LAYERSPACE, LAYERTITLESPACE, + QUERY_LAYERS, + FEATURE_COUNT, SHOWFEATURECOUNT, STYLE, STYLES, @@ -95,7 +97,13 @@ namespace QgsWms OPACITIES, SLD, FILTER, + FILTER_GEOM, FORMAT, + INFO_FORMAT, + I, + J, + X, + Y, RULE, RULELABEL, SCALE, @@ -108,7 +116,8 @@ namespace QgsWms HIGHLIGHT_LABELWEIGHT, HIGHLIGHT_LABELCOLOR, HIGHLIGHT_LABELBUFFERCOLOR, - HIGHLIGHT_LABELBUFFERSIZE + HIGHLIGHT_LABELBUFFERSIZE, + WMS_PRECISION }; Q_ENUM( ParameterName ) @@ -116,7 +125,11 @@ namespace QgsWms { NONE, JPG, - PNG + PNG, + TEXT, + XML, + HTML, + GML }; struct Parameter @@ -203,6 +216,11 @@ namespace QgsWms */ QStringList filters() const; + /** Returns the filter geometry found in FILTER_GEOM parameter. + * \returns the filter geometry as Well Known Text. + */ + QString filterGeom() const; + /** Returns the list of opacities found in OPACITIES parameter. * \returns the list of opacities in string */ @@ -221,6 +239,11 @@ namespace QgsWms */ QStringList allLayersNickname() const; + /** Returns nickname of layers found in QUERY_LAYERS parameter. + * \returns nickname of layers + */ + QStringList queryLayersNickname() const; + /** Returns styles found in STYLE and STYLES parameters. * \returns name of styles */ @@ -242,6 +265,69 @@ namespace QgsWms */ Format format() const; + /** Returns INFO_FORMAT parameter as a string. + * \returns INFO_FORMAT parameter as string + */ + QString infoFormatAsString() const; + + /** Returns infoFormat. If the INFO_FORMAT parameter is not used, then the + * default value is text/plain. + * \returns infoFormat + */ + Format infoFormat() const; + + /** Returns I parameter or an empty string if not defined. + * \returns i parameter + */ + QString i() const; + + /** Returns I parameter as an int or its default value if not + * defined. An exception is raised if I is defined and cannot be + * converted. + * \returns i parameter + * \throws QgsBadRequestException + */ + int iAsInt() const; + + /** Returns J parameter or an empty string if not defined. + * \returns j parameter + */ + QString j() const; + + /** Returns J parameter as an int or its default value if not + * defined. An exception is raised if J is defined and cannot be + * converted. + * \returns j parameter + * \throws QgsBadRequestException + */ + int jAsInt() const; + + /** Returns X parameter or an empty string if not defined. + * \returns x parameter + */ + QString x() const; + + /** Returns X parameter as an int or its default value if not + * defined. An exception is raised if X is defined and cannot be + * converted. + * \returns x parameter + * \throws QgsBadRequestException + */ + int xAsInt() const; + + /** Returns Y parameter or an empty string if not defined. + * \returns y parameter + */ + QString y() const; + + /** Returns Y parameter as an int or its default value if not + * defined. An exception is raised if Y is defined and cannot be + * converted. + * \returns j parameter + * \throws QgsBadRequestException + */ + int yAsInt() const; + /** Returns RULE parameter or an empty string if none is defined * \returns RULE parameter or an empty string if none is defined */ @@ -271,6 +357,18 @@ namespace QgsWms */ bool showFeatureCountAsBool() const; + /** Returns FEATURE_COUNT parameter or an empty string if none is defined + * \returns FEATURE_COUNT parameter or an empty string if none is defined + */ + QString featureCount() const; + + /** Returns FEATURE_COUNT as an integer. An exception is raised if an invalid + * parameter is found. + * \returns FeatureCount + * \throws QgsBadRequestException + */ + int featureCountAsInt() const; + /** Returns SCALE parameter or an empty string if none is defined * \returns SCALE parameter or an empty string if none is defined */ @@ -595,6 +693,19 @@ namespace QgsWms */ QList highlightLabelBufferColorAsColor() const; + /** Returns WMS_PRECISION parameter or an empty string if not defined. + * \returns wms precision parameter + */ + QString wmsPrecision() const; + + /** Returns WMS_PRECISION parameter as an int or its default value if not + * defined. An exception is raised if WMS_PRECISION is defined and cannot be + * converted. + * \returns wms precision parameter + * \throws QgsBadRequestException + */ + int wmsPrecisionAsInt() const; + private: QString name( ParameterName name ) const; void raiseError( ParameterName name ) const; diff --git a/src/server/services/wms/qgswmsrenderer.cpp b/src/server/services/wms/qgswmsrenderer.cpp index d31a938451f..cdcbdf93218 100644 --- a/src/server/services/wms/qgswmsrenderer.cpp +++ b/src/server/services/wms/qgswmsrenderer.cpp @@ -644,131 +644,161 @@ namespace QgsWms QDomDocument QgsRenderer::getFeatureInfo( const QString &version ) { - if ( !mConfigParser ) + // Verifying Mandatory parameters + // The INFO_FORMAT parameter is Mandatory + if ( mWmsParameters.infoFormat() == QgsWmsParameters::Format::NONE ) { - throw QgsException( QStringLiteral( "No config parser" ) ); + throw QgsBadRequestException( QStringLiteral( "ParameterMissing" ), + QStringLiteral( "INFO_FORMAT parameter is required for GetFeatureInfo" ) ); } - QDomDocument result; - QStringList layersList, stylesList; - bool conversionSuccess; - - for ( auto it = mParameters.constBegin(); it != mParameters.constEnd(); ++it ) + // The QUERY_LAYERS parameter is Mandatory + QStringList queryLayers = mWmsParameters.queryLayersNickname(); + if ( queryLayers.isEmpty() ) { - QgsMessageLog::logMessage( QStringLiteral( "%1 // %2" ).arg( it.key(), it.value() ) ); + throw QgsBadRequestException( QStringLiteral( "ParameterMissing" ), + QStringLiteral( "QUERY_LAYERS parameter is required for GetFeatureInfo" ) ); } - readLayersAndStyles( mParameters, layersList, stylesList ); - initializeSLDParser( layersList, stylesList ); + // The I/J parameters are Mandatory if they are not replaced by X/Y or FILTER or FILTER_GEOM + bool ijDefined = false; + if ( !mWmsParameters.i().isEmpty() && !mWmsParameters.j().isEmpty() ) + ijDefined = true; + bool xyDefined = false; + if ( !mWmsParameters.x().isEmpty() && !mWmsParameters.y().isEmpty() ) + xyDefined = true; + + bool filtersDefined = false; + if ( !mWmsParameters.filters().isEmpty() ) + filtersDefined = true; + + bool filterGeomDefined = false; + if ( !mWmsParameters.filterGeom().isEmpty() ) + filterGeomDefined = true; + + if ( !ijDefined && !xyDefined && !filtersDefined && !filterGeomDefined ) + { + throw QgsBadRequestException( QStringLiteral( "ParameterMissing" ), + QStringLiteral( "I/J parameters are required for GetFeatureInfo" ) ); + } + + // get layers parameters + QList layers; + QList params = mWmsParameters.layersParameters(); + + // init layer restorer before doing anything + std::unique_ptr restorer; + restorer.reset( new QgsLayerRestorer( mNicknameLayers.values() ) ); + + // init stylized layers according to LAYERS/STYLES or SLD + QString sld = mWmsParameters.sld(); + if ( !sld.isEmpty() ) + layers = sldStylizedLayers( sld ); + else + layers = stylizedLayers( params ); + + // create the mapSettings and the output image QgsMapSettings mapSettings; std::unique_ptr outputImage( createImage() ); + // configure map settings (background, DPI, ...) configureMapSettings( outputImage.get(), mapSettings ); + QgsMessageLog::logMessage( "mapSettings.destinationCrs(): " + mapSettings.destinationCrs().authid() ); QgsMessageLog::logMessage( "mapSettings.extent(): " + mapSettings.extent().toString() ); QgsMessageLog::logMessage( QStringLiteral( "mapSettings width = %1 height = %2" ).arg( mapSettings.outputSize().width() ).arg( mapSettings.outputSize().height() ) ); QgsMessageLog::logMessage( QStringLiteral( "mapSettings.mapUnitsPerPixel() = %1" ).arg( mapSettings.mapUnitsPerPixel() ) ); - //find out the current scale denominator and set it to the SLD parser QgsScaleCalculator scaleCalc( ( outputImage->logicalDpiX() + outputImage->logicalDpiY() ) / 2, mapSettings.destinationCrs().mapUnits() ); QgsRectangle mapExtent = mapSettings.extent(); double scaleDenominator = scaleCalc.calculate( mapExtent, outputImage->width() ); - mConfigParser->setScaleDenominator( scaleDenominator ); - //read FEATURE_COUNT - int featureCount = 1; - if ( mParameters.contains( QStringLiteral( "FEATURE_COUNT" ) ) ) + // remove unwanted layers (restricted layers, ...) + removeUnwantedLayers( layers, scaleDenominator ); + // remove non identifiable layers + QStringList nonIdentifiableLayers = mProject->nonIdentifiableLayers(); + if ( !nonIdentifiableLayers.isEmpty() ) { - featureCount = mParameters[ QStringLiteral( "FEATURE_COUNT" )].toInt( &conversionSuccess ); - if ( !conversionSuccess ) + QList wantedLayers; + + Q_FOREACH ( QgsMapLayer *layer, layers ) { - featureCount = 1; + if ( nonIdentifiableLayers.contains( layer->id() ) ) + continue; + + wantedLayers.append( layer ); + } + + layers = wantedLayers; + } + + Q_FOREACH ( QgsMapLayer *layer, layers ) + { + Q_FOREACH ( QgsWmsParametersLayer param, params ) + { + if ( param.mNickname == layerNickname( *layer ) ) + { + checkLayerReadPermissions( layer ); + + setLayerFilter( layer, param.mFilter ); + + setLayerAccessControlFilter( layer ); + + break; + } } } - //read QUERY_LAYERS - if ( !mParameters.contains( QStringLiteral( "QUERY_LAYERS" ) ) ) + // add layers to map settings (revert order for the rendering) + std::reverse( layers.begin(), layers.end() ); + mapSettings.setLayers( layers ); + + int featureCount = mWmsParameters.featureCountAsInt(); + if ( featureCount < 1 ) { - throw QgsBadRequestException( QStringLiteral( "ParameterMissing" ), QStringLiteral( "No QUERY_LAYERS" ) ); + featureCount = 1; } - QStringList queryLayerList = mParameters[ QStringLiteral( "QUERY_LAYERS" )].split( ',', QString::SkipEmptyParts ); - if ( queryLayerList.isEmpty() ) + int i = mWmsParameters.iAsInt(); + int j = mWmsParameters.jAsInt(); + if ( xyDefined && !ijDefined ) { - throw QgsBadRequestException( QStringLiteral( "InvalidParameterValue" ), QStringLiteral( "Malformed QUERY_LAYERS" ) ); + i = mWmsParameters.xAsInt(); + j = mWmsParameters.yAsInt(); + } + int width = mWmsParameters.widthAsInt(); + int height = mWmsParameters.heightAsInt(); + if ( ( i != -1 && j != -1 && width != 0 && height != 0 ) && ( width != outputImage->width() || height != outputImage->height() ) ) + { + i *= ( outputImage->width() / ( double )width ); + j *= ( outputImage->height() / ( double )height ); } - //read I,J resp. X,Y - QString iString = mParameters.value( QStringLiteral( "I" ), mParameters.value( QStringLiteral( "X" ) ) ); - int i = iString.toInt( &conversionSuccess ); - if ( !conversionSuccess ) - { - i = -1; - } - - QString jString = mParameters.value( QStringLiteral( "J" ), mParameters.value( QStringLiteral( "Y" ) ) ); - int j = jString.toInt( &conversionSuccess ); - if ( !conversionSuccess ) - { - j = -1; - } - - //read FILTER_GEOM - std::unique_ptr filterGeom; - if ( mParameters.contains( QStringLiteral( "FILTER_GEOM" ) ) ) - { - filterGeom.reset( new QgsGeometry( QgsGeometry::fromWkt( mParameters.value( QStringLiteral( "FILTER_GEOM" ) ) ) ) ); - } - - //In case the output image is distorted (WIDTH/HEIGHT ratio not equal to BBOX width/height), I and J need to be adapted as well - int widthParam = mParameters.value( "WIDTH", "-1" ).toInt(); - int heightParam = mParameters.value( "HEIGHT", "-1" ).toInt(); - if ( ( i != -1 && j != -1 && widthParam != -1 && heightParam != -1 ) && ( widthParam != outputImage->width() || heightParam != outputImage->height() ) ) - { - i *= ( outputImage->width() / ( double )widthParam ); - j *= ( outputImage->height() / ( double )heightParam ); - } - - //Normally, I/J or X/Y are mandatory parameters. - //However, in order to make attribute only queries via the FILTER parameter, it is allowed to skip them if the FILTER parameter is there - + // init search variables std::unique_ptr featuresRect; + std::unique_ptr filterGeom; std::unique_ptr infoPoint; - if ( i == -1 || j == -1 ) - { - if ( mParameters.contains( QStringLiteral( "FILTER" ) ) ) - { - featuresRect.reset( new QgsRectangle() ); - } - else if ( !filterGeom.get() ) - { - throw QgsBadRequestException( QStringLiteral( "ParameterMissing" ), - QStringLiteral( "I/J parameters are required for GetFeatureInfo" ) ); - } - } - else + if ( i != -1 && j != -1 ) { infoPoint.reset( new QgsPointXY() ); infoPointToMapCoordinates( i, j, infoPoint.get(), mapSettings ); } + else if ( filtersDefined ) + { + featuresRect.reset( new QgsRectangle() ); + } + else if ( filterGeomDefined ) + { + filterGeom.reset( new QgsGeometry( QgsGeometry::fromWkt( mWmsParameters.filterGeom() ) ) ); + } - //get the layer registered in QgsMapLayerRegistry and apply possible filters - ( void )layerSet( layersList, stylesList, mapSettings.destinationCrs() ); - - //scoped pointer to restore all original layer filters (subsetStrings) when pointer goes out of scope - //there's LOTS of potential exit paths here, so we avoid having to restore the filters manually - std::unique_ptr< QgsOWSServerFilterRestorer > filterRestorer( new QgsOWSServerFilterRestorer( mAccessControl ) ); - applyRequestedLayerFilters( layersList, mapSettings, filterRestorer->originalFilters() ); - -#ifdef HAVE_SERVER_PYTHON_PLUGINS - applyAccessControlLayersFilters( layersList, filterRestorer->originalFilters() ); -#endif + QDomDocument result; QDomElement getFeatureInfoElement; - QString infoFormat = mParameters.value( QStringLiteral( "INFO_FORMAT" ) ); - if ( infoFormat.startsWith( QLatin1String( "application/vnd.ogc.gml" ) ) ) + QgsWmsParameters::Format infoFormat = mWmsParameters.infoFormat(); + if ( infoFormat == QgsWmsParameters::Format::GML ) { getFeatureInfoElement = result.createElement( QStringLiteral( "wfs:FeatureCollection" ) ); getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:wfs" ), QStringLiteral( "http://www.opengis.net/wfs" ) ); @@ -782,18 +812,22 @@ namespace QgsWms } else { - QString featureInfoElemName = mConfigParser->featureInfoDocumentElement( QStringLiteral( "GetFeatureInfoResponse" ) ); - QString featureInfoElemNS = mConfigParser->featureInfoDocumentElementNS(); - if ( featureInfoElemNS.isEmpty() ) + QString featureInfoElemName = QgsServerProjectUtils::wmsFeatureInfoDocumentElement( *mProject ); + if ( featureInfoElemName.isEmpty() ) + { + featureInfoElemName = QStringLiteral( "GetFeatureInfoResponse" ); + } + QString featureInfoElemNs = QgsServerProjectUtils::wmsFeatureInfoDocumentElementNs( *mProject ); + if ( featureInfoElemNs.isEmpty() ) { getFeatureInfoElement = result.createElement( featureInfoElemName ); } else { - getFeatureInfoElement = result.createElementNS( featureInfoElemNS, featureInfoElemName ); + getFeatureInfoElement = result.createElementNS( featureInfoElemNs, featureInfoElemName ); } //feature info schema - QString featureInfoSchema = mConfigParser->featureInfoSchema(); + QString featureInfoSchema = QgsServerProjectUtils::wmsFeatureInfoSchema( *mProject ); if ( !featureInfoSchema.isEmpty() ) { getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:xsi" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema-instance" ) ); @@ -802,122 +836,92 @@ namespace QgsWms } result.appendChild( getFeatureInfoElement ); - QStringList nonIdentifiableLayers = mConfigParser->identifyDisabledLayers(); - //Render context is needed to determine feature visibility for vector layers QgsRenderContext renderContext = QgsRenderContext::fromMapSettings( mapSettings ); - bool sia2045 = mConfigParser->featureInfoFormatSIA2045(); + bool sia2045 = QgsServerProjectUtils::wmsInfoFormatSia2045( *mProject ); //layers can have assigned a different name for GetCapabilities - QHash layerAliasMap = mConfigParser->featureInfoLayerAliasMap(); + QHash layerAliasMap = QgsServerProjectUtils::wmsFeatureInfoLayerAliasMap( *mProject ); - QList layerList; - QgsMapLayer *currentLayer = nullptr; - for ( auto layerIt = queryLayerList.constBegin(); layerIt != queryLayerList.constEnd(); ++layerIt ) + Q_FOREACH ( QString queryLayer, queryLayers ) { - //create maplayers from sld parser (several layers are possible in case of feature info on a group) - layerList = mConfigParser->mapLayerFromStyle( *layerIt, QLatin1String( "" ) ); - for ( auto layerListIt = layerList.begin() ; layerListIt != layerList.end(); ++layerListIt ) + Q_FOREACH ( QgsMapLayer *layer, layers ) { - currentLayer = *layerListIt; - if ( !currentLayer || nonIdentifiableLayers.contains( currentLayer->id() ) ) + if ( queryLayer == layerNickname( *layer ) ) { - continue; - } - QgsMapLayer *registeredMapLayer = QgsProject::instance()->mapLayer( currentLayer->id() ); - if ( registeredMapLayer ) - { - currentLayer = registeredMapLayer; - } - -#ifdef HAVE_SERVER_PYTHON_PLUGINS - if ( !mAccessControl->layerReadPermission( currentLayer ) ) - { - throw QgsSecurityException( QStringLiteral( "You are not allowed to access to the layer: %1" ).arg( currentLayer->name() ) ); - } -#endif - - //skip layer if not visible at current map scale - if ( scaleDenominator > 0 && !currentLayer->isInScaleRange( scaleDenominator ) ) - { - continue; - } - - //switch depending on vector or raster - QgsVectorLayer *vectorLayer = qobject_cast( currentLayer ); - - QDomElement layerElement; - if ( infoFormat.startsWith( QLatin1String( "application/vnd.ogc.gml" ) ) ) - { - layerElement = getFeatureInfoElement; - } - else - { - layerElement = result.createElement( QStringLiteral( "Layer" ) ); - QString layerName = currentLayer->name(); - if ( mConfigParser->useLayerIds() ) - layerName = currentLayer->id(); - else if ( !currentLayer->shortName().isEmpty() ) - layerName = currentLayer->shortName(); - - //check if the layer is given a different name for GetFeatureInfo output - QHash::const_iterator layerAliasIt = layerAliasMap.find( layerName ); - if ( layerAliasIt != layerAliasMap.constEnd() ) + QDomElement layerElement; + if ( infoFormat == QgsWmsParameters::Format::GML ) { - layerName = layerAliasIt.value(); + layerElement = getFeatureInfoElement; } - layerElement.setAttribute( QStringLiteral( "name" ), layerName ); - getFeatureInfoElement.appendChild( layerElement ); - if ( sia2045 ) //the name might not be unique after alias replacement + else { - layerElement.setAttribute( QStringLiteral( "id" ), currentLayer->id() ); - } - } + layerElement = result.createElement( QStringLiteral( "Layer" ) ); + QString layerName = queryLayer; - if ( vectorLayer ) - { - if ( !featureInfoFromVectorLayer( vectorLayer, infoPoint.get(), featureCount, result, layerElement, mapSettings, renderContext, version, infoFormat, featuresRect.get(), filterGeom.get() ) ) - { - continue; - } - } - else //raster layer - { - if ( infoFormat.startsWith( QLatin1String( "application/vnd.ogc.gml" ) ) ) - { - layerElement = result.createElement( QStringLiteral( "gml:featureMember" )/*wfs:FeatureMember*/ ); - getFeatureInfoElement.appendChild( layerElement ); - } - - QgsRasterLayer *rasterLayer = qobject_cast( currentLayer ); - if ( rasterLayer ) - { - if ( !infoPoint ) + //check if the layer is given a different name for GetFeatureInfo output + QHash::const_iterator layerAliasIt = layerAliasMap.find( layerName ); + if ( layerAliasIt != layerAliasMap.constEnd() ) { - continue; + layerName = layerAliasIt.value(); } - QgsPointXY layerInfoPoint = mapSettings.mapToLayerCoordinates( currentLayer, *( infoPoint.get() ) ); - if ( !featureInfoFromRasterLayer( rasterLayer, mapSettings, &layerInfoPoint, result, layerElement, version, infoFormat ) ) + + layerElement.setAttribute( QStringLiteral( "name" ), layerName ); + getFeatureInfoElement.appendChild( layerElement ); + if ( sia2045 ) //the name might not be unique after alias replacement { - continue; + layerElement.setAttribute( QStringLiteral( "id" ), layer->id() ); + } + } + + if ( layer->type() == QgsMapLayer::VectorLayer ) + { + QgsVectorLayer *vectorLayer = qobject_cast( layer ); + if ( vectorLayer ) + { + if ( !featureInfoFromVectorLayer( vectorLayer, infoPoint.get(), featureCount, result, layerElement, mapSettings, renderContext, version, mWmsParameters.infoFormatAsString(), featuresRect.get(), filterGeom.get() ) ) + { + break; + } + break; } } else { - continue; + if ( infoFormat == QgsWmsParameters::Format::GML ) + { + layerElement = result.createElement( QStringLiteral( "gml:featureMember" )/*wfs:FeatureMember*/ ); + getFeatureInfoElement.appendChild( layerElement ); + } + + QgsRasterLayer *rasterLayer = qobject_cast( layer ); + if ( rasterLayer ) + { + if ( !infoPoint ) + { + break; + } + QgsPointXY layerInfoPoint = mapSettings.mapToLayerCoordinates( layer, *( infoPoint.get() ) ); + if ( !featureInfoFromRasterLayer( rasterLayer, mapSettings, &layerInfoPoint, result, layerElement, version, mWmsParameters.infoFormatAsString() ) ) + { + break; + } + break; + } } + break; } } } if ( featuresRect ) { - if ( infoFormat.startsWith( QLatin1String( "application/vnd.ogc.gml" ) ) ) + if ( infoFormat == QgsWmsParameters::Format::GML ) { QDomElement bBoxElem = result.createElement( QStringLiteral( "gml:boundedBy" ) ); QDomElement boxElem; - int gmlVersion = infoFormat.startsWith( QLatin1String( "application/vnd.ogc.gml/3" ) ) ? 3 : 2; + int gmlVersion = mWmsParameters.infoFormatAsString().startsWith( QLatin1String( "application/vnd.ogc.gml/3" ) ) ? 3 : 2; if ( gmlVersion < 3 ) { boxElem = QgsOgcUtils::rectangleToGMLBox( featuresRect.get(), result, 8 ); @@ -947,16 +951,11 @@ namespace QgsWms } } - if ( sia2045 && infoFormat.compare( QLatin1String( "text/xml" ), Qt::CaseInsensitive ) == 0 ) + if ( sia2045 && infoFormat == QgsWmsParameters::Format::XML ) { convertFeatureInfoToSIA2045( result ); } - //force restoration of original filters - filterRestorer.reset(); - - QgsProject::instance()->removeAllMapLayers(); - return result; } @@ -1288,8 +1287,8 @@ namespace QgsWms int featureCounter = 0; layer->updateFields(); const QgsFields &fields = layer->pendingFields(); - bool addWktGeometry = mConfigParser && mConfigParser->featureInfoWithWktGeometry(); - bool segmentizeWktGeometry = mConfigParser && mConfigParser->segmentizeFeatureInfoWktGeometry(); + bool addWktGeometry = QgsServerProjectUtils::wmsFeatureInfoAddWktGeometry( *mProject ); + bool segmentizeWktGeometry = QgsServerProjectUtils::wmsFeatureInfoSegmentizeWktGeometry( *mProject ); const QSet &excludedAttributes = layer->excludeAttributesWms(); QgsFeatureRequest fReq; @@ -1385,17 +1384,13 @@ namespace QgsWms outputCrs = mapSettings.destinationCrs(); } - if ( infoFormat == QLatin1String( "application/vnd.ogc.gml" ) ) + if ( mWmsParameters.infoFormat() == QgsWmsParameters::Format::GML ) { bool withGeom = layer->wkbType() != QgsWkbTypes::NoGeometry && addWktGeometry; - int version = infoFormat.startsWith( QLatin1String( "application/vnd.ogc.gml/3" ) ) ? 3 : 2; - QString typeName = layer->name(); - if ( mConfigParser && mConfigParser->useLayerIds() ) - typeName = layer->id(); - else if ( !layer->shortName().isEmpty() ) - typeName = layer->shortName(); + int gmlVersion = infoFormat.startsWith( QLatin1String( "application/vnd.ogc.gml/3" ) ) ? 3 : 2; + QString typeName = layerNickname( *layer ); QDomElement elem = createFeatureGML( - &feature, layer, infoDocument, outputCrs, mapSettings, typeName, withGeom, version + &feature, layer, infoDocument, outputCrs, mapSettings, typeName, withGeom, gmlVersion #ifdef HAVE_SERVER_PYTHON_PLUGINS , &attributes #endif @@ -1453,14 +1448,14 @@ namespace QgsWms } //append feature bounding box to feature info xml - if ( layer->wkbType() != QgsWkbTypes::NoGeometry && hasGeometry && mConfigParser ) + if ( layer->wkbType() != QgsWkbTypes::NoGeometry && hasGeometry ) { QDomElement bBoxElem = infoDocument.createElement( QStringLiteral( "BoundingBox" ) ); bBoxElem.setAttribute( version == QLatin1String( "1.1.1" ) ? "SRS" : "CRS", outputCrs.authid() ); - bBoxElem.setAttribute( QStringLiteral( "minx" ), qgsDoubleToString( box.xMinimum(), getWMSPrecision( 8 ) ) ); - bBoxElem.setAttribute( QStringLiteral( "maxx" ), qgsDoubleToString( box.xMaximum(), getWMSPrecision( 8 ) ) ); - bBoxElem.setAttribute( QStringLiteral( "miny" ), qgsDoubleToString( box.yMinimum(), getWMSPrecision( 8 ) ) ); - bBoxElem.setAttribute( QStringLiteral( "maxy" ), qgsDoubleToString( box.yMaximum(), getWMSPrecision( 8 ) ) ); + bBoxElem.setAttribute( QStringLiteral( "minx" ), qgsDoubleToString( box.xMinimum(), getWMSPrecision() ) ); + bBoxElem.setAttribute( QStringLiteral( "maxx" ), qgsDoubleToString( box.xMaximum(), getWMSPrecision() ) ); + bBoxElem.setAttribute( QStringLiteral( "miny" ), qgsDoubleToString( box.yMinimum(), getWMSPrecision() ) ); + bBoxElem.setAttribute( QStringLiteral( "maxy" ), qgsDoubleToString( box.yMaximum(), getWMSPrecision() ) ); featureElement.appendChild( bBoxElem ); } @@ -1484,14 +1479,14 @@ namespace QgsWms { if ( QgsWkbTypes::isCurvedType( abstractGeom->wkbType() ) ) { - QgsAbstractGeometry *segmentizedGeom = abstractGeom-> segmentize(); + QgsAbstractGeometry *segmentizedGeom = abstractGeom->segmentize(); geom.setGeometry( segmentizedGeom ); } } } QDomElement geometryElement = infoDocument.createElement( QStringLiteral( "Attribute" ) ); geometryElement.setAttribute( QStringLiteral( "name" ), QStringLiteral( "geometry" ) ); - geometryElement.setAttribute( QStringLiteral( "value" ), geom.exportToWkt( getWMSPrecision( 8 ) ) ); + geometryElement.setAttribute( QStringLiteral( "value" ), geom.exportToWkt( getWMSPrecision() ) ); geometryElement.setAttribute( QStringLiteral( "type" ), QStringLiteral( "derived" ) ); featureElement.appendChild( geometryElement ); } @@ -1540,7 +1535,7 @@ namespace QgsWms attributes = layer->dataProvider()->identify( *infoPoint, QgsRaster::IdentifyFormatValue, mapSettings.extent(), mapSettings.outputSize().width(), mapSettings.outputSize().height() ).results(); } - if ( infoFormat == QLatin1String( "application/vnd.ogc.gml" ) ) + if ( mWmsParameters.infoFormat() == QgsWmsParameters::Format::GML ) { QgsFeature feature; QgsFields fields; @@ -1554,14 +1549,10 @@ namespace QgsWms feature.setFields( fields ); QgsCoordinateReferenceSystem layerCrs = layer->crs(); - int version = infoFormat.startsWith( QLatin1String( "application/vnd.ogc.gml/3" ) ) ? 3 : 2; - QString typeName = layer->name(); - if ( mConfigParser && mConfigParser->useLayerIds() ) - typeName = layer->id(); - else if ( !layer->shortName().isEmpty() ) - typeName = layer->shortName(); + int gmlVersion = infoFormat.startsWith( QLatin1String( "application/vnd.ogc.gml/3" ) ) ? 3 : 2; + QString typeName = layerNickname( *layer ); QDomElement elem = createFeatureGML( - &feature, nullptr, infoDocument, layerCrs, mapSettings, typeName, false, version, nullptr ); + &feature, nullptr, infoDocument, layerCrs, mapSettings, typeName, false, gmlVersion, nullptr ); layerElement.appendChild( elem ); } else @@ -2263,11 +2254,11 @@ namespace QgsWms QDomElement boxElem; if ( version < 3 ) { - boxElem = QgsOgcUtils::rectangleToGMLBox( &box, doc, 8 ); + boxElem = QgsOgcUtils::rectangleToGMLBox( &box, doc, getWMSPrecision() ); } else { - boxElem = QgsOgcUtils::rectangleToGMLEnvelope( &box, doc, 8 ); + boxElem = QgsOgcUtils::rectangleToGMLEnvelope( &box, doc, getWMSPrecision() ); } if ( crs.isValid() ) @@ -2291,11 +2282,11 @@ namespace QgsWms QDomElement gmlElem; if ( version < 3 ) { - gmlElem = QgsOgcUtils::geometryToGML( geom, doc, 8 ); + gmlElem = QgsOgcUtils::geometryToGML( geom, doc, getWMSPrecision() ); } else { - gmlElem = QgsOgcUtils::geometryToGML( geom, doc, QStringLiteral( "GML3" ), 8 ); + gmlElem = QgsOgcUtils::geometryToGML( geom, doc, QStringLiteral( "GML3" ), getWMSPrecision() ); } if ( !gmlElem.isNull() ) @@ -2388,27 +2379,18 @@ namespace QgsWms return imageQuality; } - int QgsRenderer::getWMSPrecision( int defaultValue = 8 ) const + int QgsRenderer::getWMSPrecision() const { - // First taken from QGIS project - int WMSPrecision = mConfigParser->wmsPrecision(); + // First taken from QGIS project and the default value is 6 + int WMSPrecision = QgsServerProjectUtils::wmsFeatureInfoPrecision( *mProject ); // Then checks if a parameter is given, if so use it instead - if ( mParameters.contains( QStringLiteral( "WMS_PRECISION" ) ) ) - { - bool conversionSuccess; - int WMSPrecisionParameter; - WMSPrecisionParameter = mParameters[ QStringLiteral( "WMS_PRECISION" )].toInt( &conversionSuccess ); - if ( conversionSuccess ) - { - WMSPrecision = WMSPrecisionParameter; - } - } - if ( WMSPrecision == -1 ) - { - WMSPrecision = defaultValue; - } - return WMSPrecision; + int WMSPrecisionParameter = mWmsParameters.wmsPrecisionAsInt(); + + if ( WMSPrecisionParameter > -1 ) + return WMSPrecisionParameter; + else + return WMSPrecision; } QgsRectangle QgsRenderer::featureInfoSearchRect( QgsVectorLayer *ml, const QgsMapSettings &mapSettings, const QgsRenderContext &rct, const QgsPointXY &infoPoint ) const diff --git a/src/server/services/wms/qgswmsrenderer.h b/src/server/services/wms/qgswmsrenderer.h index 70be8a01e99..19f83f4a3a2 100644 --- a/src/server/services/wms/qgswmsrenderer.h +++ b/src/server/services/wms/qgswmsrenderer.h @@ -334,7 +334,7 @@ namespace QgsWms int getImageQuality() const; //! Return precision to use for GetFeatureInfo request - int getWMSPrecision( int defaultValue ) const; + int getWMSPrecision() const; }; diff --git a/tests/src/python/test_qgsserver_accesscontrol.py b/tests/src/python/test_qgsserver_accesscontrol.py index 4f01a715743..6299c50d617 100644 --- a/tests/src/python/test_qgsserver_accesscontrol.py +++ b/tests/src/python/test_qgsserver_accesscontrol.py @@ -453,6 +453,33 @@ class TestQgsServerAccessControl(unittest.TestCase): str(response).find("red") != -1, # spellok "No color in result of GetFeatureInfo\n%s" % response) + response, headers = self._get_restricted(query_string) + self.assertEqual( + headers.get("Content-Type"), "text/xml; charset=utf-8", + "Content type for GetFeatureInfo is wrong: %s" % headers.get("Content-Type")) + self.assertTrue( + str(response).find('') != -1, + "Not allowed do a GetFeatureInfo on Country" + ) + + query_string = "&".join(["%s=%s" % i for i in list({ + "MAP": urllib.parse.quote(self.projectPath), + "SERVICE": "WMS", + "VERSION": "1.1.1", + "REQUEST": "GetFeatureInfo", + "LAYERS": "Hello", + "QUERY_LAYERS": "Hello", + "STYLES": "", + "FORMAT": "image/png", + "BBOX": "-16817707,-6318936.5,5696513,16195283.5", + "HEIGHT": "500", + "WIDTH": "500", + "SRS": "EPSG:3857", + "FEATURE_COUNT": "10", + "INFO_FORMAT": "application/vnd.ogc.gml", + "X": "56", + "Y": "144" + }.items())]) response, headers = self._get_restricted(query_string) self.assertTrue( str(response).find("1") != -1, @@ -1053,6 +1080,33 @@ class TestQgsServerAccessControl(unittest.TestCase): str(response).find("1") != -1, "No good result in GetFeatureInfo Hello/1\n%s" % response) + response, headers = self._get_restricted(query_string) + self.assertEqual( + headers.get("Content-Type"), "text/xml; charset=utf-8", + "Content type for GetFeatureInfo is wrong: %s" % headers.get("Content-Type")) + self.assertTrue( + str(response).find('') != -1, + "Not allowed do a GetFeatureInfo on Country" + ) + + query_string = "&".join(["%s=%s" % i for i in list({ + "SERVICE": "WMS", + "VERSION": "1.1.1", + "REQUEST": "GetFeatureInfo", + "LAYERS": "Hello_SubsetString", + "QUERY_LAYERS": "Hello_SubsetString", + "STYLES": "", + "FORMAT": "image/png", + "BBOX": "-16817707,-6318936.5,5696513,16195283.5", + "HEIGHT": "500", + "WIDTH": "500", + "SRS": "EPSG:3857", + "FEATURE_COUNT": "10", + "INFO_FORMAT": "application/vnd.ogc.gml", + "X": "56", + "Y": "144", + "MAP": urllib.parse.quote(self.projectPath) + }.items())]) response, headers = self._get_restricted(query_string) self.assertTrue( str(response).find("") != -1, diff --git a/tests/src/python/test_qgsserver_wms.py b/tests/src/python/test_qgsserver_wms.py index fbc08d6a23d..eec680b4ed3 100644 --- a/tests/src/python/test_qgsserver_wms.py +++ b/tests/src/python/test_qgsserver_wms.py @@ -69,7 +69,16 @@ class TestQgsServerWMS(QgsServerTestBase): for request in ('GetCapabilities', 'GetProjectSettings', 'GetContext'): self.wms_request_compare(request) - # Test getfeatureinfo response + # Test getfeatureinfo response xml (default info_format) + self.wms_request_compare('GetFeatureInfo', + '&layers=testlayer%20%C3%A8%C3%A9&styles=&' + + 'info_format=text%2Fxml&transparent=true&' + + 'width=600&height=400&srs=EPSG%3A3857&bbox=913190.6389747962%2C' + + '5606005.488876367%2C913235.426296057%2C5606035.347090538&' + + 'query_layers=testlayer%20%C3%A8%C3%A9&X=190&Y=320', + 'wms_getfeatureinfo-text-xml') + + # Test getfeatureinfo response html self.wms_request_compare('GetFeatureInfo', '&layers=testlayer%20%C3%A8%C3%A9&styles=&' + 'info_format=text%2Fhtml&transparent=true&' + @@ -78,10 +87,10 @@ class TestQgsServerWMS(QgsServerTestBase): 'query_layers=testlayer%20%C3%A8%C3%A9&X=190&Y=320', 'wms_getfeatureinfo-text-html') - # Test getfeatureinfo default info_format + # Test getfeatureinfo response text self.wms_request_compare('GetFeatureInfo', '&layers=testlayer%20%C3%A8%C3%A9&styles=&' + - 'transparent=true&' + + 'info_format=text%2Fplain&transparent=true&' + 'width=600&height=400&srs=EPSG%3A3857&bbox=913190.6389747962%2C' + '5606005.488876367%2C913235.426296057%2C5606035.347090538&' + 'query_layers=testlayer%20%C3%A8%C3%A9&X=190&Y=320', diff --git a/tests/testdata/qgis_server/wms_getfeatureinfo-text-xml.txt b/tests/testdata/qgis_server/wms_getfeatureinfo-text-xml.txt new file mode 100644 index 00000000000..591eea56f80 --- /dev/null +++ b/tests/testdata/qgis_server/wms_getfeatureinfo-text-xml.txt @@ -0,0 +1,14 @@ +***** +Content-Type: text/xml; charset=utf-8 + + + + + + + + + + + +