Skip to content

Commit

Permalink
Merge pull request #7887 from pblottiere/server_filter_2
Browse files Browse the repository at this point in the history
[server] Add suport for OGC FE version 2 in GetMap requests
  • Loading branch information
pblottiere committed Sep 14, 2018
2 parents 8c31299 + d30f93f commit 268c20f
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 25 deletions.
2 changes: 1 addition & 1 deletion src/core/qgsogcutils.cpp
Expand Up @@ -3172,14 +3172,14 @@ QgsExpressionNodeBinaryOperator *QgsOgcUtilsExpressionFromFilter::nodeBinaryOper


QDomElement operandElem = element.firstChildElement(); QDomElement operandElem = element.firstChildElement();
std::unique_ptr<QgsExpressionNode> expr( nodeFromOgcFilter( operandElem ) ); std::unique_ptr<QgsExpressionNode> expr( nodeFromOgcFilter( operandElem ) );
std::unique_ptr<QgsExpressionNode> leftOp( expr->clone() );


if ( !expr ) if ( !expr )
{ {
mErrorMessage = QObject::tr( "invalid left operand for '%1' binary operator" ).arg( element.tagName() ); mErrorMessage = QObject::tr( "invalid left operand for '%1' binary operator" ).arg( element.tagName() );
return nullptr; return nullptr;
} }


std::unique_ptr<QgsExpressionNode> leftOp( expr->clone() );
for ( operandElem = operandElem.nextSiblingElement(); !operandElem.isNull(); operandElem = operandElem.nextSiblingElement() ) for ( operandElem = operandElem.nextSiblingElement(); !operandElem.isNull(); operandElem = operandElem.nextSiblingElement() )
{ {
std::unique_ptr<QgsExpressionNode> opRight( nodeFromOgcFilter( operandElem ) ); std::unique_ptr<QgsExpressionNode> opRight( nodeFromOgcFilter( operandElem ) );
Expand Down
40 changes: 29 additions & 11 deletions src/server/services/wms/qgswmsparameters.cpp
Expand Up @@ -1248,16 +1248,32 @@ namespace QgsWms
return style << styles; return style << styles;
} }


QMultiMap<QString, QString> QgsWmsParameters::getLayerFilters( const QStringList &layers ) const QMultiMap<QString, QgsWmsParametersFilter> QgsWmsParameters::layerFilters( const QStringList &layers ) const
{ {
const QString nsWfs2 = QStringLiteral( "http://www.opengis.net/fes/2.0" );
const QString prefixWfs2 = QStringLiteral( "<fes:" );

const QStringList rawFilters = filters(); const QStringList rawFilters = filters();
QMultiMap<QString, QString> layerFilters; QMultiMap<QString, QgsWmsParametersFilter> filters;
for ( int i = 0; i < rawFilters.size(); i++ ) for ( int i = 0; i < rawFilters.size(); i++ )
{ {
const QString f = rawFilters[i]; const QString f = rawFilters[i];
if ( f.startsWith( QLatin1String( "<" ) ) && f.endsWith( QLatin1String( "Filter>" ) ) && i < layers.size() ) if ( f.startsWith( QLatin1String( "<" ) ) \
&& f.endsWith( QLatin1String( "Filter>" ) ) \
&& i < layers.size() )
{ {
layerFilters.insert( layers[i], f ); QgsWmsParametersFilter filter;
filter.mFilter = f;
filter.mType = QgsWmsParametersFilter::OGC_FE;
filter.mVersion = QgsOgcUtils::FILTER_OGC_1_0;

if ( filter.mFilter.contains( nsWfs2 ) \
|| filter.mFilter.contains( prefixWfs2 ) )
{
filter.mVersion = QgsOgcUtils::FILTER_FES_2_0;
}

filters.insert( layers[i], filter );
} }
else if ( !f.isEmpty() ) else if ( !f.isEmpty() )
{ {
Expand All @@ -1266,7 +1282,10 @@ namespace QgsWms
const QStringList splits = f.split( ':' ); const QStringList splits = f.split( ':' );
if ( splits.size() == 2 ) if ( splits.size() == 2 )
{ {
layerFilters.insert( splits[0], splits[1] ); QgsWmsParametersFilter filter;
filter.mFilter = splits[1];
filter.mType = QgsWmsParametersFilter::SQL;
filters.insert( splits[0], filter );
} }
else else
{ {
Expand All @@ -1275,7 +1294,7 @@ namespace QgsWms
} }
} }
} }
return layerFilters; return filters;
} }


QList<QgsWmsParametersLayer> QgsWmsParameters::layersParameters() const QList<QgsWmsParametersLayer> QgsWmsParameters::layersParameters() const
Expand All @@ -1284,7 +1303,7 @@ namespace QgsWms
const QStringList styles = allStyles(); const QStringList styles = allStyles();
const QStringList selection = selections(); const QStringList selection = selections();
const QList<int> opacities = opacitiesAsInt(); const QList<int> opacities = opacitiesAsInt();
const QMultiMap<QString, QString> layerFilters = getLayerFilters( layers ); const QMultiMap<QString, QgsWmsParametersFilter> filters = layerFilters( layers );


// selection format: "LayerName:id0,id1;LayerName2:id0,id1;..." // selection format: "LayerName:id0,id1;LayerName2:id0,id1;..."
// several filters can be defined for one layer // several filters can be defined for one layer
Expand Down Expand Up @@ -1316,11 +1335,10 @@ namespace QgsWms
if ( i < opacities.count() ) if ( i < opacities.count() )
param.mOpacity = opacities[i]; param.mOpacity = opacities[i];


if ( layerFilters.contains( layer ) ) if ( filters.contains( layer ) )
{ {
QMultiMap<QString, QString>::const_iterator it; auto it = filters.find( layer );
it = layerFilters.find( layer ); while ( it != filters.end() && it.key() == layer )
while ( it != layerFilters.end() && it.key() == layer )
{ {
param.mFilter.append( it.value() ); param.mFilter.append( it.value() );
++it; ++it;
Expand Down
20 changes: 18 additions & 2 deletions src/server/services/wms/qgswmsparameters.h
Expand Up @@ -28,15 +28,31 @@
#include "qgsserverrequest.h" #include "qgsserverrequest.h"
#include "qgslegendsettings.h" #include "qgslegendsettings.h"
#include "qgsprojectversion.h" #include "qgsprojectversion.h"
#include "qgsogcutils.h"
#include "qgsserverparameters.h" #include "qgsserverparameters.h"


namespace QgsWms namespace QgsWms
{ {
struct QgsWmsParametersFilter
{
//! Filter type
enum Type
{
UNKNOWN,
SQL,
OGC_FE
};

QString mFilter;
QgsWmsParametersFilter::Type mType = QgsWmsParametersFilter::UNKNOWN;
QgsOgcUtils::FilterVersion mVersion = QgsOgcUtils::FILTER_OGC_1_0; // only if FE
};

struct QgsWmsParametersLayer struct QgsWmsParametersLayer
{ {
QString mNickname; // name, id or short name QString mNickname; // name, id or short name
int mOpacity = -1; int mOpacity = -1;
QStringList mFilter; // list of filter QList<QgsWmsParametersFilter> mFilter; // list of filter
QStringList mSelection; // list of string fid QStringList mSelection; // list of string fid
QString mStyle; QString mStyle;
}; };
Expand Down Expand Up @@ -1125,7 +1141,7 @@ namespace QgsWms
void raiseError( const QString &msg ) const; void raiseError( const QString &msg ) const;
void log( const QString &msg ) const; void log( const QString &msg ) const;


QMultiMap<QString, QString> getLayerFilters( const QStringList &layers ) const; QMultiMap<QString, QgsWmsParametersFilter> layerFilters( const QStringList &layers ) const;


QMap<QgsWmsParameter::Name, QgsWmsParameter> mWmsParameters; QMap<QgsWmsParameter::Name, QgsWmsParameter> mWmsParameters;
QMap<QString, QMap<QString, QString> > mExternalWMSParameters; QMap<QString, QMap<QString, QString> > mExternalWMSParameters;
Expand Down
20 changes: 10 additions & 10 deletions src/server/services/wms/qgswmsrenderer.cpp
Expand Up @@ -2795,35 +2795,35 @@ namespace QgsWms
} }
} }


void QgsRenderer::setLayerFilter( QgsMapLayer *layer, const QStringList &filters ) void QgsRenderer::setLayerFilter( QgsMapLayer *layer, const QList<QgsWmsParametersFilter> &filters )
{ {
if ( layer->type() == QgsMapLayer::VectorLayer ) if ( layer->type() == QgsMapLayer::VectorLayer )
{ {
QgsVectorLayer *filteredLayer = qobject_cast<QgsVectorLayer *>( layer ); QgsVectorLayer *filteredLayer = qobject_cast<QgsVectorLayer *>( layer );
for ( const QString &filter : filters ) for ( const QgsWmsParametersFilter &filter : filters )
{ {
if ( filter.startsWith( QLatin1String( "<" ) ) && filter.endsWith( QLatin1String( "Filter>" ) ) ) if ( filter.mType == QgsWmsParametersFilter::OGC_FE )
{ {
// OGC filter // OGC filter
QDomDocument filterXml; QDomDocument filterXml;
QString errorMsg; QString errorMsg;
if ( !filterXml.setContent( filter, true, &errorMsg ) ) if ( !filterXml.setContent( filter.mFilter, true, &errorMsg ) )
{ {
throw QgsBadRequestException( QStringLiteral( "Filter string rejected" ), throw QgsBadRequestException( QStringLiteral( "Filter string rejected" ),
QStringLiteral( "error message: %1. The XML string was: %2" ).arg( errorMsg, filter ) ); QStringLiteral( "error message: %1. The XML string was: %2" ).arg( errorMsg, filter.mFilter ) );
} }
QDomElement filterElem = filterXml.firstChildElement(); QDomElement filterElem = filterXml.firstChildElement();
std::unique_ptr<QgsExpression> expression( QgsOgcUtils::expressionFromOgcFilter( filterElem, filteredLayer ) ); std::unique_ptr<QgsExpression> expression( QgsOgcUtils::expressionFromOgcFilter( filterElem, filter.mVersion, filteredLayer ) );


if ( expression ) if ( expression )
{ {
mFeatureFilter.setFilter( filteredLayer, *expression ); mFeatureFilter.setFilter( filteredLayer, *expression );
} }
} }
else else if ( filter.mType == QgsWmsParametersFilter::SQL )
{ {
// QGIS (SQL) filter // QGIS (SQL) filter
if ( !testFilterStringSafety( filter ) ) if ( !testFilterStringSafety( filter.mFilter ) )
{ {
throw QgsBadRequestException( QStringLiteral( "Filter string rejected" ), throw QgsBadRequestException( QStringLiteral( "Filter string rejected" ),
QStringLiteral( "The filter string %1" QStringLiteral( "The filter string %1"
Expand All @@ -2833,10 +2833,10 @@ namespace QgsWms
" Allowed Keywords and special characters are " " Allowed Keywords and special characters are "
" AND,OR,IN,<,>=,>,>=,!=,',',(,),DMETAPHONE,SOUNDEX." " AND,OR,IN,<,>=,>,>=,!=,',',(,),DMETAPHONE,SOUNDEX."
" Not allowed are semicolons in the filter expression." ).arg( " Not allowed are semicolons in the filter expression." ).arg(
filter ) ); filter.mFilter ) );
} }


QString newSubsetString = filter; QString newSubsetString = filter.mFilter;
if ( !filteredLayer->subsetString().isEmpty() ) if ( !filteredLayer->subsetString().isEmpty() )
{ {
newSubsetString.prepend( ") AND (" ); newSubsetString.prepend( ") AND (" );
Expand Down
2 changes: 1 addition & 1 deletion src/server/services/wms/qgswmsrenderer.h
Expand Up @@ -162,7 +162,7 @@ namespace QgsWms
void setLayerOpacity( QgsMapLayer *layer, int opacity ) const; void setLayerOpacity( QgsMapLayer *layer, int opacity ) const;


// Set layer filter // Set layer filter
void setLayerFilter( QgsMapLayer *layer, const QStringList &filter ); void setLayerFilter( QgsMapLayer *layer, const QList<QgsWmsParametersFilter> &filters );


// Set layer python filter // Set layer python filter
void setLayerAccessControlFilter( QgsMapLayer *layer ) const; void setLayerAccessControlFilter( QgsMapLayer *layer ) const;
Expand Down
53 changes: 53 additions & 0 deletions tests/src/python/test_qgsserver_wms_getmap.py
Expand Up @@ -866,6 +866,59 @@ def test_wms_getmap_filter_ogc_with_empty(self):
r, h = self._result(self._execute_request(qs)) r, h = self._result(self._execute_request(qs))
self._img_diff_error(r, h, "WMS_GetMap_Filter_OGC3") self._img_diff_error(r, h, "WMS_GetMap_Filter_OGC3")


def test_wms_getmap_filter_ogc_v2(self):
# with namespace
filter = ('<fes:Filter xmlns:fes=\"http://www.opengis.net/fes/2.0\">'
'<fes:PropertyIsEqualTo>'
'<fes:ValueReference>name</fes:ValueReference>'
'<fes:Literal>eurasia</fes:Literal>'
'</fes:PropertyIsEqualTo>'
'</fes:Filter>')

qs = "?" + "&".join(["%s=%s" % i for i in list({
"MAP": urllib.parse.quote(self.projectPath),
"SERVICE": "WMS",
"VERSION": "1.1.1",
"REQUEST": "GetMap",
"LAYERS": "Country,Hello",
"STYLES": "",
"FORMAT": "image/png",
"BBOX": "-16817707,-4710778,5696513,14587125",
"HEIGHT": "500",
"WIDTH": "500",
"CRS": "EPSG:3857",
"FILTER": filter
}.items())])

r, h = self._result(self._execute_request(qs))
self._img_diff_error(r, h, "WMS_GetMap_Filter_OGC_V2")

# without namespace (only with prefix)
filter = ('<fes:Filter>'
'<fes:PropertyIsEqualTo>'
'<fes:ValueReference>name</fes:ValueReference>'
'<fes:Literal>eurasia</fes:Literal>'
'</fes:PropertyIsEqualTo>'
'</fes:Filter>')

qs = "?" + "&".join(["%s=%s" % i for i in list({
"MAP": urllib.parse.quote(self.projectPath),
"SERVICE": "WMS",
"VERSION": "1.1.1",
"REQUEST": "GetMap",
"LAYERS": "Country,Hello",
"STYLES": "",
"FORMAT": "image/png",
"BBOX": "-16817707,-4710778,5696513,14587125",
"HEIGHT": "500",
"WIDTH": "500",
"CRS": "EPSG:3857",
"FILTER": filter
}.items())])

r, h = self._result(self._execute_request(qs))
self._img_diff_error(r, h, "WMS_GetMap_Filter_OGC_V2")

def test_wms_getmap_selection(self): def test_wms_getmap_selection(self):
qs = "?" + "&".join(["%s=%s" % i for i in list({ qs = "?" + "&".join(["%s=%s" % i for i in list({
"MAP": urllib.parse.quote(self.projectPath), "MAP": urllib.parse.quote(self.projectPath),
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 268c20f

Please sign in to comment.