Merge pull request #4846 from rldhont/server-wms-configparser-getfeatureinfo

[Server] WMS GetFeatureInfo refactoring
This commit is contained in:
rldhont 2017-07-19 10:00:01 +02:00 committed by GitHub
commit 08c06def1b
11 changed files with 1025 additions and 478 deletions

View File

@ -145,6 +145,62 @@ namespace QgsServerProjectUtils
:rtype: bool :rtype: bool
%End %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<QString, QString> 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<str, QString>
%End
bool wmsInspireActivate( const QgsProject &project ); bool wmsInspireActivate( const QgsProject &project );
%Docstring %Docstring
Returns if Inspire is activated. Returns if Inspire is activated.

View File

@ -117,6 +117,77 @@ bool QgsServerProjectUtils::wmsInfoFormatSia2045( const QgsProject &project )
return false; 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<QString, QString> QgsServerProjectUtils::wmsFeatureInfoLayerAliasMap( const QgsProject &project )
{
QHash<QString, QString> 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 ) bool QgsServerProjectUtils::wmsInspireActivate( const QgsProject &project )
{ {
return project.readBoolEntry( QStringLiteral( "WMSInspire" ), QStringLiteral( "/activated" ) ); return project.readBoolEntry( QStringLiteral( "WMSInspire" ), QStringLiteral( "/activated" ) );

View File

@ -132,6 +132,48 @@ namespace QgsServerProjectUtils
*/ */
SERVER_EXPORT bool wmsInfoFormatSia2045( const QgsProject &project ); 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<QString, QString> wmsFeatureInfoLayerAliasMap( const QgsProject &project );
/** Returns if Inspire is activated. /** Returns if Inspire is activated.
* \param project the QGIS project * \param project the QGIS project
* \returns if Inspire is activated. * \returns if Inspire is activated.

View File

@ -25,136 +25,6 @@
namespace QgsWms namespace QgsWms
{ {
void writeInfoResponse( QDomDocument &infoDoc, QgsServerResponse &response, const QString &infoFormat )
{
QByteArray ba;
QgsMessageLog::logMessage( "Info format is:" + infoFormat );
if ( infoFormat == QLatin1String( "text/xml" ) || infoFormat.startsWith( QLatin1String( "application/vnd.ogc.gml" ) ) )
{
ba = infoDoc.toByteArray();
}
else if ( infoFormat == QLatin1String( "text/plain" ) || infoFormat == QLatin1String( "text/html" ) )
{
//create string
QString featureInfoString;
if ( infoFormat == QLatin1String( "text/plain" ) )
{
featureInfoString.append( "GetFeatureInfo results\n" );
featureInfoString.append( "\n" );
}
else if ( infoFormat == QLatin1String( "text/html" ) )
{
featureInfoString.append( "<HEAD>\n" );
featureInfoString.append( "<TITLE> GetFeatureInfo results </TITLE>\n" );
featureInfoString.append( "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\">\n" );
featureInfoString.append( "</HEAD>\n" );
featureInfoString.append( "<BODY>\n" );
}
QDomNodeList layerList = infoDoc.elementsByTagName( QStringLiteral( "Layer" ) );
//layer loop
for ( int i = 0; i < layerList.size(); ++i )
{
QDomElement layerElem = layerList.at( i ).toElement();
if ( infoFormat == QLatin1String( "text/plain" ) )
{
featureInfoString.append( "Layer '" + layerElem.attribute( QStringLiteral( "name" ) ) + "'\n" );
}
else if ( infoFormat == QLatin1String( "text/html" ) )
{
featureInfoString.append( "<TABLE border=1 width=100%>\n" );
featureInfoString.append( "<TR><TH width=25%>Layer</TH><TD>" + layerElem.attribute( QStringLiteral( "name" ) ) + "</TD></TR>\n" );
featureInfoString.append( "</BR>" );
}
//feature loop (for vector layers)
QDomNodeList featureNodeList = layerElem.elementsByTagName( QStringLiteral( "Feature" ) );
QDomElement currentFeatureElement;
if ( featureNodeList.isEmpty() ) //raster layer?
{
QDomNodeList attributeNodeList = layerElem.elementsByTagName( QStringLiteral( "Attribute" ) );
for ( int j = 0; j < attributeNodeList.size(); ++j )
{
QDomElement attributeElement = attributeNodeList.at( j ).toElement();
if ( infoFormat == QLatin1String( "text/plain" ) )
{
featureInfoString.append( attributeElement.attribute( QStringLiteral( "name" ) ) + " = '" +
attributeElement.attribute( QStringLiteral( "value" ) ) + "'\n" );
}
else if ( infoFormat == QLatin1String( "text/html" ) )
{
featureInfoString.append( "<TR><TH>" + attributeElement.attribute( QStringLiteral( "name" ) ) + "</TH><TD>" +
attributeElement.attribute( QStringLiteral( "value" ) ) + "</TD></TR>\n" );
}
}
}
else //vector layer
{
for ( int j = 0; j < featureNodeList.size(); ++j )
{
QDomElement featureElement = featureNodeList.at( j ).toElement();
if ( infoFormat == QLatin1String( "text/plain" ) )
{
featureInfoString.append( "Feature " + featureElement.attribute( QStringLiteral( "id" ) ) + "\n" );
}
else if ( infoFormat == QLatin1String( "text/html" ) )
{
featureInfoString.append( "<TABLE border=1 width=100%>\n" );
featureInfoString.append( "<TR><TH>Feature</TH><TD>" + featureElement.attribute( QStringLiteral( "id" ) ) + "</TD></TR>\n" );
}
//attribute loop
QDomNodeList attributeNodeList = featureElement.elementsByTagName( QStringLiteral( "Attribute" ) );
for ( int k = 0; k < attributeNodeList.size(); ++k )
{
QDomElement attributeElement = attributeNodeList.at( k ).toElement();
if ( infoFormat == QLatin1String( "text/plain" ) )
{
featureInfoString.append( attributeElement.attribute( QStringLiteral( "name" ) ) + " = '" +
attributeElement.attribute( QStringLiteral( "value" ) ) + "'\n" );
}
else if ( infoFormat == QLatin1String( "text/html" ) )
{
featureInfoString.append( "<TR><TH>" + attributeElement.attribute( QStringLiteral( "name" ) ) + "</TH><TD>" + attributeElement.attribute( QStringLiteral( "value" ) ) + "</TD></TR>\n" );
}
}
if ( infoFormat == QLatin1String( "text/html" ) )
{
featureInfoString.append( "</TABLE>\n</BR>\n" );
}
}
}
if ( infoFormat == QLatin1String( "text/plain" ) )
{
featureInfoString.append( "\n" );
}
else if ( infoFormat == QLatin1String( "text/html" ) )
{
featureInfoString.append( "</TABLE>\n<BR></BR>\n" );
}
}
if ( infoFormat == QLatin1String( "text/html" ) )
{
featureInfoString.append( "</BODY>\n" );
}
ba = featureInfoString.toUtf8();
}
else //unsupported format, set exception
{
throw QgsServiceException( QStringLiteral( "InvalidFormat" ),
QString( "Feature info format '%1' is not supported. Possibilities are 'text/plain', 'text/html' or 'text/xml'." ).arg( infoFormat ) );
}
response.setHeader( QStringLiteral( "Content-Type" ), infoFormat + QStringLiteral( "; charset=utf-8" ) );
response.write( ba );
}
void writeGetFeatureInfo( QgsServerInterface *serverIface, const QgsProject *project, void writeGetFeatureInfo( QgsServerInterface *serverIface, const QgsProject *project,
const QString &version, const QgsServerRequest &request, const QString &version, const QgsServerRequest &request,
QgsServerResponse &response ) QgsServerResponse &response )
@ -163,9 +33,11 @@ namespace QgsWms
QgsServerRequest::Parameters params = request.parameters(); QgsServerRequest::Parameters params = request.parameters();
QgsRenderer renderer( serverIface, project, params, getConfigParser( serverIface ) ); QgsRenderer renderer( serverIface, project, params, getConfigParser( serverIface ) );
QDomDocument doc = renderer.getFeatureInfo( version ); std::unique_ptr<QByteArray> result( renderer.getFeatureInfo( version ) );
QString outputFormat = params.value( QStringLiteral( "INFO_FORMAT" ), QStringLiteral( "text/plain" ) ); QString infoFormat = params.value( QStringLiteral( "INFO_FORMAT" ), QStringLiteral( "text/plain" ) );
writeInfoResponse( doc, response, outputFormat );
response.setHeader( QStringLiteral( "Content-Type" ), infoFormat + QStringLiteral( "; charset=utf-8" ) );
response.write( *result );
} }

View File

@ -191,6 +191,41 @@ namespace QgsWms
}; };
save( pFormat ); 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, const Parameter pRule = { ParameterName::RULE,
QVariant::String, QVariant::String,
QVariant( "" ), QVariant( "" ),
@ -254,6 +289,20 @@ namespace QgsWms
}; };
save( pLayers ); 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, const Parameter pLayerTitle = { ParameterName::LAYERTITLE,
QVariant::Bool, QVariant::Bool,
QVariant( true ), QVariant( true ),
@ -324,12 +373,26 @@ namespace QgsWms
}; };
save( pFilter ); save( pFilter );
const Parameter pFilterGeom = { ParameterName::FILTER_GEOM,
QVariant::String,
QVariant( "" ),
QVariant()
};
save( pFilterGeom );
const Parameter pSelection = { ParameterName::SELECTION, const Parameter pSelection = { ParameterName::SELECTION,
QVariant::String, QVariant::String,
QVariant( "" ), QVariant( "" ),
QVariant() QVariant()
}; };
save( pSelection ); save( pSelection );
const Parameter pWmsPrecision = { ParameterName::WMS_PRECISION,
QVariant::Int,
QVariant( -1 ),
QVariant()
};
save( pWmsPrecision );
} }
QgsWmsParameters::QgsWmsParameters( const QgsServerRequest::Parameters &parameters ) QgsWmsParameters::QgsWmsParameters( const QgsServerRequest::Parameters &parameters )
@ -628,9 +691,12 @@ namespace QgsWms
QgsWmsParameters::Format QgsWmsParameters::format() const QgsWmsParameters::Format QgsWmsParameters::format() const
{ {
Format f = Format::PNG;
QString fStr = formatAsString(); QString fStr = formatAsString();
if ( fStr.isEmpty() )
return Format::NONE;
Format f = Format::PNG;
if ( fStr.compare( QLatin1String( "jpg" ), Qt::CaseInsensitive ) == 0 if ( fStr.compare( QLatin1String( "jpg" ), Qt::CaseInsensitive ) == 0
|| fStr.compare( QLatin1String( "jpeg" ), Qt::CaseInsensitive ) == 0 || fStr.compare( QLatin1String( "jpeg" ), Qt::CaseInsensitive ) == 0
|| fStr.compare( QLatin1String( "image/jpeg" ), Qt::CaseInsensitive ) == 0 ) || fStr.compare( QLatin1String( "image/jpeg" ), Qt::CaseInsensitive ) == 0 )
@ -639,6 +705,81 @@ namespace QgsWms
return f; return f;
} }
QString QgsWmsParameters::infoFormatAsString() const
{
return value( ParameterName::INFO_FORMAT ).toString();
}
QgsWmsParameters::Format QgsWmsParameters::infoFormat() const
{
QString fStr = infoFormatAsString();
Format f = Format::TEXT;
if ( fStr.isEmpty() )
return f;
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;
}
int QgsWmsParameters::infoFormatVersion() const
{
if ( infoFormat() != Format::GML )
return -1;
QString fStr = infoFormatAsString();
if ( fStr.startsWith( QLatin1String( "application/vnd.ogc.gml/3" ), Qt::CaseInsensitive ) )
return 3;
else
return 2;
}
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 QString QgsWmsParameters::rule() const
{ {
return value( ParameterName::RULE ).toString(); return value( ParameterName::RULE ).toString();
@ -674,6 +815,16 @@ namespace QgsWms
return toBool( ParameterName::SHOWFEATURECOUNT ); 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 QString QgsWmsParameters::boxSpace() const
{ {
return value( ParameterName::BOXSPACE ).toString(); return value( ParameterName::BOXSPACE ).toString();
@ -960,6 +1111,16 @@ namespace QgsWms
return toFloatList( highlightLabelBufferSize(), ParameterName::HIGHLIGHT_LABELBUFFERSIZE ); 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 QString QgsWmsParameters::sld() const
{ {
return value( ParameterName::SLD ).toString(); return value( ParameterName::SLD ).toString();
@ -970,6 +1131,11 @@ namespace QgsWms
return toStringList( ParameterName::FILTER, ';' ); return toStringList( ParameterName::FILTER, ';' );
} }
QString QgsWmsParameters::filterGeom() const
{
return value( ParameterName::FILTER_GEOM ).toString();
}
QStringList QgsWmsParameters::selections() const QStringList QgsWmsParameters::selections() const
{ {
return toStringList( ParameterName::SELECTION ); return toStringList( ParameterName::SELECTION );
@ -992,6 +1158,11 @@ namespace QgsWms
return layer << layers; return layer << layers;
} }
QStringList QgsWmsParameters::queryLayersNickname() const
{
return toStringList( ParameterName::QUERY_LAYERS );
}
QStringList QgsWmsParameters::allStyles() const QStringList QgsWmsParameters::allStyles() const
{ {
QStringList style = value( ParameterName::STYLE ).toString().split( ",", QString::SkipEmptyParts ); QStringList style = value( ParameterName::STYLE ).toString().split( ",", QString::SkipEmptyParts );

View File

@ -86,6 +86,8 @@ namespace QgsWms
LAYERS, LAYERS,
LAYERSPACE, LAYERSPACE,
LAYERTITLESPACE, LAYERTITLESPACE,
QUERY_LAYERS,
FEATURE_COUNT,
SHOWFEATURECOUNT, SHOWFEATURECOUNT,
STYLE, STYLE,
STYLES, STYLES,
@ -95,7 +97,13 @@ namespace QgsWms
OPACITIES, OPACITIES,
SLD, SLD,
FILTER, FILTER,
FILTER_GEOM,
FORMAT, FORMAT,
INFO_FORMAT,
I,
J,
X,
Y,
RULE, RULE,
RULELABEL, RULELABEL,
SCALE, SCALE,
@ -108,7 +116,8 @@ namespace QgsWms
HIGHLIGHT_LABELWEIGHT, HIGHLIGHT_LABELWEIGHT,
HIGHLIGHT_LABELCOLOR, HIGHLIGHT_LABELCOLOR,
HIGHLIGHT_LABELBUFFERCOLOR, HIGHLIGHT_LABELBUFFERCOLOR,
HIGHLIGHT_LABELBUFFERSIZE HIGHLIGHT_LABELBUFFERSIZE,
WMS_PRECISION
}; };
Q_ENUM( ParameterName ) Q_ENUM( ParameterName )
@ -116,7 +125,11 @@ namespace QgsWms
{ {
NONE, NONE,
JPG, JPG,
PNG PNG,
TEXT,
XML,
HTML,
GML
}; };
struct Parameter struct Parameter
@ -203,6 +216,11 @@ namespace QgsWms
*/ */
QStringList filters() const; 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 found in OPACITIES parameter.
* \returns the list of opacities in string * \returns the list of opacities in string
*/ */
@ -221,6 +239,11 @@ namespace QgsWms
*/ */
QStringList allLayersNickname() const; 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 styles found in STYLE and STYLES parameters.
* \returns name of styles * \returns name of styles
*/ */
@ -242,6 +265,75 @@ namespace QgsWms
*/ */
Format format() const; 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 the infoFormat version for GML. If the INFO_FORMAT is not GML,
* then the default value is -1.
* \returns infoFormat version
*/
int infoFormatVersion() 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
* \returns RULE parameter or an empty string if none is defined * \returns RULE parameter or an empty string if none is defined
*/ */
@ -271,6 +363,18 @@ namespace QgsWms
*/ */
bool showFeatureCountAsBool() const; 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
* \returns SCALE parameter or an empty string if none is defined * \returns SCALE parameter or an empty string if none is defined
*/ */
@ -595,6 +699,19 @@ namespace QgsWms
*/ */
QList<QColor> highlightLabelBufferColorAsColor() const; QList<QColor> 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: private:
QString name( ParameterName name ) const; QString name( ParameterName name ) const;
void raiseError( ParameterName name ) const; void raiseError( ParameterName name ) const;

View File

@ -642,322 +642,111 @@ namespace QgsWms
infoPoint->setY( mapSettings.extent().yMaximum() - j * yRes - yRes / 2.0 ); infoPoint->setY( mapSettings.extent().yMaximum() - j * yRes - yRes / 2.0 );
} }
QDomDocument QgsRenderer::getFeatureInfo( const QString &version ) QByteArray *QgsRenderer::getFeatureInfo( const QString &version )
{ {
if ( !mConfigParser ) // Verifying Mandatory parameters
// The QUERY_LAYERS parameter is Mandatory
QStringList queryLayers = mWmsParameters.queryLayersNickname();
if ( queryLayers.isEmpty() )
{ {
throw QgsException( QStringLiteral( "No config parser" ) ); throw QgsBadRequestException( QStringLiteral( "ParameterMissing" ),
QStringLiteral( "QUERY_LAYERS parameter is required for GetFeatureInfo" ) );
} }
QDomDocument result; // The I/J parameters are Mandatory if they are not replaced by X/Y or FILTER or FILTER_GEOM
QStringList layersList, stylesList; bool ijDefined = false;
bool conversionSuccess; if ( !mWmsParameters.i().isEmpty() && !mWmsParameters.j().isEmpty() )
ijDefined = true;
for ( auto it = mParameters.constBegin(); it != mParameters.constEnd(); ++it ) bool xyDefined = false;
{ if ( !mWmsParameters.x().isEmpty() && !mWmsParameters.y().isEmpty() )
QgsMessageLog::logMessage( QStringLiteral( "%1 // %2" ).arg( it.key(), it.value() ) ); xyDefined = true;
}
readLayersAndStyles( mParameters, layersList, stylesList ); bool filtersDefined = false;
initializeSLDParser( layersList, stylesList ); if ( !mWmsParameters.filters().isEmpty() )
filtersDefined = true;
QgsMapSettings mapSettings; bool filterGeomDefined = false;
std::unique_ptr<QImage> outputImage( createImage() ); if ( !mWmsParameters.filterGeom().isEmpty() )
filterGeomDefined = true;
configureMapSettings( outputImage.get(), mapSettings ); if ( !ijDefined && !xyDefined && !filtersDefined && !filterGeomDefined )
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" ) ) )
{
featureCount = mParameters[ QStringLiteral( "FEATURE_COUNT" )].toInt( &conversionSuccess );
if ( !conversionSuccess )
{
featureCount = 1;
}
}
//read QUERY_LAYERS
if ( !mParameters.contains( QStringLiteral( "QUERY_LAYERS" ) ) )
{
throw QgsBadRequestException( QStringLiteral( "ParameterMissing" ), QStringLiteral( "No QUERY_LAYERS" ) );
}
QStringList queryLayerList = mParameters[ QStringLiteral( "QUERY_LAYERS" )].split( ',', QString::SkipEmptyParts );
if ( queryLayerList.isEmpty() )
{
throw QgsBadRequestException( QStringLiteral( "InvalidParameterValue" ), QStringLiteral( "Malformed QUERY_LAYERS" ) );
}
//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<QgsGeometry> 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
std::unique_ptr<QgsRectangle> featuresRect;
std::unique_ptr<QgsPointXY> infoPoint;
if ( i == -1 || j == -1 )
{
if ( mParameters.contains( QStringLiteral( "FILTER" ) ) )
{
featuresRect.reset( new QgsRectangle() );
}
else if ( !filterGeom.get() )
{ {
throw QgsBadRequestException( QStringLiteral( "ParameterMissing" ), throw QgsBadRequestException( QStringLiteral( "ParameterMissing" ),
QStringLiteral( "I/J parameters are required for GetFeatureInfo" ) ); QStringLiteral( "I/J parameters are required for GetFeatureInfo" ) );
} }
}
// get layers parameters
QList<QgsMapLayer *> layers;
QList<QgsWmsParametersLayer> params = mWmsParameters.layersParameters();
// init layer restorer before doing anything
std::unique_ptr<QgsLayerRestorer> 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 else
layers = stylizedLayers( params );
// create the mapSettings and the output image
QgsMapSettings mapSettings;
std::unique_ptr<QImage> 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() ) );
QgsScaleCalculator scaleCalc( ( outputImage->logicalDpiX() + outputImage->logicalDpiY() ) / 2, mapSettings.destinationCrs().mapUnits() );
QgsRectangle mapExtent = mapSettings.extent();
double scaleDenominator = scaleCalc.calculate( mapExtent, outputImage->width() );
// remove unwanted layers (restricted layers, ...)
removeUnwantedLayers( layers, scaleDenominator );
// remove non identifiable layers
removeNonIdentifiableLayers( layers );
Q_FOREACH ( QgsMapLayer *layer, layers )
{ {
infoPoint.reset( new QgsPointXY() ); Q_FOREACH ( QgsWmsParametersLayer param, params )
infoPointToMapCoordinates( i, j, infoPoint.get(), mapSettings ); {
if ( param.mNickname == layerNickname( *layer ) )
{
checkLayerReadPermissions( layer );
setLayerFilter( layer, param.mFilter );
setLayerAccessControlFilter( layer );
break;
}
}
} }
//get the layer registered in QgsMapLayerRegistry and apply possible filters // add layers to map settings (revert order for the rendering)
( void )layerSet( layersList, stylesList, mapSettings.destinationCrs() ); std::reverse( layers.begin(), layers.end() );
mapSettings.setLayers( layers );
//scoped pointer to restore all original layer filters (subsetStrings) when pointer goes out of scope QDomDocument result = featureInfoDocument( layers, mapSettings, outputImage.get(), version );
//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 QByteArray *ba = nullptr;
applyAccessControlLayersFilters( layersList, filterRestorer->originalFilters() ); ba = new QByteArray();
#endif
QDomElement getFeatureInfoElement; QgsWmsParameters::Format infoFormat = mWmsParameters.infoFormat();
QString infoFormat = mParameters.value( QStringLiteral( "INFO_FORMAT" ) ); if ( infoFormat == QgsWmsParameters::Format::TEXT )
if ( infoFormat.startsWith( QLatin1String( "application/vnd.ogc.gml" ) ) ) *ba = convertFeatureInfoToText( result );
{ else if ( infoFormat == QgsWmsParameters::Format::HTML )
getFeatureInfoElement = result.createElement( QStringLiteral( "wfs:FeatureCollection" ) ); *ba = convertFeatureInfoToHtml( result );
getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:wfs" ), QStringLiteral( "http://www.opengis.net/wfs" ) );
getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:ogc" ), QStringLiteral( "http://www.opengis.net/ogc" ) );
getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:gml" ), QStringLiteral( "http://www.opengis.net/gml" ) );
getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:ows" ), QStringLiteral( "http://www.opengis.net/ows" ) );
getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:qgs" ), QStringLiteral( "http://qgis.org/gml" ) );
getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:xsi" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema-instance" ) );
getFeatureInfoElement.setAttribute( QStringLiteral( "xsi:schemaLocation" ), QStringLiteral( "http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/wfs.xsd http://qgis.org/gml" ) );
}
else else
{ *ba = result.toByteArray();
QString featureInfoElemName = mConfigParser->featureInfoDocumentElement( QStringLiteral( "GetFeatureInfoResponse" ) );
QString featureInfoElemNS = mConfigParser->featureInfoDocumentElementNS();
if ( featureInfoElemNS.isEmpty() )
{
getFeatureInfoElement = result.createElement( featureInfoElemName );
}
else
{
getFeatureInfoElement = result.createElementNS( featureInfoElemNS, featureInfoElemName );
}
//feature info schema
QString featureInfoSchema = mConfigParser->featureInfoSchema();
if ( !featureInfoSchema.isEmpty() )
{
getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:xsi" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema-instance" ) );
getFeatureInfoElement.setAttribute( QStringLiteral( "xsi:schemaLocation" ), featureInfoSchema );
}
}
result.appendChild( getFeatureInfoElement );
QStringList nonIdentifiableLayers = mConfigParser->identifyDisabledLayers(); return ba;
//Render context is needed to determine feature visibility for vector layers
QgsRenderContext renderContext = QgsRenderContext::fromMapSettings( mapSettings );
bool sia2045 = mConfigParser->featureInfoFormatSIA2045();
//layers can have assigned a different name for GetCapabilities
QHash<QString, QString> layerAliasMap = mConfigParser->featureInfoLayerAliasMap();
QList<QgsMapLayer *> layerList;
QgsMapLayer *currentLayer = nullptr;
for ( auto layerIt = queryLayerList.constBegin(); layerIt != queryLayerList.constEnd(); ++layerIt )
{
//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 )
{
currentLayer = *layerListIt;
if ( !currentLayer || nonIdentifiableLayers.contains( currentLayer->id() ) )
{
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<QgsVectorLayer *>( 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<QString, QString>::const_iterator layerAliasIt = layerAliasMap.find( layerName );
if ( layerAliasIt != layerAliasMap.constEnd() )
{
layerName = layerAliasIt.value();
}
layerElement.setAttribute( QStringLiteral( "name" ), layerName );
getFeatureInfoElement.appendChild( layerElement );
if ( sia2045 ) //the name might not be unique after alias replacement
{
layerElement.setAttribute( QStringLiteral( "id" ), currentLayer->id() );
}
}
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<QgsRasterLayer *>( currentLayer );
if ( rasterLayer )
{
if ( !infoPoint )
{
continue;
}
QgsPointXY layerInfoPoint = mapSettings.mapToLayerCoordinates( currentLayer, *( infoPoint.get() ) );
if ( !featureInfoFromRasterLayer( rasterLayer, mapSettings, &layerInfoPoint, result, layerElement, version, infoFormat ) )
{
continue;
}
}
else
{
continue;
}
}
}
}
if ( featuresRect )
{
if ( infoFormat.startsWith( QLatin1String( "application/vnd.ogc.gml" ) ) )
{
QDomElement bBoxElem = result.createElement( QStringLiteral( "gml:boundedBy" ) );
QDomElement boxElem;
int gmlVersion = infoFormat.startsWith( QLatin1String( "application/vnd.ogc.gml/3" ) ) ? 3 : 2;
if ( gmlVersion < 3 )
{
boxElem = QgsOgcUtils::rectangleToGMLBox( featuresRect.get(), result, 8 );
}
else
{
boxElem = QgsOgcUtils::rectangleToGMLEnvelope( featuresRect.get(), result, 8 );
}
QgsCoordinateReferenceSystem crs = mapSettings.destinationCrs();
if ( crs.isValid() )
{
boxElem.setAttribute( QStringLiteral( "srsName" ), crs.authid() );
}
bBoxElem.appendChild( boxElem );
getFeatureInfoElement.insertBefore( bBoxElem, QDomNode() ); //insert as first child
}
else
{
QDomElement bBoxElem = result.createElement( QStringLiteral( "BoundingBox" ) );
bBoxElem.setAttribute( QStringLiteral( "CRS" ), mapSettings.destinationCrs().authid() );
bBoxElem.setAttribute( QStringLiteral( "minx" ), qgsDoubleToString( featuresRect->xMinimum(), 8 ) );
bBoxElem.setAttribute( QStringLiteral( "maxx" ), qgsDoubleToString( featuresRect->xMaximum(), 8 ) );
bBoxElem.setAttribute( QStringLiteral( "miny" ), qgsDoubleToString( featuresRect->yMinimum(), 8 ) );
bBoxElem.setAttribute( QStringLiteral( "maxy" ), qgsDoubleToString( featuresRect->yMaximum(), 8 ) );
getFeatureInfoElement.insertBefore( bBoxElem, QDomNode() ); //insert as first child
}
}
if ( sia2045 && infoFormat.compare( QLatin1String( "text/xml" ), Qt::CaseInsensitive ) == 0 )
{
convertFeatureInfoToSIA2045( result );
}
//force restoration of original filters
filterRestorer.reset();
QgsProject::instance()->removeAllMapLayers();
return result;
} }
QImage *QgsRenderer::initializeRendering( QStringList &layersList, QStringList &stylesList, QStringList &layerIdList, QgsMapSettings &mapSettings ) QImage *QgsRenderer::initializeRendering( QStringList &layersList, QStringList &stylesList, QStringList &layerIdList, QgsMapSettings &mapSettings )
@ -1243,6 +1032,224 @@ namespace QgsWms
} }
} }
QDomDocument QgsRenderer::featureInfoDocument( QList<QgsMapLayer *> &layers, const QgsMapSettings &mapSettings,
const QImage *outputImage, const QString &version ) const
{
QStringList queryLayers = mWmsParameters.queryLayersNickname();
bool ijDefined = ( !mWmsParameters.i().isEmpty() && !mWmsParameters.j().isEmpty() );
bool xyDefined = ( !mWmsParameters.x().isEmpty() && !mWmsParameters.y().isEmpty() );
bool filtersDefined = !mWmsParameters.filters().isEmpty();
bool filterGeomDefined = !mWmsParameters.filterGeom().isEmpty();
int featureCount = mWmsParameters.featureCountAsInt();
if ( featureCount < 1 )
{
featureCount = 1;
}
int i = mWmsParameters.iAsInt();
int j = mWmsParameters.jAsInt();
if ( xyDefined && !ijDefined )
{
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 );
}
// init search variables
std::unique_ptr<QgsRectangle> featuresRect;
std::unique_ptr<QgsGeometry> filterGeom;
std::unique_ptr<QgsPointXY> infoPoint;
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() ) ) );
}
QDomDocument result;
QDomElement getFeatureInfoElement;
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" ) );
getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:ogc" ), QStringLiteral( "http://www.opengis.net/ogc" ) );
getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:gml" ), QStringLiteral( "http://www.opengis.net/gml" ) );
getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:ows" ), QStringLiteral( "http://www.opengis.net/ows" ) );
getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:qgs" ), QStringLiteral( "http://qgis.org/gml" ) );
getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:xsi" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema-instance" ) );
getFeatureInfoElement.setAttribute( QStringLiteral( "xsi:schemaLocation" ), QStringLiteral( "http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/wfs.xsd http://qgis.org/gml" ) );
}
else
{
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 );
}
//feature info schema
QString featureInfoSchema = QgsServerProjectUtils::wmsFeatureInfoSchema( *mProject );
if ( !featureInfoSchema.isEmpty() )
{
getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:xsi" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema-instance" ) );
getFeatureInfoElement.setAttribute( QStringLiteral( "xsi:schemaLocation" ), featureInfoSchema );
}
}
result.appendChild( getFeatureInfoElement );
//Render context is needed to determine feature visibility for vector layers
QgsRenderContext renderContext = QgsRenderContext::fromMapSettings( mapSettings );
bool sia2045 = QgsServerProjectUtils::wmsInfoFormatSia2045( *mProject );
//layers can have assigned a different name for GetCapabilities
QHash<QString, QString> layerAliasMap = QgsServerProjectUtils::wmsFeatureInfoLayerAliasMap( *mProject );
Q_FOREACH ( QString queryLayer, queryLayers )
{
Q_FOREACH ( QgsMapLayer *layer, layers )
{
if ( queryLayer == layerNickname( *layer ) )
{
QDomElement layerElement;
if ( infoFormat == QgsWmsParameters::Format::GML )
{
layerElement = getFeatureInfoElement;
}
else
{
layerElement = result.createElement( QStringLiteral( "Layer" ) );
QString layerName = queryLayer;
//check if the layer is given a different name for GetFeatureInfo output
QHash<QString, QString>::const_iterator layerAliasIt = layerAliasMap.find( layerName );
if ( layerAliasIt != layerAliasMap.constEnd() )
{
layerName = layerAliasIt.value();
}
layerElement.setAttribute( QStringLiteral( "name" ), layerName );
getFeatureInfoElement.appendChild( layerElement );
if ( sia2045 ) //the name might not be unique after alias replacement
{
layerElement.setAttribute( QStringLiteral( "id" ), layer->id() );
}
}
if ( layer->type() == QgsMapLayer::VectorLayer )
{
QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
if ( vectorLayer )
{
if ( !featureInfoFromVectorLayer( vectorLayer, infoPoint.get(), featureCount, result, layerElement, mapSettings, renderContext, version, featuresRect.get(), filterGeom.get() ) )
{
break;
}
break;
}
}
else
{
if ( infoFormat == QgsWmsParameters::Format::GML )
{
layerElement = result.createElement( QStringLiteral( "gml:featureMember" )/*wfs:FeatureMember*/ );
getFeatureInfoElement.appendChild( layerElement );
}
QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layer );
if ( rasterLayer )
{
if ( !infoPoint )
{
break;
}
QgsPointXY layerInfoPoint = mapSettings.mapToLayerCoordinates( layer, *( infoPoint.get() ) );
if ( !featureInfoFromRasterLayer( rasterLayer, mapSettings, &layerInfoPoint, result, layerElement, version ) )
{
break;
}
break;
}
}
break;
}
}
}
if ( featuresRect )
{
if ( infoFormat == QgsWmsParameters::Format::GML )
{
QDomElement bBoxElem = result.createElement( QStringLiteral( "gml:boundedBy" ) );
QDomElement boxElem;
int gmlVersion = mWmsParameters.infoFormatVersion();
if ( gmlVersion < 3 )
{
boxElem = QgsOgcUtils::rectangleToGMLBox( featuresRect.get(), result, 8 );
}
else
{
boxElem = QgsOgcUtils::rectangleToGMLEnvelope( featuresRect.get(), result, 8 );
}
QgsCoordinateReferenceSystem crs = mapSettings.destinationCrs();
if ( crs.isValid() )
{
boxElem.setAttribute( QStringLiteral( "srsName" ), crs.authid() );
}
bBoxElem.appendChild( boxElem );
getFeatureInfoElement.insertBefore( bBoxElem, QDomNode() ); //insert as first child
}
else
{
QDomElement bBoxElem = result.createElement( QStringLiteral( "BoundingBox" ) );
bBoxElem.setAttribute( QStringLiteral( "CRS" ), mapSettings.destinationCrs().authid() );
bBoxElem.setAttribute( QStringLiteral( "minx" ), qgsDoubleToString( featuresRect->xMinimum(), 8 ) );
bBoxElem.setAttribute( QStringLiteral( "maxx" ), qgsDoubleToString( featuresRect->xMaximum(), 8 ) );
bBoxElem.setAttribute( QStringLiteral( "miny" ), qgsDoubleToString( featuresRect->yMinimum(), 8 ) );
bBoxElem.setAttribute( QStringLiteral( "maxy" ), qgsDoubleToString( featuresRect->yMaximum(), 8 ) );
getFeatureInfoElement.insertBefore( bBoxElem, QDomNode() ); //insert as first child
}
}
if ( sia2045 && infoFormat == QgsWmsParameters::Format::XML )
{
convertFeatureInfoToSia2045( result );
}
return result;
}
bool QgsRenderer::featureInfoFromVectorLayer( QgsVectorLayer *layer, bool QgsRenderer::featureInfoFromVectorLayer( QgsVectorLayer *layer,
const QgsPointXY *infoPoint, const QgsPointXY *infoPoint,
int nFeatures, int nFeatures,
@ -1251,7 +1258,6 @@ namespace QgsWms
const QgsMapSettings &mapSettings, const QgsMapSettings &mapSettings,
QgsRenderContext &renderContext, QgsRenderContext &renderContext,
const QString &version, const QString &version,
const QString &infoFormat,
QgsRectangle *featureBBox, QgsRectangle *featureBBox,
QgsGeometry *filterGeom ) const QgsGeometry *filterGeom ) const
{ {
@ -1288,8 +1294,8 @@ namespace QgsWms
int featureCounter = 0; int featureCounter = 0;
layer->updateFields(); layer->updateFields();
const QgsFields &fields = layer->pendingFields(); const QgsFields &fields = layer->pendingFields();
bool addWktGeometry = mConfigParser && mConfigParser->featureInfoWithWktGeometry(); bool addWktGeometry = QgsServerProjectUtils::wmsFeatureInfoAddWktGeometry( *mProject );
bool segmentizeWktGeometry = mConfigParser && mConfigParser->segmentizeFeatureInfoWktGeometry(); bool segmentizeWktGeometry = QgsServerProjectUtils::wmsFeatureInfoSegmentizeWktGeometry( *mProject );
const QSet<QString> &excludedAttributes = layer->excludeAttributesWms(); const QSet<QString> &excludedAttributes = layer->excludeAttributesWms();
QgsFeatureRequest fReq; QgsFeatureRequest fReq;
@ -1385,17 +1391,13 @@ namespace QgsWms
outputCrs = mapSettings.destinationCrs(); outputCrs = mapSettings.destinationCrs();
} }
if ( infoFormat == QLatin1String( "application/vnd.ogc.gml" ) ) if ( mWmsParameters.infoFormat() == QgsWmsParameters::Format::GML )
{ {
bool withGeom = layer->wkbType() != QgsWkbTypes::NoGeometry && addWktGeometry; bool withGeom = layer->wkbType() != QgsWkbTypes::NoGeometry && addWktGeometry;
int version = infoFormat.startsWith( QLatin1String( "application/vnd.ogc.gml/3" ) ) ? 3 : 2; int gmlVersion = mWmsParameters.infoFormatVersion();
QString typeName = layer->name(); QString typeName = layerNickname( *layer );
if ( mConfigParser && mConfigParser->useLayerIds() )
typeName = layer->id();
else if ( !layer->shortName().isEmpty() )
typeName = layer->shortName();
QDomElement elem = createFeatureGML( QDomElement elem = createFeatureGML(
&feature, layer, infoDocument, outputCrs, mapSettings, typeName, withGeom, version &feature, layer, infoDocument, outputCrs, mapSettings, typeName, withGeom, gmlVersion
#ifdef HAVE_SERVER_PYTHON_PLUGINS #ifdef HAVE_SERVER_PYTHON_PLUGINS
, &attributes , &attributes
#endif #endif
@ -1453,14 +1455,14 @@ namespace QgsWms
} }
//append feature bounding box to feature info xml //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" ) ); QDomElement bBoxElem = infoDocument.createElement( QStringLiteral( "BoundingBox" ) );
bBoxElem.setAttribute( version == QLatin1String( "1.1.1" ) ? "SRS" : "CRS", outputCrs.authid() ); bBoxElem.setAttribute( version == QLatin1String( "1.1.1" ) ? "SRS" : "CRS", outputCrs.authid() );
bBoxElem.setAttribute( QStringLiteral( "minx" ), qgsDoubleToString( box.xMinimum(), getWMSPrecision( 8 ) ) ); bBoxElem.setAttribute( QStringLiteral( "minx" ), qgsDoubleToString( box.xMinimum(), getWMSPrecision() ) );
bBoxElem.setAttribute( QStringLiteral( "maxx" ), qgsDoubleToString( box.xMaximum(), getWMSPrecision( 8 ) ) ); bBoxElem.setAttribute( QStringLiteral( "maxx" ), qgsDoubleToString( box.xMaximum(), getWMSPrecision() ) );
bBoxElem.setAttribute( QStringLiteral( "miny" ), qgsDoubleToString( box.yMinimum(), getWMSPrecision( 8 ) ) ); bBoxElem.setAttribute( QStringLiteral( "miny" ), qgsDoubleToString( box.yMinimum(), getWMSPrecision() ) );
bBoxElem.setAttribute( QStringLiteral( "maxy" ), qgsDoubleToString( box.yMaximum(), getWMSPrecision( 8 ) ) ); bBoxElem.setAttribute( QStringLiteral( "maxy" ), qgsDoubleToString( box.yMaximum(), getWMSPrecision() ) );
featureElement.appendChild( bBoxElem ); featureElement.appendChild( bBoxElem );
} }
@ -1484,14 +1486,14 @@ namespace QgsWms
{ {
if ( QgsWkbTypes::isCurvedType( abstractGeom->wkbType() ) ) if ( QgsWkbTypes::isCurvedType( abstractGeom->wkbType() ) )
{ {
QgsAbstractGeometry *segmentizedGeom = abstractGeom-> segmentize(); QgsAbstractGeometry *segmentizedGeom = abstractGeom->segmentize();
geom.setGeometry( segmentizedGeom ); geom.setGeometry( segmentizedGeom );
} }
} }
} }
QDomElement geometryElement = infoDocument.createElement( QStringLiteral( "Attribute" ) ); QDomElement geometryElement = infoDocument.createElement( QStringLiteral( "Attribute" ) );
geometryElement.setAttribute( QStringLiteral( "name" ), QStringLiteral( "geometry" ) ); 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" ) ); geometryElement.setAttribute( QStringLiteral( "type" ), QStringLiteral( "derived" ) );
featureElement.appendChild( geometryElement ); featureElement.appendChild( geometryElement );
} }
@ -1511,8 +1513,7 @@ namespace QgsWms
const QgsPointXY *infoPoint, const QgsPointXY *infoPoint,
QDomDocument &infoDocument, QDomDocument &infoDocument,
QDomElement &layerElement, QDomElement &layerElement,
const QString &version, const QString &version ) const
const QString &infoFormat ) const
{ {
Q_UNUSED( version ); Q_UNUSED( version );
@ -1540,7 +1541,7 @@ namespace QgsWms
attributes = layer->dataProvider()->identify( *infoPoint, QgsRaster::IdentifyFormatValue, mapSettings.extent(), mapSettings.outputSize().width(), mapSettings.outputSize().height() ).results(); 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; QgsFeature feature;
QgsFields fields; QgsFields fields;
@ -1554,14 +1555,10 @@ namespace QgsWms
feature.setFields( fields ); feature.setFields( fields );
QgsCoordinateReferenceSystem layerCrs = layer->crs(); QgsCoordinateReferenceSystem layerCrs = layer->crs();
int version = infoFormat.startsWith( QLatin1String( "application/vnd.ogc.gml/3" ) ) ? 3 : 2; int gmlVersion = mWmsParameters.infoFormatVersion();
QString typeName = layer->name(); QString typeName = layerNickname( *layer );
if ( mConfigParser && mConfigParser->useLayerIds() )
typeName = layer->id();
else if ( !layer->shortName().isEmpty() )
typeName = layer->shortName();
QDomElement elem = createFeatureGML( QDomElement elem = createFeatureGML(
&feature, nullptr, infoDocument, layerCrs, mapSettings, typeName, false, version, nullptr ); &feature, nullptr, infoDocument, layerCrs, mapSettings, typeName, false, gmlVersion, nullptr );
layerElement.appendChild( elem ); layerElement.appendChild( elem );
} }
else else
@ -2097,7 +2094,7 @@ namespace QgsWms
return true; return true;
} }
void QgsRenderer::convertFeatureInfoToSIA2045( QDomDocument &doc ) void QgsRenderer::convertFeatureInfoToSia2045( QDomDocument &doc ) const
{ {
QDomDocument SIAInfoDoc; QDomDocument SIAInfoDoc;
QDomElement infoDocElement = doc.documentElement(); QDomElement infoDocElement = doc.documentElement();
@ -2212,6 +2209,130 @@ namespace QgsWms
doc = SIAInfoDoc; doc = SIAInfoDoc;
} }
QByteArray QgsRenderer::convertFeatureInfoToHtml( const QDomDocument &doc ) const
{
QString featureInfoString;
//the HTML head
featureInfoString.append( "<HEAD>\n" );
featureInfoString.append( "<TITLE> GetFeatureInfo results </TITLE>\n" );
featureInfoString.append( "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\">\n" );
featureInfoString.append( "</HEAD>\n" );
//start the html body
featureInfoString.append( "<BODY>\n" );
QDomNodeList layerList = doc.elementsByTagName( QStringLiteral( "Layer" ) );
//layer loop
for ( int i = 0; i < layerList.size(); ++i )
{
QDomElement layerElem = layerList.at( i ).toElement();
featureInfoString.append( "<TABLE border=1 width=100%>\n" );
featureInfoString.append( "<TR><TH width=25%>Layer</TH><TD>" + layerElem.attribute( QStringLiteral( "name" ) ) + "</TD></TR>\n" );
featureInfoString.append( "</BR>" );
//feature loop (for vector layers)
QDomNodeList featureNodeList = layerElem.elementsByTagName( QStringLiteral( "Feature" ) );
QDomElement currentFeatureElement;
if ( !featureNodeList.isEmpty() ) //vector layer
{
for ( int j = 0; j < featureNodeList.size(); ++j )
{
QDomElement featureElement = featureNodeList.at( j ).toElement();
featureInfoString.append( "<TABLE border=1 width=100%>\n" );
featureInfoString.append( "<TR><TH>Feature</TH><TD>" + featureElement.attribute( QStringLiteral( "id" ) ) +
"</TD></TR>\n" );
//attribute loop
QDomNodeList attributeNodeList = featureElement.elementsByTagName( QStringLiteral( "Attribute" ) );
for ( int k = 0; k < attributeNodeList.size(); ++k )
{
QDomElement attributeElement = attributeNodeList.at( k ).toElement();
featureInfoString.append( "<TR><TH>" + attributeElement.attribute( QStringLiteral( "name" ) ) +
"</TH><TD>" + attributeElement.attribute( QStringLiteral( "value" ) ) + "</TD></TR>\n" );
}
featureInfoString.append( "</TABLE>\n</BR>\n" );
}
}
else //raster layer
{
QDomNodeList attributeNodeList = layerElem.elementsByTagName( QStringLiteral( "Attribute" ) );
for ( int j = 0; j < attributeNodeList.size(); ++j )
{
QDomElement attributeElement = attributeNodeList.at( j ).toElement();
featureInfoString.append( "<TR><TH>" + attributeElement.attribute( QStringLiteral( "name" ) ) +
"</TH><TD>" + attributeElement.attribute( QStringLiteral( "value" ) ) + "</TD></TR>\n" );
}
}
featureInfoString.append( "</TABLE>\n<BR></BR>\n" );
}
//start the html body
featureInfoString.append( "</BODY>\n" );
return featureInfoString.toUtf8();
}
QByteArray QgsRenderer::convertFeatureInfoToText( const QDomDocument &doc ) const
{
QString featureInfoString;
//the Text head
featureInfoString.append( "GetFeatureInfo results\n" );
featureInfoString.append( "\n" );
QDomNodeList layerList = doc.elementsByTagName( QStringLiteral( "Layer" ) );
//layer loop
for ( int i = 0; i < layerList.size(); ++i )
{
QDomElement layerElem = layerList.at( i ).toElement();
featureInfoString.append( "Layer '" + layerElem.attribute( QStringLiteral( "name" ) ) + "'\n" );
//feature loop (for vector layers)
QDomNodeList featureNodeList = layerElem.elementsByTagName( QStringLiteral( "Feature" ) );
QDomElement currentFeatureElement;
if ( !featureNodeList.isEmpty() ) //vector layer
{
for ( int j = 0; j < featureNodeList.size(); ++j )
{
QDomElement featureElement = featureNodeList.at( j ).toElement();
featureInfoString.append( "Feature " + featureElement.attribute( QStringLiteral( "id" ) ) + "\n" );
//attribute loop
QDomNodeList attributeNodeList = featureElement.elementsByTagName( QStringLiteral( "Attribute" ) );
for ( int k = 0; k < attributeNodeList.size(); ++k )
{
QDomElement attributeElement = attributeNodeList.at( k ).toElement();
featureInfoString.append( attributeElement.attribute( QStringLiteral( "name" ) ) + " = '" +
attributeElement.attribute( QStringLiteral( "value" ) ) + "'\n" );
}
}
}
else //raster layer
{
QDomNodeList attributeNodeList = layerElem.elementsByTagName( QStringLiteral( "Attribute" ) );
for ( int j = 0; j < attributeNodeList.size(); ++j )
{
QDomElement attributeElement = attributeNodeList.at( j ).toElement();
featureInfoString.append( attributeElement.attribute( QStringLiteral( "name" ) ) + " = '" +
attributeElement.attribute( QStringLiteral( "value" ) ) + "'\n" );
}
}
featureInfoString.append( "\n" );
}
return featureInfoString.toUtf8();
}
QDomElement QgsRenderer::createFeatureGML( QDomElement QgsRenderer::createFeatureGML(
QgsFeature *feat, QgsFeature *feat,
QgsVectorLayer *layer, QgsVectorLayer *layer,
@ -2263,11 +2384,11 @@ namespace QgsWms
QDomElement boxElem; QDomElement boxElem;
if ( version < 3 ) if ( version < 3 )
{ {
boxElem = QgsOgcUtils::rectangleToGMLBox( &box, doc, 8 ); boxElem = QgsOgcUtils::rectangleToGMLBox( &box, doc, getWMSPrecision() );
} }
else else
{ {
boxElem = QgsOgcUtils::rectangleToGMLEnvelope( &box, doc, 8 ); boxElem = QgsOgcUtils::rectangleToGMLEnvelope( &box, doc, getWMSPrecision() );
} }
if ( crs.isValid() ) if ( crs.isValid() )
@ -2291,11 +2412,11 @@ namespace QgsWms
QDomElement gmlElem; QDomElement gmlElem;
if ( version < 3 ) if ( version < 3 )
{ {
gmlElem = QgsOgcUtils::geometryToGML( geom, doc, 8 ); gmlElem = QgsOgcUtils::geometryToGML( geom, doc, getWMSPrecision() );
} }
else else
{ {
gmlElem = QgsOgcUtils::geometryToGML( geom, doc, QStringLiteral( "GML3" ), 8 ); gmlElem = QgsOgcUtils::geometryToGML( geom, doc, QStringLiteral( "GML3" ), getWMSPrecision() );
} }
if ( !gmlElem.isNull() ) if ( !gmlElem.isNull() )
@ -2388,26 +2509,17 @@ namespace QgsWms
return imageQuality; return imageQuality;
} }
int QgsRenderer::getWMSPrecision( int defaultValue = 8 ) const int QgsRenderer::getWMSPrecision() const
{ {
// First taken from QGIS project // First taken from QGIS project and the default value is 6
int WMSPrecision = mConfigParser->wmsPrecision(); int WMSPrecision = QgsServerProjectUtils::wmsFeatureInfoPrecision( *mProject );
// Then checks if a parameter is given, if so use it instead // Then checks if a parameter is given, if so use it instead
if ( mParameters.contains( QStringLiteral( "WMS_PRECISION" ) ) ) int WMSPrecisionParameter = mWmsParameters.wmsPrecisionAsInt();
{
bool conversionSuccess; if ( WMSPrecisionParameter > -1 )
int WMSPrecisionParameter; return WMSPrecisionParameter;
WMSPrecisionParameter = mParameters[ QStringLiteral( "WMS_PRECISION" )].toInt( &conversionSuccess ); else
if ( conversionSuccess )
{
WMSPrecision = WMSPrecisionParameter;
}
}
if ( WMSPrecision == -1 )
{
WMSPrecision = defaultValue;
}
return WMSPrecision; return WMSPrecision;
} }
@ -2929,6 +3041,25 @@ namespace QgsWms
layers = wantedLayers; layers = wantedLayers;
} }
void QgsRenderer::removeNonIdentifiableLayers( QList<QgsMapLayer *> &layers ) const
{
QStringList nonIdentifiableLayers = mProject->nonIdentifiableLayers();
if ( !nonIdentifiableLayers.isEmpty() )
{
QList<QgsMapLayer *> wantedLayers;
Q_FOREACH ( QgsMapLayer *layer, layers )
{
if ( nonIdentifiableLayers.contains( layer->id() ) )
continue;
wantedLayers.append( layer );
}
layers = wantedLayers;
}
}
QgsLayerTreeModel *QgsRenderer::buildLegendTreeModel( const QList<QgsMapLayer *> &layers, double scaleDenominator, QgsLayerTree &rootGroup ) QgsLayerTreeModel *QgsRenderer::buildLegendTreeModel( const QList<QgsMapLayer *> &layers, double scaleDenominator, QgsLayerTree &rootGroup )
{ {
// get params // get params

View File

@ -116,7 +116,7 @@ namespace QgsWms
/** Creates an xml document that describes the result of the getFeatureInfo request. /** Creates an xml document that describes the result of the getFeatureInfo request.
* May throw an exception * May throw an exception
*/ */
QDomDocument getFeatureInfo( const QString &version = "1.3.0" ); QByteArray *getFeatureInfo( const QString &version = "1.3.0" );
private: private:
@ -140,6 +140,9 @@ namespace QgsWms
// Remove unwanted layers (restricted, not visible, etc) // Remove unwanted layers (restricted, not visible, etc)
void removeUnwantedLayers( QList<QgsMapLayer *> &layers, double scaleDenominator = -1 ) const; void removeUnwantedLayers( QList<QgsMapLayer *> &layers, double scaleDenominator = -1 ) const;
// Remove non identifiable layers (restricted, not visible, etc)
void removeNonIdentifiableLayers( QList<QgsMapLayer *> &layers ) const;
// Rendering step for layers // Rendering step for layers
QPainter *layersRendering( const QgsMapSettings &mapSettings, QImage &image, HitTest *hitTest = nullptr ) const; QPainter *layersRendering( const QgsMapSettings &mapSettings, QImage &image, HitTest *hitTest = nullptr ) const;
@ -211,6 +214,9 @@ namespace QgsWms
*/ */
void initializeSLDParser( QStringList &layersList, QStringList &stylesList ); void initializeSLDParser( QStringList &layersList, QStringList &stylesList );
QDomDocument featureInfoDocument( QList<QgsMapLayer *> &layers, const QgsMapSettings &mapSettings,
const QImage *outputImage, const QString &version ) const;
/** Appends feature info xml for the layer to the layer element of the feature info dom document /** Appends feature info xml for the layer to the layer element of the feature info dom document
\param featureBBox the bounding box of the selected features in output CRS \param featureBBox the bounding box of the selected features in output CRS
\returns true in case of success*/ \returns true in case of success*/
@ -222,7 +228,6 @@ namespace QgsWms
const QgsMapSettings &mapSettings, const QgsMapSettings &mapSettings,
QgsRenderContext &renderContext, QgsRenderContext &renderContext,
const QString &version, const QString &version,
const QString &infoFormat,
QgsRectangle *featureBBox = nullptr, QgsRectangle *featureBBox = nullptr,
QgsGeometry *filterGeom = nullptr ) const; QgsGeometry *filterGeom = nullptr ) const;
//! Appends feature info xml for the layer to the layer element of the dom document //! Appends feature info xml for the layer to the layer element of the dom document
@ -231,8 +236,7 @@ namespace QgsWms
const QgsPointXY *infoPoint, const QgsPointXY *infoPoint,
QDomDocument &infoDocument, QDomDocument &infoDocument,
QDomElement &layerElement, QDomElement &layerElement,
const QString &version, const QString &version ) const;
const QString &infoFormat ) const;
/** Creates a layer set and returns a stringlist with layer ids that can be passed to a renderer. Usually used in conjunction with readLayersAndStyles /** Creates a layer set and returns a stringlist with layer ids that can be passed to a renderer. Usually used in conjunction with readLayersAndStyles
\param scaleDenominator Filter out layer if scale based visibility does not match (or use -1 if no scale restriction)*/ \param scaleDenominator Filter out layer if scale based visibility does not match (or use -1 if no scale restriction)*/
@ -291,7 +295,13 @@ namespace QgsWms
bool checkMaximumWidthHeight() const; bool checkMaximumWidthHeight() const;
//! Converts a feature info xml document to SIA2045 norm //! Converts a feature info xml document to SIA2045 norm
void convertFeatureInfoToSIA2045( QDomDocument &doc ); void convertFeatureInfoToSia2045( QDomDocument &doc ) const;
//! Converts a feature info xml document to HTML
QByteArray convertFeatureInfoToHtml( const QDomDocument &doc ) const;
//! Converts a feature info xml document to Text
QByteArray convertFeatureInfoToText( const QDomDocument &doc ) const;
QDomElement createFeatureGML( QDomElement createFeatureGML(
QgsFeature *feat, QgsFeature *feat,
@ -334,7 +344,7 @@ namespace QgsWms
int getImageQuality() const; int getImageQuality() const;
//! Return precision to use for GetFeatureInfo request //! Return precision to use for GetFeatureInfo request
int getWMSPrecision( int defaultValue ) const; int getWMSPrecision() const;
}; };

View File

@ -453,6 +453,33 @@ class TestQgsServerAccessControl(unittest.TestCase):
str(response).find("<qgs:color>red</qgs:color>") != -1, # spellok str(response).find("<qgs:color>red</qgs:color>") != -1, # spellok
"No color in result of GetFeatureInfo\n%s" % response) "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('<ServiceException code="Security">') != -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) response, headers = self._get_restricted(query_string)
self.assertTrue( self.assertTrue(
str(response).find("<qgs:pk>1</qgs:pk>") != -1, str(response).find("<qgs:pk>1</qgs:pk>") != -1,
@ -1053,6 +1080,33 @@ class TestQgsServerAccessControl(unittest.TestCase):
str(response).find("<qgs:pk>1</qgs:pk>") != -1, str(response).find("<qgs:pk>1</qgs:pk>") != -1,
"No good result in GetFeatureInfo Hello/1\n%s" % response) "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('<ServiceException code="Security">') != -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) response, headers = self._get_restricted(query_string)
self.assertTrue( self.assertTrue(
str(response).find("<qgs:pk>") != -1, str(response).find("<qgs:pk>") != -1,

View File

@ -69,7 +69,16 @@ class TestQgsServerWMS(QgsServerTestBase):
for request in ('GetCapabilities', 'GetProjectSettings', 'GetContext'): for request in ('GetCapabilities', 'GetProjectSettings', 'GetContext'):
self.wms_request_compare(request) self.wms_request_compare(request)
# Test getfeatureinfo response # Test getfeatureinfo response xml
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', self.wms_request_compare('GetFeatureInfo',
'&layers=testlayer%20%C3%A8%C3%A9&styles=&' + '&layers=testlayer%20%C3%A8%C3%A9&styles=&' +
'info_format=text%2Fhtml&transparent=true&' + 'info_format=text%2Fhtml&transparent=true&' +

View File

@ -0,0 +1,14 @@
*****
Content-Type: text/xml; charset=utf-8
<GetFeatureInfoResponse>
<Layer name="testlayer èé">
<Feature id="2">
<Attribute value="3" name="id"/>
<Attribute value="three" name="name"/>
<Attribute value="three èé↓" name="utf8nameè"/>
<BoundingBox maxy="5606011.4565" maxx="913204.9128" miny="5606011.4565" CRS="EPSG:3857" minx="913204.9128"/>
<Attribute type="derived" value="Point (913204.9128 5606011.4565)" name="geometry"/>
</Feature>
</Layer>
</GetFeatureInfoResponse>