diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2020-11-22 01:23:12 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-11-22 01:23:12 +0100 |
| commit | 427a4545e044b577f7311baf76b427218eef5dda (patch) | |
| tree | 830264fc953ccbca002eada48d50710499cac2c1 | |
| parent | 3f4058308bc328765dcf6ecdcb8fa7a644f3cc19 (diff) | |
| parent | 650e37f159b96d18f5ff03784466784a769688e4 (diff) | |
| download | PROJ-427a4545e044b577f7311baf76b427218eef5dda.tar.gz PROJ-427a4545e044b577f7311baf76b427218eef5dda.zip | |
Merge pull request #2443 from rouault/fix_parse_wkt2_2019_projected_with_base_geocentric
WKT parsing: fix ingestion of WKT with a Geocentric CRS as the base of the projected CRS
| -rw-r--r-- | src/iso19111/io.cpp | 59 | ||||
| -rw-r--r-- | test/unit/test_io.cpp | 203 |
2 files changed, 253 insertions, 9 deletions
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index fa359d39..713a471d 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -2794,6 +2794,32 @@ WKTParser::Private::buildGeodeticCRS(const WKTNodeNNPtr &node) { .as_nullable() : nullptr; auto cs = buildCS(csNode, node, angularUnit); + + // If there's no CS[] node, typically for a BASEGEODCRS of a projected CRS, + // in a few rare cases, this might be a Geocentric CRS, and thus a + // Cartesian CS, and not the ellipsoidalCS we assumed above. The only way + // to figure that is to resolve the CRS from its code... + if (isNull(csNode) && dbContext_ && + ci_equal(nodeName, WKTConstants::BASEGEODCRS)) { + const auto &nodeChildren = nodeP->children(); + for (const auto &subNode : nodeChildren) { + const auto &subNodeName(subNode->GP()->value()); + if (ci_equal(subNodeName, WKTConstants::ID) || + ci_equal(subNodeName, WKTConstants::AUTHORITY)) { + auto id = buildId(subNode, true, false); + if (id) { + try { + auto authFactory = AuthorityFactory::create( + NN_NO_CHECK(dbContext_), *id->codeSpace()); + auto dbCRS = authFactory->createGeodeticCRS(id->code()); + cs = dbCRS->coordinateSystem(); + } catch (const util::Exception &) { + } + } + } + } + } + auto ellipsoidalCS = nn_dynamic_pointer_cast<EllipsoidalCS>(cs); if (ellipsoidalCS) { if (ci_equal(nodeName, WKTConstants::GEOCCS)) { @@ -4954,6 +4980,10 @@ class JSONParser { TransformationNNPtr buildTransformation(const json &j); ConcatenatedOperationNNPtr buildConcatenatedOperation(const json &j); + void buildGeodeticDatumOrDatumEnsemble(const json &j, + GeodeticReferenceFramePtr &datum, + DatumEnsemblePtr &datumEnsemble); + static util::optional<std::string> getAnchor(const json &j) { util::optional<std::string> anchor; if (j.contains("anchor")) { @@ -5413,9 +5443,9 @@ BaseObjectNNPtr JSONParser::create(const json &j) // --------------------------------------------------------------------------- -GeographicCRSNNPtr JSONParser::buildGeographicCRS(const json &j) { - GeodeticReferenceFramePtr datum; - DatumEnsemblePtr datumEnsemble; +void JSONParser::buildGeodeticDatumOrDatumEnsemble( + const json &j, GeodeticReferenceFramePtr &datum, + DatumEnsemblePtr &datumEnsemble) { if (j.contains("datum")) { auto datumJ = getObject(j, "datum"); datum = util::nn_dynamic_pointer_cast<GeodeticReferenceFrame>( @@ -5428,6 +5458,14 @@ GeographicCRSNNPtr JSONParser::buildGeographicCRS(const json &j) { datumEnsemble = buildDatumEnsemble(getObject(j, "datum_ensemble")).as_nullable(); } +} + +// --------------------------------------------------------------------------- + +GeographicCRSNNPtr JSONParser::buildGeographicCRS(const json &j) { + GeodeticReferenceFramePtr datum; + DatumEnsemblePtr datumEnsemble; + buildGeodeticDatumOrDatumEnsemble(j, datum, datumEnsemble); auto csJ = getObject(j, "coordinate_system"); auto ellipsoidalCS = util::nn_dynamic_pointer_cast<EllipsoidalCS>(buildCS(csJ)); @@ -5441,12 +5479,9 @@ GeographicCRSNNPtr JSONParser::buildGeographicCRS(const json &j) { // --------------------------------------------------------------------------- GeodeticCRSNNPtr JSONParser::buildGeodeticCRS(const json &j) { - auto datumJ = getObject(j, "datum"); - if (getType(datumJ) != "GeodeticReferenceFrame") { - throw ParsingException("Unsupported type for datum."); - } - auto datum = buildGeodeticReferenceFrame(datumJ); + GeodeticReferenceFramePtr datum; DatumEnsemblePtr datumEnsemble; + buildGeodeticDatumOrDatumEnsemble(j, datum, datumEnsemble); auto csJ = getObject(j, "coordinate_system"); auto cs = buildCS(csJ); auto props = buildProperties(j); @@ -5481,7 +5516,13 @@ GeodeticCRSNNPtr JSONParser::buildGeodeticCRS(const json &j) { // --------------------------------------------------------------------------- ProjectedCRSNNPtr JSONParser::buildProjectedCRS(const json &j) { - auto baseCRS = buildGeographicCRS(getObject(j, "base_crs")); + auto jBaseCRS = getObject(j, "base_crs"); + auto jBaseCS = getObject(jBaseCRS, "coordinate_system"); + auto baseCS = buildCS(jBaseCS); + auto baseCRS = dynamic_cast<EllipsoidalCS *>(baseCS.get()) != nullptr + ? util::nn_static_pointer_cast<GeodeticCRS>( + buildGeographicCRS(jBaseCRS)) + : buildGeodeticCRS(jBaseCRS); auto csJ = getObject(j, "coordinate_system"); auto cartesianCS = util::nn_dynamic_pointer_cast<CartesianCS>(buildCS(csJ)); if (!cartesianCS) { diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index dff8ec45..81894fb0 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -1884,6 +1884,60 @@ TEST(wkt_parse, wkt2_2019_projected_utm_3D) { // --------------------------------------------------------------------------- +TEST(wkt_parse, wkt2_2019_projected_with_base_geocentric) { + auto wkt = + "PROJCRS[\"EPSG topocentric example B\",\n" + " BASEGEODCRS[\"WGS 84\",\n" + " ENSEMBLE[\"World Geodetic System 1984 ensemble\",\n" + " MEMBER[\"World Geodetic System 1984 (Transit)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G730)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G873)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G1150)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G1674)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G1762)\"],\n" + " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n" + " LENGTHUNIT[\"metre\",1]],\n" + " ENSEMBLEACCURACY[2.0]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " ID[\"EPSG\",4978]],\n" + " CONVERSION[\"EPSG topocentric example B\",\n" + " METHOD[\"Geocentric/topocentric conversions\",\n" + " ID[\"EPSG\",9836]],\n" + " PARAMETER[\"Geocentric X of topocentric origin\",3771793.97,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8837]],\n" + " PARAMETER[\"Geocentric Y of topocentric origin\",140253.34,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8838]],\n" + " PARAMETER[\"Geocentric Z of topocentric origin\",5124304.35,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8839]]],\n" + " CS[Cartesian,3],\n" + " AXIS[\"topocentric East (U)\",east,\n" + " ORDER[1],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " AXIS[\"topocentric North (V)\",north,\n" + " ORDER[2],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " AXIS[\"topocentric height (W)\",up,\n" + " ORDER[3],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " USAGE[\n" + " SCOPE[\"Example only (fictitious).\"],\n" + " AREA[\"Description of the extent of the CRS.\"],\n" + " BBOX[-90,-180,90,180]],\n" + " ID[\"EPSG\",5820]]"; + auto dbContext = DatabaseContext::create(); + // Need a database so that EPSG:4978 is resolved + auto obj = WKTParser().attachDatabaseContext(dbContext).createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_TRUE(crs->baseCRS()->isGeocentric()); +} + +// --------------------------------------------------------------------------- + TEST(crs, projected_angular_unit_from_primem) { auto obj = WKTParser().createFromWKT( "PROJCRS[\"NTF (Paris) / Lambert Nord France\",\n" @@ -11152,6 +11206,155 @@ TEST(json_import, projected_crs) { // --------------------------------------------------------------------------- +TEST(json_import, projected_crs_with_geocentric_base) { + auto json = "{\n" + " \"$schema\": \"foo\",\n" + " \"type\": \"ProjectedCRS\",\n" + " \"name\": \"EPSG topocentric example B\",\n" + " \"base_crs\": {\n" + " \"name\": \"WGS 84\",\n" + " \"datum_ensemble\": {\n" + " \"name\": \"World Geodetic System 1984 ensemble\",\n" + " \"members\": [\n" + " {\n" + " \"name\": \"World Geodetic System 1984 (Transit)\"\n" + " },\n" + " {\n" + " \"name\": \"World Geodetic System 1984 (G730)\"\n" + " },\n" + " {\n" + " \"name\": \"World Geodetic System 1984 (G873)\"\n" + " },\n" + " {\n" + " \"name\": \"World Geodetic System 1984 (G1150)\"\n" + " },\n" + " {\n" + " \"name\": \"World Geodetic System 1984 (G1674)\"\n" + " },\n" + " {\n" + " \"name\": \"World Geodetic System 1984 (G1762)\"\n" + " }\n" + " ],\n" + " \"ellipsoid\": {\n" + " \"name\": \"WGS 84\",\n" + " \"semi_major_axis\": 6378137,\n" + " \"inverse_flattening\": 298.257223563\n" + " },\n" + " \"accuracy\": \"2.0\"\n" + " },\n" + " \"coordinate_system\": {\n" + " \"subtype\": \"Cartesian\",\n" + " \"axis\": [\n" + " {\n" + " \"name\": \"Geocentric X\",\n" + " \"abbreviation\": \"X\",\n" + " \"direction\": \"geocentricX\",\n" + " \"unit\": \"metre\"\n" + " },\n" + " {\n" + " \"name\": \"Geocentric Y\",\n" + " \"abbreviation\": \"Y\",\n" + " \"direction\": \"geocentricY\",\n" + " \"unit\": \"metre\"\n" + " },\n" + " {\n" + " \"name\": \"Geocentric Z\",\n" + " \"abbreviation\": \"Z\",\n" + " \"direction\": \"geocentricZ\",\n" + " \"unit\": \"metre\"\n" + " }\n" + " ]\n" + " },\n" + " \"id\": {\n" + " \"authority\": \"EPSG\",\n" + " \"code\": 4978\n" + " }\n" + " },\n" + " \"conversion\": {\n" + " \"name\": \"EPSG topocentric example B\",\n" + " \"method\": {\n" + " \"name\": \"Geocentric/topocentric conversions\",\n" + " \"id\": {\n" + " \"authority\": \"EPSG\",\n" + " \"code\": 9836\n" + " }\n" + " },\n" + " \"parameters\": [\n" + " {\n" + " \"name\": \"Geocentric X of topocentric origin\",\n" + " \"value\": 3771793.97,\n" + " \"unit\": \"metre\",\n" + " \"id\": {\n" + " \"authority\": \"EPSG\",\n" + " \"code\": 8837\n" + " }\n" + " },\n" + " {\n" + " \"name\": \"Geocentric Y of topocentric origin\",\n" + " \"value\": 140253.34,\n" + " \"unit\": \"metre\",\n" + " \"id\": {\n" + " \"authority\": \"EPSG\",\n" + " \"code\": 8838\n" + " }\n" + " },\n" + " {\n" + " \"name\": \"Geocentric Z of topocentric origin\",\n" + " \"value\": 5124304.35,\n" + " \"unit\": \"metre\",\n" + " \"id\": {\n" + " \"authority\": \"EPSG\",\n" + " \"code\": 8839\n" + " }\n" + " }\n" + " ]\n" + " },\n" + " \"coordinate_system\": {\n" + " \"subtype\": \"Cartesian\",\n" + " \"axis\": [\n" + " {\n" + " \"name\": \"Topocentric East\",\n" + " \"abbreviation\": \"U\",\n" + " \"direction\": \"east\",\n" + " \"unit\": \"metre\"\n" + " },\n" + " {\n" + " \"name\": \"Topocentric North\",\n" + " \"abbreviation\": \"V\",\n" + " \"direction\": \"north\",\n" + " \"unit\": \"metre\"\n" + " },\n" + " {\n" + " \"name\": \"Topocentric height\",\n" + " \"abbreviation\": \"W\",\n" + " \"direction\": \"up\",\n" + " \"unit\": \"metre\"\n" + " }\n" + " ]\n" + " },\n" + " \"scope\": \"Example only (fictitious).\",\n" + " \"area\": \"Description of the extent of the CRS.\",\n" + " \"bbox\": {\n" + " \"south_latitude\": -90,\n" + " \"west_longitude\": -180,\n" + " \"north_latitude\": 90,\n" + " \"east_longitude\": 180\n" + " },\n" + " \"id\": {\n" + " \"authority\": \"EPSG\",\n" + " \"code\": 5820\n" + " }\n" + "}"; + auto obj = createFromUserInput(json, nullptr); + auto pcrs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(pcrs != nullptr); + EXPECT_TRUE(pcrs->baseCRS()->isGeocentric()); + EXPECT_EQ(pcrs->exportToJSON(&(JSONFormatter::create()->setSchema("foo"))), + json); +} + +// --------------------------------------------------------------------------- + TEST(json_import, compound_crs) { auto json = "{\n" " \"$schema\": \"foo\",\n" |
