From 17fe008a83c45ba07dfa471643618f9d93945f17 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 26 Nov 2024 16:59:44 +0100 Subject: [PATCH] PROJJSON export: for a Projected CRS, add an explicit type=GeographicCRS/GeodeticCRS members to the base_crs member This can make consumption/validation of PROJJSON easier. This doesn't affect forward/backward compatibility as the importer is tolerant about presence or absence of base_crs.type, and the schema too. --- docs/source/specifications/projjson.rst | 7 +++++++ src/iso19111/crs.cpp | 1 - test/unit/test_io.cpp | 22 ++++++++++++++++++---- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/docs/source/specifications/projjson.rst b/docs/source/specifications/projjson.rst index 44063abd41..586eb29352 100644 --- a/docs/source/specifications/projjson.rst +++ b/docs/source/specifications/projjson.rst @@ -504,6 +504,7 @@ The EPSG:32631 / "WGS 84 / UTM zone 31N" projected CRS can be expressed as "type": "ProjectedCRS", "name": "WGS 84 / UTM zone 31N", "base_crs": { + "type": "GeographicCRS", "name": "WGS 84", "datum": { "type": "GeodeticReferenceFrame", @@ -623,6 +624,12 @@ The EPSG:32631 / "WGS 84 / UTM zone 31N" projected CRS can be expressed as } } + +.. note:: + + The ``type`` member of the ``base_crs`` member was omitted in PROJ export of PROJJSON + prior to PROJ 9.6. + CompoundCRS +++++++++++ diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index 0dfecbbca7..9dd5f4feb6 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -4533,7 +4533,6 @@ void ProjectedCRS::_exportToJSON( writer->AddObjKey("base_crs"); formatter->setAllowIDInImmediateChild(); - formatter->setOmitTypeInImmediateChild(); baseCRS()->_exportToJSON(formatter); writer->AddObjKey("conversion"); diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index c88c6010dd..c9a07d0749 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -15133,8 +15133,14 @@ TEST(json_import, projected_crs) { auto obj = createFromUserInput(json, nullptr); auto pcrs = nn_dynamic_pointer_cast(obj); ASSERT_TRUE(pcrs != nullptr); - EXPECT_EQ(pcrs->exportToJSON(&(JSONFormatter::create()->setSchema("foo"))), - json); + std::string got_json = + pcrs->exportToJSON(&(JSONFormatter::create()->setSchema("foo"))); + const char *typeGeogCRS = " \"type\": \"GeographicCRS\",\n"; + const auto posTypeGeogCRS = got_json.find(typeGeogCRS); + EXPECT_TRUE(posTypeGeogCRS != std::string::npos) << got_json; + got_json = got_json.substr(0, posTypeGeogCRS) + + got_json.substr(posTypeGeogCRS + strlen(typeGeogCRS)); + EXPECT_STREQ(got_json.c_str(), json); } // --------------------------------------------------------------------------- @@ -15363,8 +15369,14 @@ TEST(json_import, projected_crs_with_geocentric_base) { auto pcrs = nn_dynamic_pointer_cast(obj); ASSERT_TRUE(pcrs != nullptr); EXPECT_TRUE(pcrs->baseCRS()->isGeocentric()); - EXPECT_EQ(pcrs->exportToJSON(&(JSONFormatter::create()->setSchema("foo"))), - json); + std::string got_json = + pcrs->exportToJSON(&(JSONFormatter::create()->setSchema("foo"))); + const char *typeGeodCRS = " \"type\": \"GeodeticCRS\",\n"; + const auto posTypeGeodCRS = got_json.find(typeGeodCRS); + EXPECT_TRUE(posTypeGeodCRS != std::string::npos) << got_json; + got_json = got_json.substr(0, posTypeGeodCRS) + + got_json.substr(posTypeGeodCRS + strlen(typeGeodCRS)); + EXPECT_STREQ(got_json.c_str(), json); } // --------------------------------------------------------------------------- @@ -16153,6 +16165,7 @@ TEST(json_import, concatenated_operation) { " \"type\": \"ProjectedCRS\",\n" " \"name\": \"GDA94 / Vicgrid\",\n" " \"base_crs\": {\n" + " \"type\": \"GeographicCRS\",\n" " \"name\": \"GDA94\",\n" " \"datum\": {\n" " \"type\": \"GeodeticReferenceFrame\",\n" @@ -17371,6 +17384,7 @@ TEST(json_import, derived_projected_crs) { " \"type\": \"ProjectedCRS\",\n" " \"name\": \"WGS 84 / UTM zone 31N\",\n" " \"base_crs\": {\n" + " \"type\": \"GeographicCRS\",\n" " \"name\": \"WGS 84\",\n" " \"datum\": {\n" " \"type\": \"GeodeticReferenceFrame\",\n"