Skip to content

Commit

Permalink
[Feat] Add crs information in WMS GetFeatureInfo output
Browse files Browse the repository at this point in the history
  • Loading branch information
troopa81 committed Feb 29, 2024
1 parent d0d7465 commit 5046beb
Show file tree
Hide file tree
Showing 18 changed files with 168 additions and 3 deletions.
Expand Up @@ -1000,6 +1000,14 @@ projection in the WGS 84 CRS.
Returns the crs as OGC URI (format: http://www.opengis.net/def/crs/OGC/1.3/CRS84)
Returns an empty string on failure.

.. versionadded:: 3.30
%End

QString toOgcUrn() const;
%Docstring
Returns the crs as OGC URN (format: urn:ogc:def:crs:OGC:1.3:CRS84)
Returns an empty string on failure.

.. versionadded:: 3.30
%End

Expand Down
1 change: 1 addition & 0 deletions python/PyQt6/core/auto_generated/qgsjsonutils.sip.in
Expand Up @@ -369,6 +369,7 @@ Returns a null geometry if the geometry could not be parsed.




};

/************************************************************************
Expand Down
Expand Up @@ -1000,6 +1000,14 @@ projection in the WGS 84 CRS.
Returns the crs as OGC URI (format: http://www.opengis.net/def/crs/OGC/1.3/CRS84)
Returns an empty string on failure.

.. versionadded:: 3.30
%End

QString toOgcUrn() const;
%Docstring
Returns the crs as OGC URN (format: urn:ogc:def:crs:OGC:1.3:CRS84)
Returns an empty string on failure.

.. versionadded:: 3.30
%End

Expand Down
1 change: 1 addition & 0 deletions python/core/auto_generated/qgsjsonutils.sip.in
Expand Up @@ -369,6 +369,7 @@ Returns a null geometry if the geometry could not be parsed.




};

/************************************************************************
Expand Down
24 changes: 24 additions & 0 deletions src/core/proj/qgscoordinatereferencesystem.cpp
Expand Up @@ -1591,6 +1591,30 @@ QString QgsCoordinateReferenceSystem::toOgcUri() const
return QString();
}

QString QgsCoordinateReferenceSystem::toOgcUrn() const
{
const auto parts { authid().split( ':' ) };
if ( parts.length() == 2 )
{
if ( parts[0] == QLatin1String( "EPSG" ) )
return QStringLiteral( "urn:ogc:def:crs:EPSG:0:%1" ).arg( parts[1] );
else if ( parts[0] == QLatin1String( "OGC" ) )
{
return QStringLiteral( "urn:ogc:def:crs:OGC:1.3:%1" ).arg( parts[1] );
}
else
{
QgsMessageLog::logMessage( QStringLiteral( "Error converting published CRS to URN %1: (not OGC or EPSG)" ).arg( authid() ), QStringLiteral( "CRS" ), Qgis::MessageLevel::Critical );
}
}
else
{
QgsMessageLog::logMessage( QStringLiteral( "Error converting published CRS to URN: %1" ).arg( authid() ), QStringLiteral( "CRS" ), Qgis::MessageLevel::Critical );
}
return QString();
}


void QgsCoordinateReferenceSystem::updateDefinition()
{
if ( !d->mIsValid )
Expand Down
8 changes: 8 additions & 0 deletions src/core/proj/qgscoordinatereferencesystem.h
Expand Up @@ -923,6 +923,14 @@ class CORE_EXPORT QgsCoordinateReferenceSystem
*/
QString toOgcUri() const;

/**
* Returns the crs as OGC URN (format: urn:ogc:def:crs:OGC:1.3:CRS84)
* Returns an empty string on failure.
*
* \since QGIS 3.30
*/
QString toOgcUrn() const;

// Mutators -----------------------------------

/**
Expand Down
14 changes: 14 additions & 0 deletions src/core/qgsjsonutils.cpp
Expand Up @@ -245,6 +245,9 @@ json QgsJsonExporter::exportFeaturesToJsonObject( const QgsFeatureList &features
{ "type", "FeatureCollection" },
{ "features", json::array() }
};

QgsJsonUtils::addCrsInfo( data, mDestinationCrs );

for ( const QgsFeature &feature : std::as_const( features ) )
{
data["features"].push_back( exportFeatureToJsonObject( feature ) );
Expand Down Expand Up @@ -912,3 +915,14 @@ json QgsJsonUtils::exportAttributesToJsonObject( const QgsFeature &feature, QgsV
}
return attrs;
}

void QgsJsonUtils::addCrsInfo( json &value, const QgsCoordinateReferenceSystem &crs )
{
// When user request EPSG:4326 we return a compliant CRS84 lon/lat GeoJSON
// so no need to add CRS information
if ( crs.authid() == "OGC:CRS84" || crs.authid() == "EPSG:4326" )
return;

value["crs"]["type"] = "name";
value["crs"]["properties"]["name"] = crs.toOgcUrn().toStdString();
}
9 changes: 9 additions & 0 deletions src/core/qgsjsonutils.h
Expand Up @@ -426,6 +426,15 @@ class CORE_EXPORT QgsJsonUtils
*/
static QVariant jsonToVariant( const json &value ) SIP_SKIP;

/**
* Add \a crs information entry in \a json object regarding old GeoJSON specification format
* if it differs from OGC:CRS84 or EPSG:4326.
* According to new specification RFC 7946, coordinate reference system for all GeoJSON coordinates
* is assumed to be OGC:CRS84 but when user specifically request a different CRS, this method
* allow to add this information in the JSON output
*/
static void addCrsInfo( json &value, const QgsCoordinateReferenceSystem &crs ) SIP_SKIP;

};

#endif // QGSJSONUTILS_H
6 changes: 4 additions & 2 deletions src/server/services/wms/qgswmsrenderer.cpp
Expand Up @@ -1316,7 +1316,7 @@ namespace QgsWms
else if ( infoFormat == QgsWmsParameters::Format::HTML )
ba = convertFeatureInfoToHtml( result );
else if ( infoFormat == QgsWmsParameters::Format::JSON )
ba = convertFeatureInfoToJson( layers, result );
ba = convertFeatureInfoToJson( layers, result, mapSettings.destinationCrs() );
else
ba = result.toByteArray();

Expand Down Expand Up @@ -2768,7 +2768,7 @@ namespace QgsWms
return featureInfoString.toUtf8();
}

QByteArray QgsRenderer::convertFeatureInfoToJson( const QList<QgsMapLayer *> &layers, const QDomDocument &doc ) const
QByteArray QgsRenderer::convertFeatureInfoToJson( const QList<QgsMapLayer *> &layers, const QDomDocument &doc, const QgsCoordinateReferenceSystem &destCRS ) const
{
json json
{
Expand Down Expand Up @@ -2870,6 +2870,8 @@ namespace QgsWms
exporter.setIncludeGeometry( withGeometry );
exporter.setTransformGeometries( false );

QgsJsonUtils::addCrsInfo( json, destCRS );

for ( const auto &feature : std::as_const( features ) )
{
const QString id = QStringLiteral( "%1.%2" ).arg( layerName ).arg( fidMap.value( feature.id() ) );
Expand Down
2 changes: 1 addition & 1 deletion src/server/services/wms/qgswmsrenderer.h
Expand Up @@ -325,7 +325,7 @@ namespace QgsWms
QByteArray convertFeatureInfoToText( const QDomDocument &doc ) const;

//! Converts a feature info xml document to json
QByteArray convertFeatureInfoToJson( const QList<QgsMapLayer *> &layers, const QDomDocument &doc ) const;
QByteArray convertFeatureInfoToJson( const QList<QgsMapLayer *> &layers, const QDomDocument &doc, const QgsCoordinateReferenceSystem &destCRS ) const;

QDomElement createFeatureGML(
const QgsFeature *feat,
Expand Down
24 changes: 24 additions & 0 deletions tests/src/python/test_qgsserver_wms_getfeatureinfo.py
Expand Up @@ -595,6 +595,30 @@ def testGetFeatureInfoJSON(self):
'wms_getfeatureinfo_raster_json',
normalizeJson=True)

# simple test with geometry with underlying layer in 4326 and CRS is EPSG:4326
self.wms_request_compare('GetFeatureInfo',
'&layers=testlayer%20%C3%A8%C3%A9&styles=&' +
'info_format=application%2Fjson&transparent=true&' +
'width=600&height=400&srs=EPSG:4326&' +
'bbox=44.9014173,8.2034387,44.9015094,8.2036094&' +
'query_layers=testlayer2&X=203&Y=116&' +
'with_geometry=true',
'wms_getfeatureinfo_geometry_CRS84_json',
'test_project.qgs',
normalizeJson=True)

# simple test with geometry with underlying layer in 4326 and CRS is CRS84
self.wms_request_compare('GetFeatureInfo',
'&layers=testlayer%20%C3%A8%C3%A9&styles=&' +
'info_format=application%2Fjson&transparent=true&' +
'width=600&height=400&srs=OGC:CRS84&' +
'bbox=8.2034387,44.9014173,8.2036094,44.9015094&' +
'query_layers=testlayer2&X=203&Y=116&' +
'with_geometry=true',
'wms_getfeatureinfo_geometry_CRS84_json',
'test_project.qgs',
normalizeJson=True)

def testGetFeatureInfoGroupedLayers(self):
"""Test that we can get feature info from the top and group layers"""

Expand Down
7 changes: 7 additions & 0 deletions tests/testdata/qgis_server/wms_getfeatureinfo_alias_json.txt
Expand Up @@ -2,6 +2,13 @@
Content-Type: application/json; charset=utf-8

{
"crs":
{
"properties": {
"name": "urn:ogc:def:crs:EPSG:0:3857"
},
"type": "name"
},
"features": [
{
"geometry": null,
Expand Down
Expand Up @@ -2,6 +2,13 @@
Content-Type: application/json; charset=utf-8

{
"crs":
{
"properties": {
"name": "urn:ogc:def:crs:EPSG:0:3857"
},
"type": "name"
},
"features": [
{
"geometry": null,
Expand Down
7 changes: 7 additions & 0 deletions tests/testdata/qgis_server/wms_getfeatureinfo_geojson.txt
Expand Up @@ -2,6 +2,13 @@
Content-Type: application/geo+json; charset=utf-8

{
"crs":
{
"properties": {
"name": "urn:ogc:def:crs:EPSG:0:3857"
},
"type": "name"
},
"features": [
{
"geometry": null,
Expand Down
@@ -0,0 +1,24 @@
*****
Content-Type: application/json; charset=utf-8

{
"features": [
{
"geometry": {
"coordinates": [
8.2035,
44.9015
],
"type": "Point"
},
"id": "testlayer2.0",
"properties": {
"id": 1,
"name": "one",
"utf8nameè": "one èé"
},
"type": "Feature"
}
],
"type": "FeatureCollection"
}
Expand Up @@ -2,6 +2,13 @@
Content-Type: application/json; charset=utf-8

{
"crs":
{
"properties": {
"name": "urn:ogc:def:crs:EPSG:0:3857"
},
"type": "name"
},
"features": [
{
"geometry": {
Expand Down
7 changes: 7 additions & 0 deletions tests/testdata/qgis_server/wms_getfeatureinfo_json.txt
Expand Up @@ -2,6 +2,13 @@
Content-Type: application/json; charset=utf-8

{
"crs":
{
"properties": {
"name": "urn:ogc:def:crs:EPSG:0:3857"
},
"type": "name"
},
"features": [
{
"geometry": null,
Expand Down
Expand Up @@ -2,6 +2,13 @@
Content-Type: application/json; charset=utf-8

{
"crs":
{
"properties": {
"name": "urn:ogc:def:crs:EPSG:0:3857"
},
"type": "name"
},
"features": [
{
"geometry": null,
Expand Down

0 comments on commit 5046beb

Please sign in to comment.