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();
std::unique_ptr<QgsExpressionNode> expr( nodeFromOgcFilter( operandElem ) );
std::unique_ptr<QgsExpressionNode> leftOp( expr->clone() );

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

std::unique_ptr<QgsExpressionNode> leftOp( expr->clone() );
for ( operandElem = operandElem.nextSiblingElement(); !operandElem.isNull(); operandElem = operandElem.nextSiblingElement() )
{
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;
}

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();
QMultiMap<QString, QString> layerFilters;
QMultiMap<QString, QgsWmsParametersFilter> filters;
for ( int i = 0; i < rawFilters.size(); 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() )
{
Expand All @@ -1266,7 +1282,10 @@ namespace QgsWms
const QStringList splits = f.split( ':' );
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
{
Expand All @@ -1275,7 +1294,7 @@ namespace QgsWms
}
}
}
return layerFilters;
return filters;
}

QList<QgsWmsParametersLayer> QgsWmsParameters::layersParameters() const
Expand All @@ -1284,7 +1303,7 @@ namespace QgsWms
const QStringList styles = allStyles();
const QStringList selection = selections();
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;..."
// several filters can be defined for one layer
Expand Down Expand Up @@ -1316,11 +1335,10 @@ namespace QgsWms
if ( i < opacities.count() )
param.mOpacity = opacities[i];

if ( layerFilters.contains( layer ) )
if ( filters.contains( layer ) )
{
QMultiMap<QString, QString>::const_iterator it;
it = layerFilters.find( layer );
while ( it != layerFilters.end() && it.key() == layer )
auto it = filters.find( layer );
while ( it != filters.end() && it.key() == layer )
{
param.mFilter.append( it.value() );
++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 "qgslegendsettings.h"
#include "qgsprojectversion.h"
#include "qgsogcutils.h"
#include "qgsserverparameters.h"

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
{
QString mNickname; // name, id or short name
int mOpacity = -1;
QStringList mFilter; // list of filter
QList<QgsWmsParametersFilter> mFilter; // list of filter
QStringList mSelection; // list of string fid
QString mStyle;
};
Expand Down Expand Up @@ -1125,7 +1141,7 @@ namespace QgsWms
void raiseError( 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<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 )
{
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
QDomDocument filterXml;
QString errorMsg;
if ( !filterXml.setContent( filter, true, &errorMsg ) )
if ( !filterXml.setContent( filter.mFilter, true, &errorMsg ) )
{
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();
std::unique_ptr<QgsExpression> expression( QgsOgcUtils::expressionFromOgcFilter( filterElem, filteredLayer ) );
std::unique_ptr<QgsExpression> expression( QgsOgcUtils::expressionFromOgcFilter( filterElem, filter.mVersion, filteredLayer ) );

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

QString newSubsetString = filter;
QString newSubsetString = filter.mFilter;
if ( !filteredLayer->subsetString().isEmpty() )
{
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;

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

// Set layer python filter
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))
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):
qs = "?" + "&".join(["%s=%s" % i for i in list({
"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.