diff --git a/python/core/auto_generated/qgsogcutils.sip.in b/python/core/auto_generated/qgsogcutils.sip.in
index ea0881f9c7bc..46bea48f91aa 100644
--- a/python/core/auto_generated/qgsogcutils.sip.in
+++ b/python/core/auto_generated/qgsogcutils.sip.in
@@ -25,6 +25,17 @@ Currently supported standards:
%End
public:
+ struct Context
+ {
+
+ Context( const QgsMapLayer *layer = 0, const QgsCoordinateTransformContext &transformContext = QgsCoordinateTransformContext() );
+%Docstring
+Constructs a Context from ``layer`` and ``transformContext``
+%End
+ const QgsMapLayer *layer;
+ QgsCoordinateTransformContext transformContext;
+ };
+
enum GMLVersion
{
GML_2_1_2,
@@ -32,16 +43,17 @@ Currently supported standards:
GML_3_2_1,
};
- static QgsGeometry geometryFromGML( const QString &xmlString );
+ static QgsGeometry geometryFromGML( const QString &xmlString, const Context &context = Context() );
%Docstring
Static method that creates geometry from GML
:param xmlString: xml representation of the geometry. GML elements are expected to be
in default namespace (\verbatim {... \endverbatim) or in
"gml" namespace (\verbatim ... \endverbatim)
+:param context: QgsOgcUtils context
%End
- static QgsGeometry geometryFromGML( const QDomNode &geometryNode );
+ static QgsGeometry geometryFromGML( const QDomNode &geometryNode, const Context &context = Context() );
%Docstring
Static method that creates geometry from GML
%End
diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp
index 7df781317a89..a905f6da9e92 100644
--- a/src/core/expression/qgsexpressionfunction.cpp
+++ b/src/core/expression/qgsexpressionfunction.cpp
@@ -3122,10 +3122,20 @@ static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionC
return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
}
-static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
+static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
- QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml );
+ QgsOgcUtils::Context ogcContext;
+ if ( context )
+ {
+ QgsWeakMapLayerPointer mapLayerPtr {context->variable( QStringLiteral( "layer" ) ).value() };
+ if ( mapLayerPtr )
+ {
+ ogcContext.layer = mapLayerPtr.data();
+ ogcContext.transformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value();
+ }
+ }
+ QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
return result;
}
diff --git a/src/core/qgsogcutils.cpp b/src/core/qgsogcutils.cpp
index 986e262d5b1e..ebbd64f66a55 100644
--- a/src/core/qgsogcutils.cpp
+++ b/src/core/qgsogcutils.cpp
@@ -24,6 +24,7 @@
#include "qgsrectangle.h"
#include "qgsvectorlayer.h"
#include "qgsexpressioncontextutils.h"
+#include "qgslogger.h"
#include
#include
@@ -72,10 +73,11 @@ QgsOgcUtilsExprToFilter::QgsOgcUtilsExprToFilter( QDomDocument &doc,
}
}
-QgsGeometry QgsOgcUtils::geometryFromGML( const QDomNode &geometryNode )
+QgsGeometry QgsOgcUtils::geometryFromGML( const QDomNode &geometryNode, const Context &context )
{
QDomElement geometryTypeElement = geometryNode.toElement();
QString geomType = geometryTypeElement.tagName();
+ QgsGeometry geometry;
if ( !( geomType == QLatin1String( "Point" ) || geomType == QLatin1String( "LineString" ) || geomType == QLatin1String( "Polygon" ) ||
geomType == QLatin1String( "MultiPoint" ) || geomType == QLatin1String( "MultiLineString" ) || geomType == QLatin1String( "MultiPolygon" ) ||
@@ -84,7 +86,7 @@ QgsGeometry QgsOgcUtils::geometryFromGML( const QDomNode &geometryNode )
QDomNode geometryChild = geometryNode.firstChild();
if ( geometryChild.isNull() )
{
- return QgsGeometry();
+ return geometry;
}
geometryTypeElement = geometryChild.toElement();
geomType = geometryTypeElement.tagName();
@@ -97,43 +99,72 @@ QgsGeometry QgsOgcUtils::geometryFromGML( const QDomNode &geometryNode )
if ( geomType == QLatin1String( "Point" ) )
{
- return geometryFromGMLPoint( geometryTypeElement );
+ geometry = geometryFromGMLPoint( geometryTypeElement );
}
else if ( geomType == QLatin1String( "LineString" ) )
{
- return geometryFromGMLLineString( geometryTypeElement );
+ geometry = geometryFromGMLLineString( geometryTypeElement );
}
else if ( geomType == QLatin1String( "Polygon" ) )
{
- return geometryFromGMLPolygon( geometryTypeElement );
+ geometry = geometryFromGMLPolygon( geometryTypeElement );
}
else if ( geomType == QLatin1String( "MultiPoint" ) )
{
- return geometryFromGMLMultiPoint( geometryTypeElement );
+ geometry = geometryFromGMLMultiPoint( geometryTypeElement );
}
else if ( geomType == QLatin1String( "MultiLineString" ) )
{
- return geometryFromGMLMultiLineString( geometryTypeElement );
+ geometry = geometryFromGMLMultiLineString( geometryTypeElement );
}
else if ( geomType == QLatin1String( "MultiPolygon" ) )
{
- return geometryFromGMLMultiPolygon( geometryTypeElement );
+ geometry = geometryFromGMLMultiPolygon( geometryTypeElement );
}
else if ( geomType == QLatin1String( "Box" ) )
{
- return QgsGeometry::fromRect( rectangleFromGMLBox( geometryTypeElement ) );
+ geometry = QgsGeometry::fromRect( rectangleFromGMLBox( geometryTypeElement ) );
}
else if ( geomType == QLatin1String( "Envelope" ) )
{
- return QgsGeometry::fromRect( rectangleFromGMLEnvelope( geometryTypeElement ) );
+ geometry = QgsGeometry::fromRect( rectangleFromGMLEnvelope( geometryTypeElement ) );
}
else //unknown type
{
- return QgsGeometry();
+ return geometry;
}
+
+ // Handle srsName if context has information about the layer and the transformation context
+ if ( context.layer )
+ {
+ QgsCoordinateReferenceSystem geomSrs;
+
+ if ( geometryTypeElement.hasAttribute( QStringLiteral( "srsName" ) ) )
+ {
+ geomSrs.createFromString( geometryTypeElement.attribute( QStringLiteral( "srsName" ) ) );
+ if ( geomSrs.isValid() && geomSrs != context.layer->crs() )
+ {
+ const QgsCoordinateTransform transformer { geomSrs, context.layer->crs(), context.transformContext };
+ try
+ {
+ const QgsGeometry::OperationResult result = geometry.transform( transformer );
+ if ( result != QgsGeometry::OperationResult::Success )
+ {
+ QgsDebugMsgLevel( QStringLiteral( "Error transforming geometry: %1" ).arg( result ), 2 );
+ }
+ }
+ catch ( QgsCsException & )
+ {
+ QgsDebugMsgLevel( QStringLiteral( "CS error transforming geometry" ), 2 );
+ }
+ }
+ }
+ }
+
+ return geometry;
}
-QgsGeometry QgsOgcUtils::geometryFromGML( const QString &xmlString )
+QgsGeometry QgsOgcUtils::geometryFromGML( const QString &xmlString, const Context &context )
{
// wrap the string into a root tag to have "gml" namespace (and also as a default namespace)
QString xml = QStringLiteral( "%2" ).arg( GML_NAMESPACE, xmlString );
@@ -141,7 +172,7 @@ QgsGeometry QgsOgcUtils::geometryFromGML( const QString &xmlString )
if ( !doc.setContent( xml, true ) )
return QgsGeometry();
- return geometryFromGML( doc.documentElement().firstChildElement() );
+ return geometryFromGML( doc.documentElement().firstChildElement(), context );
}
@@ -328,7 +359,8 @@ QgsGeometry QgsOgcUtils::geometryFromGMLPolygon( const QDomElement &geometryElem
{
return QgsGeometry();
}
- if ( readGMLPositions( interiorPointList, posElement ) != 0 )
+ // Note: readGMLPositions returns true on errors and false on success
+ if ( readGMLPositions( interiorPointList, posElement ) )
{
return QgsGeometry();
}
diff --git a/src/core/qgsogcutils.h b/src/core/qgsogcutils.h
index 89cb7164603c..75958ca0353e 100644
--- a/src/core/qgsogcutils.h
+++ b/src/core/qgsogcutils.h
@@ -50,6 +50,25 @@ class CORE_EXPORT QgsOgcUtils
{
public:
+ /**
+ * The Context struct stores the current layer and coordinate transform context.
+ * \since QGIS 3.14
+ */
+ struct Context
+ {
+
+ /**
+ * Constructs a Context from \a layer and \a transformContext
+ */
+ Context( const QgsMapLayer *layer = nullptr, const QgsCoordinateTransformContext &transformContext = QgsCoordinateTransformContext() )
+ : layer( layer )
+ , transformContext( transformContext )
+ {
+ }
+ const QgsMapLayer *layer = nullptr;
+ QgsCoordinateTransformContext transformContext;
+ };
+
/**
*GML version
*/
@@ -62,16 +81,17 @@ class CORE_EXPORT QgsOgcUtils
/**
* Static method that creates geometry from GML
- \param xmlString xml representation of the geometry. GML elements are expected to be
- in default namespace (\verbatim {... \endverbatim) or in
- "gml" namespace (\verbatim ... \endverbatim)
+ * \param xmlString xml representation of the geometry. GML elements are expected to be
+ * in default namespace (\verbatim {... \endverbatim) or in
+ * "gml" namespace (\verbatim ... \endverbatim)
+ * \param context QgsOgcUtils context
*/
- static QgsGeometry geometryFromGML( const QString &xmlString );
+ static QgsGeometry geometryFromGML( const QString &xmlString, const Context &context = Context() );
/**
* Static method that creates geometry from GML
*/
- static QgsGeometry geometryFromGML( const QDomNode &geometryNode );
+ static QgsGeometry geometryFromGML( const QDomNode &geometryNode, const Context &context = Context() );
//! Read rectangle from GML2 Box
static QgsRectangle rectangleFromGMLBox( const QDomNode &boxNode );
@@ -279,7 +299,8 @@ class CORE_EXPORT QgsOgcUtils
* Reads the \verbatim \endverbatim element and extracts the coordinates as points
\param coords list where the found coordinates are appended
\param elem the \verbatim \endverbatim element
- \returns boolean for success*/
+ \returns boolean FALSE on success
+ */
static bool readGMLCoordinates( QgsPolylineXY &coords, const QDomElement &elem );
/**
@@ -288,7 +309,8 @@ class CORE_EXPORT QgsOgcUtils
\param coords list where the found coordinates are appended
\param elem the \verbatim \endverbatim or
\verbatim \endverbatim element
- \returns boolean for success*/
+ \returns boolean FALSE on success
+ */
static bool readGMLPositions( QgsPolylineXY &coords, const QDomElement &elem );
diff --git a/src/server/services/wfs/qgswfsutils.cpp b/src/server/services/wfs/qgswfsutils.cpp
index 2f6b67c85e30..bd10df1d34fc 100644
--- a/src/server/services/wfs/qgswfsutils.cpp
+++ b/src/server/services/wfs/qgswfsutils.cpp
@@ -100,14 +100,14 @@ namespace QgsWfs
return nullptr;
}
- QgsFeatureRequest parseFilterElement( const QString &typeName, QDomElement &filterElem, const QgsProject *project )
+ QgsFeatureRequest parseFilterElement( const QString &typeName, QDomElement &filterElem, QgsProject *project )
{
// Get the server feature ids in filter element
QStringList collectedServerFids;
return parseFilterElement( typeName, filterElem, collectedServerFids, project );
}
- QgsFeatureRequest parseFilterElement( const QString &typeName, QDomElement &filterElem, QStringList &serverFids, const QgsProject *project )
+ QgsFeatureRequest parseFilterElement( const QString &typeName, QDomElement &filterElem, QStringList &serverFids, const QgsProject *project, const QgsMapLayer *layer )
{
QgsFeatureRequest request;
@@ -147,7 +147,7 @@ namespace QgsWfs
}
else if ( !goidNodes.isEmpty() )
{
- // Get the server feature idsin filter element
+ // Get the server feature ids in filter element
QStringList collectedServerFids;
QDomElement goidElem;
for ( int f = 0; f < goidNodes.size(); f++ )
@@ -192,7 +192,8 @@ namespace QgsWfs
}
else if ( childElem.tagName() != QLatin1String( "PropertyName" ) )
{
- QgsGeometry geom = QgsOgcUtils::geometryFromGML( childElem );
+ QgsOgcUtils::Context ctx { layer, project ? project->transformContext() : QgsCoordinateTransformContext() };
+ QgsGeometry geom = QgsOgcUtils::geometryFromGML( childElem, ctx );
request.setFilterRect( geom.boundingBox() );
}
childElem = childElem.nextSiblingElement();
diff --git a/src/server/services/wfs/qgswfsutils.h b/src/server/services/wfs/qgswfsutils.h
index 009169199ec6..76b8c2636f4a 100644
--- a/src/server/services/wfs/qgswfsutils.h
+++ b/src/server/services/wfs/qgswfsutils.h
@@ -60,12 +60,12 @@ namespace QgsWfs
/**
* Transform a Filter element to a feature request
*/
- QgsFeatureRequest parseFilterElement( const QString &typeName, QDomElement &filterElem, const QgsProject *project = nullptr );
+ QgsFeatureRequest parseFilterElement( const QString &typeName, QDomElement &filterElem, QgsProject *project = nullptr );
/**
* Transform a Filter element to a feature request and update server feature ids
*/
- QgsFeatureRequest parseFilterElement( const QString &typeName, QDomElement &filterElem, QStringList &serverFids, const QgsProject *project = nullptr );
+ QgsFeatureRequest parseFilterElement( const QString &typeName, QDomElement &filterElem, QStringList &serverFids, const QgsProject *project = nullptr, const QgsMapLayer *layer = nullptr );
// Define namespaces used in WFS documents
const QString WFS_NAMESPACE = QStringLiteral( "http://www.opengis.net/wfs" );
diff --git a/tests/src/python/test_qgsserver_wfs.py b/tests/src/python/test_qgsserver_wfs.py
index 7dd0bbaf480f..210f1b8c29c2 100644
--- a/tests/src/python/test_qgsserver_wfs.py
+++ b/tests/src/python/test_qgsserver_wfs.py
@@ -257,6 +257,62 @@ def test_getfeature_post(self):
"""
tests.append(('srsname_post', srsTemplate.format("")))
+ # Issue https://github.com/qgis/QGIS/issues/36398
+ # Check get feature within polygon having srsName=EPSG:4326 (same as the project/layer)
+ within4326FilterTemplate = """
+
+
+
+
+ geometry
+
+
+
+
+ 8.20344131 44.90137909
+ 8.20347748 44.90137909
+ 8.20347748 44.90141005
+ 8.20344131 44.90141005
+ 8.20344131 44.90137909
+
+
+
+
+
+
+
+
+"""
+ tests.append(('within4326FilterTemplate_post', within4326FilterTemplate.format("")))
+
+ # Check get feature within polygon having srsName=EPSG:3857 (different from the project/layer)
+ # The coordinates are converted from the one in 4326
+ within3857FilterTemplate = """
+
+
+
+
+ geometry
+
+
+
+
+ 913202.90938171 5606008.98136456
+ 913206.93580769 5606008.98136456
+ 913206.93580769 5606013.84701639
+ 913202.90938171 5606013.84701639
+ 913202.90938171 5606008.98136456
+
+
+
+
+
+
+
+
+"""
+ tests.append(('within3857FilterTemplate_post', within3857FilterTemplate.format("")))
+
srsTwoLayersTemplate = """