Skip to content

Commit

Permalink
Server WFS: handle axis order in BBOX
Browse files Browse the repository at this point in the history
Follows GeoServer convention, fixes #36584

Cherry-picked from master e967fd9
  • Loading branch information
elpaso authored and nyalldawson committed Feb 19, 2021
1 parent ecd8dc4 commit f1b056b
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 1 deletion.
16 changes: 15 additions & 1 deletion src/server/services/wfs/qgswfsgetfeature.cpp
Expand Up @@ -544,7 +544,7 @@ namespace QgsWfs
}

// each Feature requested by FEATUREID can have each own property list
QString key = QStringLiteral( "%1(%2)" ).arg( typeName ).arg( propertyName );
QString key = QStringLiteral( "%1(%2)" ).arg( typeName, propertyName );
QStringList fids;
if ( fidsMap.contains( key ) )
{
Expand Down Expand Up @@ -734,12 +734,15 @@ namespace QgsWfs
// get bbox extent
QgsRectangle extent = mWfsParameters.bboxAsRectangle();

QString extentSrsName { mWfsParameters.srsName() };

// handle WFS 1.1.0 optional CRS
if ( mWfsParameters.bbox().split( ',' ).size() == 5 && ! mWfsParameters.srsName().isEmpty() )
{
QString crs( mWfsParameters.bbox().split( ',' )[4] );
if ( crs != mWfsParameters.srsName() )
{
extentSrsName = crs;
QgsCoordinateReferenceSystem sourceCrs( crs );
QgsCoordinateReferenceSystem destinationCrs( mWfsParameters.srsName() );
if ( sourceCrs.isValid() && destinationCrs.isValid( ) )
Expand All @@ -763,6 +766,17 @@ namespace QgsWfs
}
}

// Follow GeoServer conventions and handle axis order
// See: https://docs.geoserver.org/latest/en/user/services/wfs/axis_order.html#wfs-basics-axis
QgsCoordinateReferenceSystem extentCrs;
extentCrs.createFromUserInput( extentSrsName );
if ( extentCrs.isValid() && extentCrs.hasAxisInverted() && ! extentSrsName.startsWith( QStringLiteral( "EPSG:" ) ) )
{
QgsGeometry geom { QgsGeometry::fromRect( extent ) };
geom.get()->swapXy();
extent = geom.boundingBox();
}

// set feature request filter rectangle
QList<getFeatureQuery>::iterator qIt = request.queries.begin();
for ( ; qIt != request.queries.end(); ++qIt )
Expand Down
55 changes: 55 additions & 0 deletions tests/src/python/test_qgsserver_wfs.py
Expand Up @@ -702,6 +702,61 @@ 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), "Transaction 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)

def _test_getFeature(version, srsName, lat_lon=False):
# Now get the feature through WFS using BBOX filter
bbox = QgsGeometry.fromWkt('point( 10.7006 52.4317)').boundingBox()
bbox.grow(0.0001)
bbox_text = "%s,%s,%s,%s" % ((bbox.yMinimum(), bbox.xMinimum(), bbox.yMaximum(), bbox.xMaximum()) if lat_lon else (bbox.xMinimum(), bbox.yMinimum(), bbox.xMaximum(), bbox.yMaximum()))
req = query_string + '&REQUEST=GetFeature&VERSION={version}&TYPENAME=as_symbols&SRSNAME={srsName}&BBOX={bbox},{srsName}'.format(version=version, srsName=srsName, bbox=bbox_text)
header, body = self._execute_request(req)
self.assertTrue(b'gid>7' in body, "GetFeature Failed: %s , %s, lat_lon=%s" % (version, srsName, lat_lon))

_test_getFeature('1.1.0', 'urn:ogc:def:crs:EPSG::4326', lat_lon=True)
_test_getFeature('1.1.0', 'EPSG:4326', lat_lon=False)

_test_getFeature('1.0.0', 'urn:ogc:def:crs:EPSG::4326', lat_lon=True)
_test_getFeature('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()

0 comments on commit f1b056b

Please sign in to comment.