Skip to content

Commit

Permalink
Server WFS: handle axis orientation
Browse files Browse the repository at this point in the history
Fixes #36584
  • Loading branch information
elpaso committed Jan 28, 2021
1 parent ed5341b commit 84f61ff
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 7 deletions.
21 changes: 20 additions & 1 deletion src/core/qgsogcutils.cpp
Expand Up @@ -141,9 +141,28 @@ QgsGeometry QgsOgcUtils::geometryFromGML( const QDomNode &geometryNode, const Co

if ( geometryTypeElement.hasAttribute( QStringLiteral( "srsName" ) ) )
{
geomSrs.createFromString( geometryTypeElement.attribute( QStringLiteral( "srsName" ) ) );
QString srsName { geometryTypeElement.attribute( QStringLiteral( "srsName" ) ) };

// The logic here follows WFS GeoServer conventions from https://docs.geoserver.org/latest/en/user/services/wfs/axis_order.html
const bool ignoreAxisOrientation { srsName.startsWith( QStringLiteral( "http://www.opengis.net/gml/srs/" ) ) || srsName.startsWith( QStringLiteral( "EPSG:" ) ) };

// GDAL does not recognise http://www.opengis.net/gml/srs/epsg.xml#4326 but it does
// http://www.opengis.net/def/crs/EPSG/0/4326 so, let's try that
if ( srsName.startsWith( QStringLiteral( "http://www.opengis.net/gml/srs/" ) ) )
{
const auto parts { srsName.split( QRegularExpression( QStringLiteral( R"raw(/|#|\.)raw" ) ) ) };
if ( parts.length() == 10 )
{
srsName = QStringLiteral( "http://www.opengis.net/def/crs/%1/0/%2" ).arg( parts[ 7 ].toUpper(), parts[ 9 ] );
}
}
geomSrs.createFromUserInput( srsName );
if ( geomSrs.isValid() && geomSrs != context.layer->crs() )
{
if ( geomSrs.hasAxisInverted() && ! ignoreAxisOrientation )
{
geometry.get()->swapXy();
}
const QgsCoordinateTransform transformer { geomSrs, context.layer->crs(), context.transformContext };
try
{
Expand Down
2 changes: 1 addition & 1 deletion src/server/services/wfs/qgswfstransaction.cpp
Expand Up @@ -716,7 +716,7 @@ namespace QgsWfs
catch ( QgsOgcServiceException &ex )
{
action.error = true;
action.errorMsg = QStringLiteral( "%1 '%2'" ).arg( ex.message() ).arg( typeName );
action.errorMsg = QStringLiteral( "%1 '%2'" ).arg( ex.message(), typeName );
continue;
}

Expand Down
12 changes: 8 additions & 4 deletions src/server/services/wfs/qgswfstransaction_1_0_0.cpp
Expand Up @@ -687,12 +687,12 @@ namespace QgsWfs
QgsFeatureList featureList;
try
{
featureList = featuresFromGML( action.featureNodeList, provider );
featureList = featuresFromGML( action.featureNodeList, vlayer );
}
catch ( QgsOgcServiceException &ex )
{
action.error = true;
action.errorMsg = QStringLiteral( "%1 '%2'" ).arg( ex.message() ).arg( typeName );
action.errorMsg = QStringLiteral( "%1 '%2'" ).arg( ex.message(), typeName );
continue;
}

Expand Down Expand Up @@ -762,11 +762,14 @@ namespace QgsWfs
filterRestorer.reset();
}

QgsFeatureList featuresFromGML( QDomNodeList featureNodeList, QgsVectorDataProvider *provider )
QgsFeatureList featuresFromGML( QDomNodeList featureNodeList, QgsVectorLayer *layer )
{
// Store the inserted features
QgsFeatureList featList;

const auto provider { layer->dataProvider() };
Q_ASSERT( provider );

// Get Layer Field Information
QgsFields fields = provider->fields();
const QMap<QString, int> fieldMap = provider->fieldNameMap();
Expand Down Expand Up @@ -815,7 +818,8 @@ namespace QgsWfs
}
else //a geometry attribute
{
QgsGeometry g = QgsOgcUtils::geometryFromGML( currentAttributeElement );
const QgsOgcUtils::Context context { layer, provider->transformContext() };
QgsGeometry g = QgsOgcUtils::geometryFromGML( currentAttributeElement, context );
if ( g.isNull() )
{
throw QgsRequestNotWellFormedException( QStringLiteral( "Geometry from GML error on layer insert" ) );
Expand Down
2 changes: 1 addition & 1 deletion src/server/services/wfs/qgswfstransaction_1_0_0.h
Expand Up @@ -110,7 +110,7 @@ namespace QgsWfs
/**
* Transform GML feature nodes to features
*/
QgsFeatureList featuresFromGML( QDomNodeList featureNodeList, QgsVectorDataProvider *provider );
QgsFeatureList featuresFromGML( QDomNodeList featureNodeList, QgsVectorLayer *layer );

/**
* Perform the transaction
Expand Down
40 changes: 40 additions & 0 deletions tests/src/python/test_qgsserver_wfs.py
Expand Up @@ -702,6 +702,46 @@ def test_insert_srsName(self):
geom = feature.geometry()
self.assertEqual(geom.asWkt(0), geom_4326.asWkt(0))

# Tests for inverted axis issue GH #36584
# Cleanup
self.assertTrue(vl.startEditing())
vl.selectByExpression('"name" LIKE \'4326-test%\'')
vl.deleteSelectedFeatures()
self.assertTrue(vl.commitChanges())

self.i = 0

def _test(version, srsName, lat_lon=False):
self.i += 1
name = '4326-test_%s' % self.i
request = post_data.format(
name=name,
version=version,
srsName=srsName,
coordinates='52.48,10.67' if lat_lon else '10.67,52.48'
)
header, body = self._execute_request(
query_string, requestMethod=QgsServerRequest.PostMethod, data=request.encode('utf-8'))
feature = next(vl.getFeatures(QgsFeatureRequest(QgsExpression('"name" = \'%s\'' % name))))
geom = feature.geometry()
self.assertEqual(geom.asWkt(0), geom_4326.asWkt(0), "Failed: %s , %s, lat_lon=%s" % (version, srsName, lat_lon))

_test('1.1.0', 'urn:ogc:def:crs:EPSG::4326', lat_lon=True)
_test('1.1.0', 'http://www.opengis.net/def/crs/EPSG/0/4326', lat_lon=True)
_test('1.1.0', 'http://www.opengis.net/gml/srs/epsg.xml#4326', lat_lon=False)
_test('1.1.0', 'EPSG:4326', lat_lon=False)

_test('1.0.0', 'urn:ogc:def:crs:EPSG::4326', lat_lon=True)
_test('1.0.0', 'http://www.opengis.net/def/crs/EPSG/0/4326', lat_lon=True)
_test('1.0.0', 'http://www.opengis.net/gml/srs/epsg.xml#4326', lat_lon=False)
_test('1.0.0', 'EPSG:4326', lat_lon=False)

# Cleanup
self.assertTrue(vl.startEditing())
vl.selectByExpression('"name" LIKE \'4326-test%\'')
vl.deleteSelectedFeatures()
self.assertTrue(vl.commitChanges())


if __name__ == '__main__':
unittest.main()
Binary file modified tests/testdata/qgis_server/test_project_wms_grouped_layers.gpkg
Binary file not shown.

0 comments on commit 84f61ff

Please sign in to comment.