diff --git a/src/server/services/wfs/qgswfsgetfeature.cpp b/src/server/services/wfs/qgswfsgetfeature.cpp index e95c4c0e276d..be6feee746b6 100644 --- a/src/server/services/wfs/qgswfsgetfeature.cpp +++ b/src/server/services/wfs/qgswfsgetfeature.cpp @@ -414,12 +414,30 @@ namespace QgsWfs geometryName = QLatin1String( "NONE" ); } // outputCrs - QgsCoordinateReferenceSystem outputCrs = vlayer->crs(); + // if the crs is defined in the parameters, use it + // otherwise fallback: + // - geojson uses 'EPSG:4326' by default + // - other formats use the default CRS (the layer's CRS) + const QString requestSrsName = request.serverParameters().value( QStringLiteral( "SRSNAME" ) ); + QString outputSrsName; if ( !query.srsName.isEmpty() ) { - outputCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( query.srsName ); + outputSrsName = query.srsName; + } + else if ( !requestSrsName.isEmpty() ) + { + outputSrsName = requestSrsName; + } + else + { + // fallback to a default value + // geojson uses 'EPSG:4326' by default + outputSrsName = ( aRequest.outputFormat == QgsWfsParameters::Format::GeoJSON ) ? QStringLiteral( "EPSG:4326" ) : vlayer->crs().authid(); } + QgsCoordinateReferenceSystem outputCrs; + outputCrs.createFromUserInput( outputSrsName ); + bool forceGeomToMulti = QgsWkbTypes::isMultiType( vlayer->wkbType() ); if ( !featureRequest.filterRect().isEmpty() ) @@ -465,19 +483,9 @@ namespace QgsWfs // It needs to be an EPSG urn, e.g. urn:ogc:def:crs:EPSG::4326 // This follows geoserver convention // See: https://docs.geoserver.org/stable/en/user/services/wfs/axis_order.html - // if the crs is defined in the parameters, use it - // otherwise: - // - geojson uses 'EPSG:4326' by default - // - other formats use the default CRS (DefaultSRS, which is the layer's CRS) - const QString requestSrsName = request.serverParameters().value( QStringLiteral( "SRSNAME" ) ); - const QString srsName - { - !requestSrsName.isEmpty() ? requestSrsName : - ( aRequest.outputFormat == QgsWfsParameters::Format::GeoJSON ? QStringLiteral( "EPSG:4326" ) : outputCrs.authid() ) - }; const bool invertAxis { mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) && outputCrs.hasAxisInverted() && - ! srsName.startsWith( QLatin1String( "EPSG:" ) ) }; + ! outputSrsName.startsWith( QLatin1String( "EPSG:" ) ) }; const createFeatureParams cfp = { layerPrecision, layerCrs, @@ -487,7 +495,7 @@ namespace QgsWfs geometryName, outputCrs, forceGeomToMulti, - srsName, + outputSrsName, invertAxis }; while ( fit.nextFeature( feature ) && ( aRequest.maxFeatures == -1 || sentFeatures < aRequest.maxFeatures ) ) @@ -1275,21 +1283,15 @@ namespace QgsWfs QDomElement bbElem = doc.createElement( QStringLiteral( "gml:boundedBy" ) ); if ( format == QgsWfsParameters::Format::GML3 ) { - // For WFS 1.1 we honor requested CRS and axis order - // Axis is not inverted if srsName starts with EPSG - // It needs to be an EPSG urn, e.g. urn:ogc:def:crs:EPSG::4326 - // This follows geoserver convention - // See: https://docs.geoserver.org/stable/en/user/services/wfs/axis_order.html + // If requested SRS (outputSrsName) is different from rect CRS (crs) we need to transform the envelope const QString requestSrsName = request.serverParameters().value( QStringLiteral( "SRSNAME" ) ); - const QString srsName = !requestSrsName.isEmpty() ? requestSrsName : crs.authid(); - const bool invertAxis { mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) && - crs.hasAxisInverted() && - ! srsName.startsWith( QLatin1String( "EPSG:" ) ) }; + const QString outputSrsName = !requestSrsName.isEmpty() ? requestSrsName : crs.authid(); + QgsCoordinateReferenceSystem outputCrs; + outputCrs.createFromUserInput( outputSrsName ); - // If requested SRS (srsName) is different from rect CRS (crs) we need to transform the envelope QgsCoordinateTransform transform; transform.setSourceCrs( crs ); - transform.setDestinationCrs( QgsCoordinateReferenceSystem( srsName ) ); + transform.setDestinationCrs( outputCrs ); QgsRectangle crsCorrectedRect { rect ? *rect : QgsRectangle() }; try @@ -1301,10 +1303,19 @@ namespace QgsWfs Q_UNUSED( cse ) } - QDomElement envElem = QgsOgcUtils::rectangleToGMLEnvelope( &crsCorrectedRect, doc, srsName, invertAxis, prec ); + // For WFS 1.1 we honor requested CRS and axis order + // Axis is not inverted if srsName starts with EPSG + // It needs to be an EPSG urn, e.g. urn:ogc:def:crs:EPSG::4326 + // This follows geoserver convention + // See: https://docs.geoserver.org/stable/en/user/services/wfs/axis_order.html + const bool invertAxis { mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) && + outputCrs.hasAxisInverted() && + !outputSrsName.startsWith( QLatin1String( "EPSG:" ) ) }; + + QDomElement envElem = QgsOgcUtils::rectangleToGMLEnvelope( &crsCorrectedRect, doc, outputSrsName, invertAxis, prec ); if ( !envElem.isNull() ) { - if ( crs.isValid() && srsName.isEmpty() ) + if ( crs.isValid() && outputSrsName.isEmpty() ) { envElem.setAttribute( QStringLiteral( "srsName" ), crs.authid() ); } diff --git a/tests/src/python/test_qgsserver_wfs.py b/tests/src/python/test_qgsserver_wfs.py index 37503a36131c..ccc40f456378 100644 --- a/tests/src/python/test_qgsserver_wfs.py +++ b/tests/src/python/test_qgsserver_wfs.py @@ -287,7 +287,7 @@ def test_getfeature_post(self): tests = [] template = """ - + @@ -301,15 +301,20 @@ def test_getfeature_post(self): """ - tests.append(('nobbox_post', template.format(""))) - tests.append(('startindex2_post', template.format('startIndex="2"'))) - tests.append(('limit2_post', template.format('maxFeatures="2"'))) - tests.append(('start1_limit1_post', template.format( - 'startIndex="1" maxFeatures="1"'))) - - srsTemplate = """ - - + for version in ['1.0.0', '1.1.0']: + version_underscore = '_' + version.replace(".", "_") + tests.append((f'nobbox_post{version_underscore}', template.format( + version, ""))) + tests.append((f'startindex2_post{version_underscore}', template.format( + version, 'startIndex="2"'))) + tests.append((f'limit2_post{version_underscore}', template.format( + version, 'maxFeatures="2"'))) + tests.append((f'start1_limit1_post{version_underscore}', template.format( + version, 'startIndex="1" maxFeatures="1"'))) + + srsTemplate = """ + + geometry @@ -322,7 +327,12 @@ def test_getfeature_post(self): """ - tests.append(('srsname_post', srsTemplate.format(""))) + tests.append(('srsname_post_1_0_0', srsTemplate.format( + '1.0.0', '', 'srsName="EPSG:3857"'))) + tests.append(('srsname_post_1_1_0', srsTemplate.format( + '1.1.0', '', 'srsName="EPSG:3857"'))) + tests.append(('srsname_post_1_1_0_urn', srsTemplate.format( + '1.1.0', '', 'srsName="urn:ogc:def:crs:EPSG::3857"'))) # Issue https://github.com/qgis/QGIS/issues/36398 # Check get feature within polygon having srsName=EPSG:4326 (same as the project/layer) @@ -494,7 +504,7 @@ def test_getfeature_post(self): """ - tests.append(('nobbox_post', template.format(""))) + tests.append(('nobbox_post_1_0_0', template.format(""))) template = """ @@ -511,7 +521,7 @@ def test_getfeature_post(self): """ - tests.append(('nobbox_post', template.format(""))) + tests.append(('nobbox_post_1_0_0', template.format(""))) for id, req in tests: self.wfs_getfeature_post_compare(id, req) @@ -572,11 +582,16 @@ def test_getFeatureFeatureId(self): "GetFeature", '1.0.0', "SRSNAME=EPSG:4326&TYPENAME=testlayer&FEATUREID=testlayer.0&PROPERTYNAME=*", 'wfs_getFeature_1_0_0_featureid_0') def test_getFeatureFeature11urn(self): - """Test GetFeature with SRSNAME urn:ogc:def:crs:EPSG::4326""" + """Test GetFeature with SRSNAME as urn:ogc:def:crs:EPSG::X""" + # urn:ogc:def:crs:EPSG::4326 self.wfs_request_compare( "GetFeature", '1.1.0', "SRSNAME=urn:ogc:def:crs:EPSG::4326&TYPENAME=testlayer&FEATUREID=testlayer.0", 'wfs_getFeature_1_1_0_featureid_0_1_1_0') + # urn:ogc:def:crs:EPSG::3857 + self.wfs_request_compare( + "GetFeature", '1.1.0', "SRSNAME=urn:ogc:def:crs:EPSG::3857&TYPENAME=testlayer&FEATUREID=testlayer.0", 'wfs_getFeature_1_1_0_featureid_0_1_1_0_epsg3857') + def test_get_feature_srsname_empty(self): """Test GetFeature with an empty SRSNAME.""" self.wfs_request_compare( diff --git a/tests/testdata/qgis_server/wfs_getFeature_1_1_0_featureid_0_1_1_0_epsg3857.txt b/tests/testdata/qgis_server/wfs_getFeature_1_1_0_featureid_0_1_1_0_epsg3857.txt new file mode 100644 index 000000000000..1cd1b8d4c572 --- /dev/null +++ b/tests/testdata/qgis_server/wfs_getFeature_1_1_0_featureid_0_1_1_0_epsg3857.txt @@ -0,0 +1,28 @@ +Content-Type: text/xml; subtype=gml/3.1.1; charset=utf-8 + + + + + 913204.91280263 5606011.45647302 + 913214.67407005 5606025.23730414 + + + + + + + 913209.03579284 5606025.23730414 + 913209.03579284 5606025.23730414 + + + + + 913209.03579284 5606025.23730414 + + + 1 + one + one èé + + + diff --git a/tests/testdata/qgis_server/wfs_getfeature_limit2_post.txt b/tests/testdata/qgis_server/wfs_getfeature_limit2_post_1_0_0.txt similarity index 100% rename from tests/testdata/qgis_server/wfs_getfeature_limit2_post.txt rename to tests/testdata/qgis_server/wfs_getfeature_limit2_post_1_0_0.txt diff --git a/tests/testdata/qgis_server/wfs_getfeature_limit2_post_1_1_0.txt b/tests/testdata/qgis_server/wfs_getfeature_limit2_post_1_1_0.txt new file mode 100644 index 000000000000..c8626c1f6b73 --- /dev/null +++ b/tests/testdata/qgis_server/wfs_getfeature_limit2_post_1_1_0.txt @@ -0,0 +1,46 @@ +Content-Type: text/xml; subtype=gml/3.1.1; charset=utf-8 + + + + + 8 44 + 9 45 + + + + + + + 8.20349634 44.90148253 + 8.20349634 44.90148253 + + + + + 8.20349634 44.90148253 + + + 1 + one + one èé + + + + + + + 8.20354699 44.90143568 + 8.20354699 44.90143568 + + + + + 8.20354699 44.90143568 + + + 2 + two + two àò + + + diff --git a/tests/testdata/qgis_server/wfs_getfeature_nobbox_post.txt b/tests/testdata/qgis_server/wfs_getfeature_nobbox_post_1_0_0.txt similarity index 100% rename from tests/testdata/qgis_server/wfs_getfeature_nobbox_post.txt rename to tests/testdata/qgis_server/wfs_getfeature_nobbox_post_1_0_0.txt diff --git a/tests/testdata/qgis_server/wfs_getfeature_nobbox_post_1_1_0.txt b/tests/testdata/qgis_server/wfs_getfeature_nobbox_post_1_1_0.txt new file mode 100644 index 000000000000..4de8144ecaaf --- /dev/null +++ b/tests/testdata/qgis_server/wfs_getfeature_nobbox_post_1_1_0.txt @@ -0,0 +1,64 @@ +Content-Type: text/xml; subtype=gml/3.1.1; charset=utf-8 + + + + + 8 44 + 9 45 + + + + + + + 8.20349634 44.90148253 + 8.20349634 44.90148253 + + + + + 8.20349634 44.90148253 + + + 1 + one + one èé + + + + + + + 8.20354699 44.90143568 + 8.20354699 44.90143568 + + + + + 8.20354699 44.90143568 + + + 2 + two + two àò + + + + + + + 8.20345931 44.90139484 + 8.20345931 44.90139484 + + + + + 8.20345931 44.90139484 + + + 3 + three + three èé↓ + + + diff --git a/tests/testdata/qgis_server/wfs_getfeature_nobbox_post_urn4326.txt b/tests/testdata/qgis_server/wfs_getfeature_nobbox_post_urn4326.txt new file mode 100644 index 000000000000..63afe6c48a68 --- /dev/null +++ b/tests/testdata/qgis_server/wfs_getfeature_nobbox_post_urn4326.txt @@ -0,0 +1,60 @@ +Content-Type: text/xml; subtype=gml/2.1.2; charset=utf-8 + + + + + 8,44 9,45 + + + + + + + 8.20349634,44.90148253 8.20349634,44.90148253 + + + + + 8.20349634,44.90148253 + + + 1 + one + one èé + + + + + + + 8.20354699,44.90143568 8.20354699,44.90143568 + + + + + 8.20354699,44.90143568 + + + 2 + two + two àò + + + + + + + 8.20345931,44.90139484 8.20345931,44.90139484 + + + + + 8.20345931,44.90139484 + + + 3 + three + three èé↓ + + + diff --git a/tests/testdata/qgis_server/wfs_getfeature_srsname_post.txt b/tests/testdata/qgis_server/wfs_getfeature_srsname_post_1_0_0.txt similarity index 100% rename from tests/testdata/qgis_server/wfs_getfeature_srsname_post.txt rename to tests/testdata/qgis_server/wfs_getfeature_srsname_post_1_0_0.txt diff --git a/tests/testdata/qgis_server/wfs_getfeature_srsname_post_1_1_0.txt b/tests/testdata/qgis_server/wfs_getfeature_srsname_post_1_1_0.txt new file mode 100644 index 000000000000..8530a8ad084b --- /dev/null +++ b/tests/testdata/qgis_server/wfs_getfeature_srsname_post_1_1_0.txt @@ -0,0 +1,64 @@ +Content-Type: text/xml; subtype=gml/3.1.1; charset=utf-8 + + + + + 8 44 + 9 45 + + + + + + + 913209.03579284 5606025.23730414 + 913209.03579284 5606025.23730414 + + + + + 913209.03579284 5606025.23730414 + + + 1 + one + one èé + + + + + + + 913214.67407005 5606017.87425818 + 913214.67407005 5606017.87425818 + + + + + 913214.67407005 5606017.87425818 + + + 2 + two + two àò + + + + + + + 913204.91280263 5606011.45647302 + 913204.91280263 5606011.45647302 + + + + + 913204.91280263 5606011.45647302 + + + 3 + three + three èé↓ + + + diff --git a/tests/testdata/qgis_server/wfs_getfeature_srsname_post_1_1_0_urn.txt b/tests/testdata/qgis_server/wfs_getfeature_srsname_post_1_1_0_urn.txt new file mode 100644 index 000000000000..f2ec396f1ce8 --- /dev/null +++ b/tests/testdata/qgis_server/wfs_getfeature_srsname_post_1_1_0_urn.txt @@ -0,0 +1,64 @@ +Content-Type: text/xml; subtype=gml/3.1.1; charset=utf-8 + + + + + 8 44 + 9 45 + + + + + + + 913209.03579284 5606025.23730414 + 913209.03579284 5606025.23730414 + + + + + 913209.03579284 5606025.23730414 + + + 1 + one + one èé + + + + + + + 913214.67407005 5606017.87425818 + 913214.67407005 5606017.87425818 + + + + + 913214.67407005 5606017.87425818 + + + 2 + two + two àò + + + + + + + 913204.91280263 5606011.45647302 + 913204.91280263 5606011.45647302 + + + + + 913204.91280263 5606011.45647302 + + + 3 + three + three èé↓ + + + diff --git a/tests/testdata/qgis_server/wfs_getfeature_start1_limit1_post.txt b/tests/testdata/qgis_server/wfs_getfeature_start1_limit1_post_1_0_0.txt similarity index 100% rename from tests/testdata/qgis_server/wfs_getfeature_start1_limit1_post.txt rename to tests/testdata/qgis_server/wfs_getfeature_start1_limit1_post_1_0_0.txt diff --git a/tests/testdata/qgis_server/wfs_getfeature_start1_limit1_post_1_1_0.txt b/tests/testdata/qgis_server/wfs_getfeature_start1_limit1_post_1_1_0.txt new file mode 100644 index 000000000000..5296986f83d3 --- /dev/null +++ b/tests/testdata/qgis_server/wfs_getfeature_start1_limit1_post_1_1_0.txt @@ -0,0 +1,28 @@ +Content-Type: text/xml; subtype=gml/3.1.1; charset=utf-8 + + + + + 8 44 + 9 45 + + + + + + + 8.20354699 44.90143568 + 8.20354699 44.90143568 + + + + + 8.20354699 44.90143568 + + + 2 + two + two àò + + + diff --git a/tests/testdata/qgis_server/wfs_getfeature_startindex2_post.txt b/tests/testdata/qgis_server/wfs_getfeature_startindex2_post_1_0_0.txt similarity index 100% rename from tests/testdata/qgis_server/wfs_getfeature_startindex2_post.txt rename to tests/testdata/qgis_server/wfs_getfeature_startindex2_post_1_0_0.txt diff --git a/tests/testdata/qgis_server/wfs_getfeature_startindex2_post_1_1_0.txt b/tests/testdata/qgis_server/wfs_getfeature_startindex2_post_1_1_0.txt new file mode 100644 index 000000000000..4fda9ef5f154 --- /dev/null +++ b/tests/testdata/qgis_server/wfs_getfeature_startindex2_post_1_1_0.txt @@ -0,0 +1,28 @@ +Content-Type: text/xml; subtype=gml/3.1.1; charset=utf-8 + + + + + 8 44 + 9 45 + + + + + + + 8.20345931 44.90139484 + 8.20345931 44.90139484 + + + + + 8.20345931 44.90139484 + + + 3 + three + three èé↓ + + +