From d53911f100cfd64a77ed8aa783c0538003dbfa2d Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 25 Mar 2019 11:35:31 +0100 Subject: test: test exporting ENSEMBLE with ID in MEMBER --- test/unit/test_crs.cpp | 90 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 25 deletions(-) diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp index 4953529c..02586877 100644 --- a/test/unit/test_crs.cpp +++ b/test/unit/test_crs.cpp @@ -148,32 +148,72 @@ TEST(crs, GeographicCRS_datum_ensemble) { std::vector{GeodeticReferenceFrame::EPSG_6326, GeodeticReferenceFrame::EPSG_6326}, PositionalAccuracy::create("100")); - auto crs = GeographicCRS::create( - PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), nullptr, - ensemble_vdatum, - EllipsoidalCS::createLatitudeLongitude(UnitOfMeasure::DEGREE)); - WKTFormatterNNPtr f( - WKTFormatter::create(WKTFormatter::Convention::WKT2_2018)); - f->simulCurNodeHasId(); - crs->exportToWKT(f.get()); - auto expected = "GEOGCRS[\"unnamed\",\n" - " ENSEMBLE[\"unnamed\",\n" - " MEMBER[\"World Geodetic System 1984\"],\n" - " MEMBER[\"World Geodetic System 1984\"],\n" - " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n" - " LENGTHUNIT[\"metre\",1]],\n" - " ENSEMBLEACCURACY[100]],\n" - " PRIMEM[\"Greenwich\",0,\n" - " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" - " CS[ellipsoidal,2],\n" - " AXIS[\"latitude\",north,\n" - " ORDER[1],\n" - " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" - " AXIS[\"longitude\",east,\n" - " ORDER[2],\n" - " ANGLEUNIT[\"degree\",0.0174532925199433]]]"; + { + auto crs = GeographicCRS::create( + PropertyMap() + .set(IdentifiedObject::NAME_KEY, "unnamed") + .set(Identifier::CODESPACE_KEY, "MY_CODESPACE") + .set(Identifier::CODE_KEY, "MY_ID"), + nullptr, ensemble_vdatum, + EllipsoidalCS::createLatitudeLongitude(UnitOfMeasure::DEGREE)); + WKTFormatterNNPtr f( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018)); + crs->exportToWKT(f.get()); + auto expected = + "GEOGCRS[\"unnamed\",\n" + " ENSEMBLE[\"unnamed\",\n" + " MEMBER[\"World Geodetic System 1984\"],\n" + " MEMBER[\"World Geodetic System 1984\"],\n" + " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n" + " LENGTHUNIT[\"metre\",1]],\n" + " ENSEMBLEACCURACY[100]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " CS[ellipsoidal,2],\n" + " AXIS[\"latitude\",north,\n" + " ORDER[1],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " AXIS[\"longitude\",east,\n" + " ORDER[2],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " ID[\"MY_CODESPACE\",\"MY_ID\"]]"; - EXPECT_EQ(f->toString(), expected); + EXPECT_EQ(f->toString(), expected); + } + + { + auto crs = GeographicCRS::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), nullptr, + ensemble_vdatum, + EllipsoidalCS::createLatitudeLongitude(UnitOfMeasure::DEGREE)); + WKTFormatterNNPtr f( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018)); + crs->exportToWKT(f.get()); + auto expected = "GEOGCRS[\"unnamed\",\n" + " ENSEMBLE[\"unnamed\",\n" + " MEMBER[\"World Geodetic System 1984\",\n" + " ID[\"EPSG\",6326]],\n" + " MEMBER[\"World Geodetic System 1984\",\n" + " ID[\"EPSG\",6326]],\n" + " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",7030]],\n" + " ENSEMBLEACCURACY[100]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8901]],\n" + " CS[ellipsoidal,2],\n" + " AXIS[\"latitude\",north,\n" + " ORDER[1],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433,\n" + " ID[\"EPSG\",9122]]],\n" + " AXIS[\"longitude\",east,\n" + " ORDER[2],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433,\n" + " ID[\"EPSG\",9122]]]]"; + + EXPECT_EQ(f->toString(), expected); + } } // --------------------------------------------------------------------------- -- cgit v1.2.3 From 72f0e8a895a861e4323cac61b73f807c8a5f1c0d Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 25 Mar 2019 12:11:21 +0100 Subject: crs.cpp: remove non-intented (harmless here) pass by reference of a pointer --- include/proj/crs.hpp | 2 +- src/iso19111/crs.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/proj/crs.hpp b/include/proj/crs.hpp index b9594165..ed3463cd 100644 --- a/include/proj/crs.hpp +++ b/include/proj/crs.hpp @@ -503,7 +503,7 @@ class PROJ_GCC_DLL DerivedCRS : virtual public SingleCRS { PROJ_INTERNAL void setDerivingConversionCRS(); PROJ_INTERNAL void baseExportToWKT( - io::WKTFormatter *&formatter, const std::string &keyword, + io::WKTFormatter *formatter, const std::string &keyword, const std::string &baseKeyword) const; // throw(FormattingException) PROJ_INTERNAL bool diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index 9688883d..74f6f999 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -2421,7 +2421,7 @@ void DerivedCRS::setDerivingConversionCRS() { // --------------------------------------------------------------------------- -void DerivedCRS::baseExportToWKT(io::WKTFormatter *&formatter, +void DerivedCRS::baseExportToWKT(io::WKTFormatter *formatter, const std::string &keyword, const std::string &baseKeyword) const { formatter->startNode(keyword, !identifiers().empty()); @@ -4848,7 +4848,7 @@ DerivedCRSTemplate::create( // --------------------------------------------------------------------------- -static void DerivedCRSTemplateCheckExportToWKT(io::WKTFormatter *&formatter, +static void DerivedCRSTemplateCheckExportToWKT(io::WKTFormatter *formatter, const std::string &crsName, bool wkt2_2018_only) { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; -- cgit v1.2.3 From d4fd50f10ecabb9e9642cb4f877262e082677be4 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 25 Mar 2019 12:07:35 +0100 Subject: WKT2_2018: export ID in base crs node, when there is none on top of upper node This is the standard logic, that is now possible since ID is allowed in BASEGEOGCRS and similar node --- include/proj/io.hpp | 3 +-- src/iso19111/crs.cpp | 11 +++++++++-- test/unit/test_crs.cpp | 3 ++- test/unit/test_io.cpp | 29 +++++++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/include/proj/io.hpp b/include/proj/io.hpp index 8cf8e3c4..90163001 100644 --- a/include/proj/io.hpp +++ b/include/proj/io.hpp @@ -195,8 +195,7 @@ class PROJ_GCC_DLL WKTFormatter { WKT2_2018_SIMPLIFIED, /** WKT1 as traditionally output by GDAL, deriving from OGC 01-009. - A notable departuPROJ_GCC_DLLre from WKT1_GDAL with respect to OGC - 01-009 is + A notable departure from WKT1_GDAL with respect to OGC 01-009 is that in WKT1_GDAL, the unit of the PRIMEM value is always degrees. */ WKT1_GDAL, diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index 74f6f999..9779d817 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -2428,9 +2428,13 @@ void DerivedCRS::baseExportToWKT(io::WKTFormatter *formatter, formatter->addQuotedString(nameStr()); const auto &l_baseCRS = d->baseCRS_; - formatter->startNode(baseKeyword, !l_baseCRS->identifiers().empty()); + formatter->startNode(baseKeyword, formatter->use2018Keywords() && + !l_baseCRS->identifiers().empty()); formatter->addQuotedString(l_baseCRS->nameStr()); l_baseCRS->exportDatumOrDatumEnsembleToWkt(formatter); + if (formatter->use2018Keywords() && formatter->outputId()) { + l_baseCRS->formatID(formatter); + } formatter->endNode(); formatter->setUseDerivingConversion(true); @@ -2658,7 +2662,7 @@ void ProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const { dynamic_cast(l_baseCRS.get())) ? io::WKTConstants::BASEGEOGCRS : io::WKTConstants::BASEGEODCRS, - !l_baseCRS->identifiers().empty()); + formatter->use2018Keywords() && !l_baseCRS->identifiers().empty()); formatter->addQuotedString(l_baseCRS->nameStr()); l_baseCRS->exportDatumOrDatumEnsembleToWkt(formatter); // insert ellipsoidal cs unit when the units of the map @@ -2669,6 +2673,9 @@ void ProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const { geodeticCRSAxisList[0]->unit()._exportToWKT(formatter); } l_baseCRS->primeMeridian()->_exportToWKT(formatter); + if (formatter->use2018Keywords() && formatter->outputId()) { + l_baseCRS->formatID(formatter); + } formatter->endNode(); } else { const auto oldAxisOutputRule = formatter->outputAxis(); diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp index 02586877..bfd6ee66 100644 --- a/test/unit/test_crs.cpp +++ b/test/unit/test_crs.cpp @@ -4562,7 +4562,8 @@ TEST(crs, DerivedVerticalCRS_WKT2) { auto expected = "VERTCRS[\"Derived vertCRS\",\n" " BASEVERTCRS[\"ODN height\",\n" - " VDATUM[\"Ordnance Datum Newlyn\"]],\n" + " VDATUM[\"Ordnance Datum Newlyn\",\n" + " ID[\"EPSG\",5101]]],\n" " DERIVINGCONVERSION[\"unnamed\",\n" " METHOD[\"PROJ unimplemented\"]],\n" " CS[vertical,1],\n" diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index 1d097d9b..2181b321 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -1667,6 +1667,35 @@ TEST(wkt_parse, wkt2_2018_projected_with_id_in_basegeodcrs) { // --------------------------------------------------------------------------- +TEST(wkt_parse, wkt2_2018_projected_no_id_but_id_in_basegeodcrs) { + auto wkt = "PROJCRS[\"WGS 84 / UTM zone 31N\",\n" + " BASEGEOGCRS[\"WGS 84\",\n" + " DATUM[\"World Geodetic System 1984\",\n" + " ELLIPSOID[\"WGS 84\",6378137,298.257223563]],\n" + " ID[\"EPSG\",4326]],\n" + " CONVERSION[\"UTM zone 31N\",\n" + " METHOD[\"Transverse Mercator\"],\n" + " PARAMETER[\"Latitude of natural origin\",0],\n" + " PARAMETER[\"Longitude of natural origin\",3],\n" + " PARAMETER[\"Scale factor at natural origin\",0.9996],\n" + " PARAMETER[\"False easting\",500000],\n" + " PARAMETER[\"False northing\",0]],\n" + " CS[Cartesian,2],\n" + " AXIS[\"(E)\",east],\n" + " AXIS[\"(N)\",north],\n" + " UNIT[\"metre\",1]]"; + auto obj = WKTParser().createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast(obj); + ASSERT_TRUE(crs != nullptr); + + auto got_wkt = crs->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get()); + EXPECT_TRUE(got_wkt.find("ID[\"EPSG\",4326]]") != std::string::npos) + << got_wkt; +} + +// --------------------------------------------------------------------------- + TEST(wkt_parse, wkt2_2018_simplified_projected) { auto wkt = "PROJCRS[\"WGS 84 / UTM zone 31N\",\n" " BASEGEOGCRS[\"WGS 84\",\n" -- cgit v1.2.3 From 46f08f1434f66a4160d7c74923efcfb81505b398 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 25 Mar 2019 12:44:05 +0100 Subject: WKT2_2018: always export ID in base crs node, even if there is one on upper node This is a particular logic allowed by paragraph 7.3.3 Identifier of OGC 18-010r6 --- include/proj/io.hpp | 2 ++ src/iso19111/crs.cpp | 6 ++++-- src/iso19111/io.cpp | 12 +++++++++++ test/cli/testprojinfo_out.dist | 3 ++- test/unit/test_crs.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++ test/unit/test_io.cpp | 15 ++++++++++++++ 6 files changed, 81 insertions(+), 3 deletions(-) diff --git a/include/proj/io.hpp b/include/proj/io.hpp index 90163001..ace8ad9d 100644 --- a/include/proj/io.hpp +++ b/include/proj/io.hpp @@ -302,6 +302,8 @@ class PROJ_GCC_DLL WKTFormatter { PROJ_INTERNAL bool primeMeridianOrParameterUnitOmittedIfSameAsAxis() const; PROJ_INTERNAL bool primeMeridianInDegree() const; PROJ_INTERNAL bool outputCSUnitOnlyOnceIfSame() const; + PROJ_INTERNAL bool idOnTopLevelOnly() const; + PROJ_INTERNAL bool topLevelHasId() const; /** WKT version. */ enum class Version { diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index 9779d817..2dc6b3bf 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -2432,7 +2432,8 @@ void DerivedCRS::baseExportToWKT(io::WKTFormatter *formatter, !l_baseCRS->identifiers().empty()); formatter->addQuotedString(l_baseCRS->nameStr()); l_baseCRS->exportDatumOrDatumEnsembleToWkt(formatter); - if (formatter->use2018Keywords() && formatter->outputId()) { + if (formatter->use2018Keywords() && + !(formatter->idOnTopLevelOnly() && formatter->topLevelHasId())) { l_baseCRS->formatID(formatter); } formatter->endNode(); @@ -2673,7 +2674,8 @@ void ProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const { geodeticCRSAxisList[0]->unit()._exportToWKT(formatter); } l_baseCRS->primeMeridian()->_exportToWKT(formatter); - if (formatter->use2018Keywords() && formatter->outputId()) { + if (formatter->use2018Keywords() && + !(formatter->idOnTopLevelOnly() && formatter->topLevelHasId())) { l_baseCRS->formatID(formatter); } formatter->endNode(); diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index 220ee967..78b18719 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -634,6 +634,18 @@ bool WKTFormatter::primeMeridianInDegree() const { // --------------------------------------------------------------------------- +bool WKTFormatter::idOnTopLevelOnly() const { + return d->params_.idOnTopLevelOnly_; +} + +// --------------------------------------------------------------------------- + +bool WKTFormatter::topLevelHasId() const { + return d->stackHasId_.size() >= 2 && d->stackHasId_[1]; +} + +// --------------------------------------------------------------------------- + WKTFormatter::Version WKTFormatter::version() const { return d->params_.version_; } diff --git a/test/cli/testprojinfo_out.dist b/test/cli/testprojinfo_out.dist index a2e2cab9..ee9aeca7 100644 --- a/test/cli/testprojinfo_out.dist +++ b/test/cli/testprojinfo_out.dist @@ -633,7 +633,8 @@ PROJCRS["Monte Mario (Rome) / Italy zone 1", ELLIPSOID["International 1924",6378388,297, LENGTHUNIT["metre",1]]], PRIMEM["Rome",12.4523333333333, - ANGLEUNIT["degree",0.0174532925199433]]], + ANGLEUNIT["degree",0.0174532925199433]], + ID["EPSG",4806]], CONVERSION["Italy zone 1", METHOD["Transverse Mercator", ID["EPSG",9807]], diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp index bfd6ee66..d7303df9 100644 --- a/test/unit/test_crs.cpp +++ b/test/unit/test_crs.cpp @@ -1601,6 +1601,52 @@ TEST(crs, projectedCRS_as_WKT2) { // --------------------------------------------------------------------------- +TEST(crs, projectedCRS_as_WKT2_2018) { + auto crs = createProjected(); + + auto expected = + "PROJCRS[\"WGS 84 / UTM zone 31N\",\n" + " BASEGEOGCRS[\"WGS 84\",\n" + " DATUM[\"World Geodetic System 1984\",\n" + " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " ID[\"EPSG\",4326]],\n" + " CONVERSION[\"UTM zone 31N\",\n" + " METHOD[\"Transverse Mercator\",\n" + " ID[\"EPSG\",9807]],\n" + " PARAMETER[\"Latitude of natural origin\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",3,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"Scale factor at natural origin\",0.9996,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8805]],\n" + " PARAMETER[\"False easting\",500000,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",0,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]],\n" + " CS[Cartesian,2],\n" + " AXIS[\"(E)\",east,\n" + " ORDER[1],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " AXIS[\"(N)\",north,\n" + " ORDER[2],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " ID[\"EPSG\",32631]]"; + + EXPECT_EQ( + crs->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get()), + expected); +} +// --------------------------------------------------------------------------- + TEST(crs, projectedCRS_as_WKT2_simplified) { auto crs = createProjected(); diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index 2181b321..17b3c334 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -1663,6 +1663,21 @@ TEST(wkt_parse, wkt2_2018_projected_with_id_in_basegeodcrs) { ASSERT_TRUE(crs != nullptr); ASSERT_EQ(crs->baseCRS()->identifiers().size(), 1U); EXPECT_EQ(crs->baseCRS()->identifiers().front()->code(), "4326"); + + { + auto got_wkt = crs->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get()); + EXPECT_TRUE(got_wkt.find("ID[\"EPSG\",4326]]") != std::string::npos) + << got_wkt; + } + + { + auto got_wkt = crs->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018_SIMPLIFIED) + .get()); + EXPECT_TRUE(got_wkt.find("ID[\"EPSG\",4326]]") == std::string::npos) + << got_wkt; + } } // --------------------------------------------------------------------------- -- cgit v1.2.3 From 09db4826d4a1e5df900cb4b93a4b3eae2c487cb9 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 25 Mar 2019 13:56:43 +0100 Subject: WKT2_2018: always export ID of SOURCECRS/TARGETCRS and STEPs even if there is one on upper node This is a particular logic allowed by paragraph 7.3.3 Identifier of OGC 18-010r6 --- include/proj/io.hpp | 7 +++ src/iso19111/common.cpp | 2 +- src/iso19111/coordinateoperation.cpp | 88 ++++++++++++++++++++++++++++-------- src/iso19111/io.cpp | 28 ++++++++++++ test/cli/testprojinfo_out.dist | 58 ++++++++++++------------ test/unit/test_factory.cpp | 18 +++++--- test/unit/test_operation.cpp | 4 -- 7 files changed, 145 insertions(+), 60 deletions(-) diff --git a/include/proj/io.hpp b/include/proj/io.hpp index ace8ad9d..5386ca6c 100644 --- a/include/proj/io.hpp +++ b/include/proj/io.hpp @@ -261,6 +261,13 @@ class PROJ_GCC_DLL WKTFormatter { PROJ_INTERNAL void popOutputId(); PROJ_INTERNAL bool outputId() const; + PROJ_INTERNAL void pushHasId(bool hasId); + PROJ_INTERNAL void popHasId(); + + PROJ_INTERNAL void pushDisableUsage(); + PROJ_INTERNAL void popDisableUsage(); + PROJ_INTERNAL bool outputUsage() const; + PROJ_INTERNAL void pushAxisLinearUnit(const common::UnitOfMeasureNNPtr &unit); PROJ_INTERNAL void popAxisLinearUnit(); diff --git a/src/iso19111/common.cpp b/src/iso19111/common.cpp index 4b947dc9..d46da0da 100644 --- a/src/iso19111/common.cpp +++ b/src/iso19111/common.cpp @@ -1073,7 +1073,7 @@ void ObjectUsage::setProperties( void ObjectUsage::baseExportToWKT(WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == WKTFormatter::Version::WKT2; - if (isWKT2 && formatter->outputId()) { + if (isWKT2 && formatter->outputUsage()) { auto l_domains = domains(); if (!l_domains.empty()) { if (formatter->use2018Keywords()) { diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 80c1a572..d7f138a4 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -7639,6 +7639,53 @@ void Transformation::_exportToWKT(io::WKTFormatter *formatter) const { // --------------------------------------------------------------------------- +static void exportSourceCRSAndTargetCRSToWKT(const CoordinateOperation *co, + io::WKTFormatter *formatter) { + auto l_sourceCRS = co->sourceCRS(); + assert(l_sourceCRS); + auto l_targetCRS = co->targetCRS(); + assert(l_targetCRS); + const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; + const bool canExportCRSId = + (isWKT2 && formatter->use2018Keywords() && + !(formatter->idOnTopLevelOnly() && formatter->topLevelHasId())); + + const bool hasDomains = !co->domains().empty(); + if (hasDomains) { + formatter->pushDisableUsage(); + } + + formatter->startNode(io::WKTConstants::SOURCECRS, false); + if (canExportCRSId && !l_sourceCRS->identifiers().empty()) { + // fake that top node has no id, so that the sourceCRS id is + // considered + formatter->pushHasId(false); + l_sourceCRS->_exportToWKT(formatter); + formatter->popHasId(); + } else { + l_sourceCRS->_exportToWKT(formatter); + } + formatter->endNode(); + + formatter->startNode(io::WKTConstants::TARGETCRS, false); + if (canExportCRSId && !l_targetCRS->identifiers().empty()) { + // fake that top node has no id, so that the targetCRS id is + // considered + formatter->pushHasId(false); + l_targetCRS->_exportToWKT(formatter); + formatter->popHasId(); + } else { + l_targetCRS->_exportToWKT(formatter); + } + formatter->endNode(); + + if (hasDomains) { + formatter->popDisableUsage(); + } +} + +// --------------------------------------------------------------------------- + void SingleOperation::exportTransformationToWKT( io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; @@ -7647,11 +7694,6 @@ void SingleOperation::exportTransformationToWKT( "Transformation can only be exported to WKT2"); } - auto l_sourceCRS = sourceCRS(); - assert(l_sourceCRS); - auto l_targetCRS = targetCRS(); - assert(l_targetCRS); - if (formatter->abridgedTransformation()) { formatter->startNode(io::WKTConstants::ABRIDGEDTRANSFORMATION, !identifiers().empty()); @@ -7672,13 +7714,7 @@ void SingleOperation::exportTransformationToWKT( } if (!formatter->abridgedTransformation()) { - formatter->startNode(io::WKTConstants::SOURCECRS, false); - l_sourceCRS->_exportToWKT(formatter); - formatter->endNode(); - - formatter->startNode(io::WKTConstants::TARGETCRS, false); - l_targetCRS->_exportToWKT(formatter); - formatter->endNode(); + exportSourceCRSAndTargetCRSToWKT(this, formatter); } method()->_exportToWKT(formatter); @@ -9337,20 +9373,34 @@ void ConcatenatedOperation::_exportToWKT(io::WKTFormatter *formatter) const { } } - formatter->startNode(io::WKTConstants::SOURCECRS, false); - sourceCRS()->_exportToWKT(formatter); - formatter->endNode(); + exportSourceCRSAndTargetCRSToWKT(this, formatter); - formatter->startNode(io::WKTConstants::TARGETCRS, false); - targetCRS()->_exportToWKT(formatter); - formatter->endNode(); + const bool canExportOperationId = + !(formatter->idOnTopLevelOnly() && formatter->topLevelHasId()); + + const bool hasDomains = !domains().empty(); + if (hasDomains) { + formatter->pushDisableUsage(); + } for (const auto &operation : operations()) { formatter->startNode(io::WKTConstants::STEP, false); - operation->_exportToWKT(formatter); + if (canExportOperationId && !operation->identifiers().empty()) { + // fake that top node has no id, so that the operation id is + // considered + formatter->pushHasId(false); + operation->_exportToWKT(formatter); + formatter->popHasId(); + } else { + operation->_exportToWKT(formatter); + } formatter->endNode(); } + if (hasDomains) { + formatter->popDisableUsage(); + } + ObjectUsage::baseExportToWKT(formatter); formatter->endNode(); } diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index 78b18719..578234b4 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -141,6 +141,7 @@ struct WKTFormatter::Private { std::vector stackHasChild_{}; std::vector stackHasId_{false}; std::vector stackEmptyKeyword_{}; + std::vector stackDisableUsage_{}; std::vector outputUnitStack_{true}; std::vector outputIdStack_{true}; std::vector axisLinearUnitStack_{ @@ -272,6 +273,11 @@ const std::string &WKTFormatter::toString() const { if (d->outputUnitStack_.size() != 1) throw FormattingException( "Unbalanced pushOutputUnit() / popOutputUnit()"); + if (d->stackHasId_.size() != 1) + throw FormattingException("Unbalanced pushHasId() / popHasId()"); + if (!d->stackDisableUsage_.empty()) + throw FormattingException( + "Unbalanced pushDisableUsage() / popDisableUsage()"); return d->result_; } @@ -556,6 +562,28 @@ bool WKTFormatter::outputId() const { // --------------------------------------------------------------------------- +void WKTFormatter::pushHasId(bool hasId) { d->stackHasId_.push_back(hasId); } + +// --------------------------------------------------------------------------- + +void WKTFormatter::popHasId() { d->stackHasId_.pop_back(); } + +// --------------------------------------------------------------------------- + +void WKTFormatter::pushDisableUsage() { d->stackDisableUsage_.push_back(true); } + +// --------------------------------------------------------------------------- + +void WKTFormatter::popDisableUsage() { d->stackDisableUsage_.pop_back(); } + +// --------------------------------------------------------------------------- + +bool WKTFormatter::outputUsage() const { + return outputId() && d->stackDisableUsage_.empty(); +} + +// --------------------------------------------------------------------------- + void WKTFormatter::pushAxisLinearUnit(const UnitOfMeasureNNPtr &unit) { d->axisLinearUnitStack_.push_back(unit); } diff --git a/test/cli/testprojinfo_out.dist b/test/cli/testprojinfo_out.dist index ee9aeca7..eaa10689 100644 --- a/test/cli/testprojinfo_out.dist +++ b/test/cli/testprojinfo_out.dist @@ -193,10 +193,6 @@ COORDINATEOPERATION["Ballpark geographic offset from NAD27 to NAD83", AXIS["geodetic longitude (Lon)",east, ORDER[2], ANGLEUNIT["degree",0.0174532925199433]], - USAGE[ - SCOPE["unknown"], - AREA["North America - NAD27"], - BBOX[7.15,167.65,83.17,-47.74]], ID["EPSG",4267]]], TARGETCRS[ GEOGCRS["NAD83", @@ -212,10 +208,6 @@ COORDINATEOPERATION["Ballpark geographic offset from NAD27 to NAD83", AXIS["geodetic longitude (Lon)",east, ORDER[2], ANGLEUNIT["degree",0.0174532925199433]], - USAGE[ - SCOPE["unknown"], - AREA["North America - NAD83"], - BBOX[14.92,167.65,86.46,-47.74]], ID["EPSG",4269]]], METHOD["Geographic2D offsets", ID["EPSG",9619]], @@ -265,7 +257,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (3)", ANGLEUNIT["degree",0.0174532925199433]], AXIS["geodetic longitude (Lon)",east, ORDER[2], - ANGLEUNIT["degree",0.0174532925199433]]]], + ANGLEUNIT["degree",0.0174532925199433]], + ID["EPSG",4267]]], TARGETCRS[ GEOGCRS["NAD83", DATUM["North American Datum 1983", @@ -279,7 +272,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (3)", ANGLEUNIT["degree",0.0174532925199433]], AXIS["geodetic longitude (Lon)",east, ORDER[2], - ANGLEUNIT["degree",0.0174532925199433]]]], + ANGLEUNIT["degree",0.0174532925199433]], + ID["EPSG",4269]]], METHOD["NTv1", ID["EPSG",9614]], PARAMETERFILE["Latitude and longitude difference file","ntv1_can.dat"], @@ -313,7 +307,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (4)", ANGLEUNIT["degree",0.0174532925199433]], AXIS["geodetic longitude (Lon)",east, ORDER[2], - ANGLEUNIT["degree",0.0174532925199433]]]], + ANGLEUNIT["degree",0.0174532925199433]], + ID["EPSG",4267]]], TARGETCRS[ GEOGCRS["NAD83", DATUM["North American Datum 1983", @@ -327,7 +322,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (4)", ANGLEUNIT["degree",0.0174532925199433]], AXIS["geodetic longitude (Lon)",east, ORDER[2], - ANGLEUNIT["degree",0.0174532925199433]]]], + ANGLEUNIT["degree",0.0174532925199433]], + ID["EPSG",4269]]], METHOD["NTv2", ID["EPSG",9615]], PARAMETERFILE["Latitude and longitude difference file","ntv2_0.gsb"], @@ -361,7 +357,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (1)", ANGLEUNIT["degree",0.0174532925199433]], AXIS["geodetic longitude (Lon)",east, ORDER[2], - ANGLEUNIT["degree",0.0174532925199433]]]], + ANGLEUNIT["degree",0.0174532925199433]], + ID["EPSG",4267]]], TARGETCRS[ GEOGCRS["NAD83", DATUM["North American Datum 1983", @@ -375,7 +372,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (1)", ANGLEUNIT["degree",0.0174532925199433]], AXIS["geodetic longitude (Lon)",east, ORDER[2], - ANGLEUNIT["degree",0.0174532925199433]]]], + ANGLEUNIT["degree",0.0174532925199433]], + ID["EPSG",4269]]], METHOD["CTABLE2"], PARAMETERFILE["Latitude and longitude difference file","conus"], OPERATIONACCURACY[0.15], @@ -408,7 +406,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (2)", ANGLEUNIT["degree",0.0174532925199433]], AXIS["geodetic longitude (Lon)",east, ORDER[2], - ANGLEUNIT["degree",0.0174532925199433]]]], + ANGLEUNIT["degree",0.0174532925199433]], + ID["EPSG",4267]]], TARGETCRS[ GEOGCRS["NAD83", DATUM["North American Datum 1983", @@ -422,7 +421,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (2)", ANGLEUNIT["degree",0.0174532925199433]], AXIS["geodetic longitude (Lon)",east, ORDER[2], - ANGLEUNIT["degree",0.0174532925199433]]]], + ANGLEUNIT["degree",0.0174532925199433]], + ID["EPSG",4269]]], METHOD["CTABLE2"], PARAMETERFILE["Latitude and longitude difference file","alaska"], OPERATIONACCURACY[0.5], @@ -455,7 +455,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (5)", ANGLEUNIT["degree",0.0174532925199433]], AXIS["geodetic longitude (Lon)",east, ORDER[2], - ANGLEUNIT["degree",0.0174532925199433]]]], + ANGLEUNIT["degree",0.0174532925199433]], + ID["EPSG",4267]]], TARGETCRS[ GEOGCRS["NAD83", DATUM["North American Datum 1983", @@ -469,7 +470,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (5)", ANGLEUNIT["degree",0.0174532925199433]], AXIS["geodetic longitude (Lon)",east, ORDER[2], - ANGLEUNIT["degree",0.0174532925199433]]]], + ANGLEUNIT["degree",0.0174532925199433]], + ID["EPSG",4269]]], METHOD["NTv1", ID["EPSG",9614]], PARAMETERFILE["Latitude and longitude difference file","GS2783v1.QUE"], @@ -503,7 +505,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (6)", ANGLEUNIT["degree",0.0174532925199433]], AXIS["geodetic longitude (Lon)",east, ORDER[2], - ANGLEUNIT["degree",0.0174532925199433]]]], + ANGLEUNIT["degree",0.0174532925199433]], + ID["EPSG",4267]]], TARGETCRS[ GEOGCRS["NAD83", DATUM["North American Datum 1983", @@ -517,7 +520,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (6)", ANGLEUNIT["degree",0.0174532925199433]], AXIS["geodetic longitude (Lon)",east, ORDER[2], - ANGLEUNIT["degree",0.0174532925199433]]]], + ANGLEUNIT["degree",0.0174532925199433]], + ID["EPSG",4269]]], METHOD["NTv2", ID["EPSG",9615]], PARAMETERFILE["Latitude and longitude difference file","QUE27-83.gsb"], @@ -552,10 +556,6 @@ COORDINATEOPERATION["Ballpark geographic offset from NAD27 to NAD83", AXIS["geodetic longitude (Lon)",east, ORDER[2], ANGLEUNIT["degree",0.0174532925199433]], - USAGE[ - SCOPE["unknown"], - AREA["North America - NAD27"], - BBOX[7.15,167.65,83.17,-47.74]], ID["EPSG",4267]]], TARGETCRS[ GEOGCRS["NAD83", @@ -571,10 +571,6 @@ COORDINATEOPERATION["Ballpark geographic offset from NAD27 to NAD83", AXIS["geodetic longitude (Lon)",east, ORDER[2], ANGLEUNIT["degree",0.0174532925199433]], - USAGE[ - SCOPE["unknown"], - AREA["North America - NAD83"], - BBOX[14.92,167.65,86.46,-47.74]], ID["EPSG",4269]]], METHOD["Geographic2D offsets", ID["EPSG",9619]], @@ -712,7 +708,8 @@ COORDINATEOPERATION["RH2000 height to SWEREF99", VDATUM["Rikets hojdsystem 2000"], CS[vertical,1], AXIS["gravity-related height (H)",up, - LENGTHUNIT["metre",1]]]], + LENGTHUNIT["metre",1]], + ID["EPSG",5613]]], TARGETCRS[ GEOGCRS["SWEREF99 (3D)", DATUM["SWEREF99", @@ -729,7 +726,8 @@ COORDINATEOPERATION["RH2000 height to SWEREF99", ANGLEUNIT["degree minute second hemisphere",0.0174532925199433]], AXIS["ellipsoidal height (h)",up, ORDER[3], - LENGTHUNIT["metre",1]]]], + LENGTHUNIT["metre",1]], + ID["EPSG",4377]]], METHOD["GravityRelatedHeight to Geographic3D", ID["PROJ","HEIGHT_TO_GEOGRAPHIC3D"]], PARAMETERFILE["Geoid (height correction) model file","SWEN17_RH2000.gtx"], diff --git a/test/unit/test_factory.cpp b/test/unit/test_factory.cpp index 944e0ebe..f4c560d5 100644 --- a/test/unit/test_factory.cpp +++ b/test/unit/test_factory.cpp @@ -734,7 +734,8 @@ TEST(factory, AuthorityFactory_createCoordinateOperation_helmert_15_CF) { " LENGTHUNIT[\"metre\",1]],\n" " AXIS[\"(Z)\",geocentricZ,\n" " ORDER[3],\n" - " LENGTHUNIT[\"metre\",1]]]],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " ID[\"EPSG\",5332]]],\n" " TARGETCRS[\n" " GEODCRS[\"GDA94\",\n" " DATUM[\"Geocentric Datum of Australia 1994\",\n" @@ -751,7 +752,8 @@ TEST(factory, AuthorityFactory_createCoordinateOperation_helmert_15_CF) { " LENGTHUNIT[\"metre\",1]],\n" " AXIS[\"(Z)\",geocentricZ,\n" " ORDER[3],\n" - " LENGTHUNIT[\"metre\",1]]]],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " ID[\"EPSG\",4938]]],\n" " METHOD[\"Time-dependent Coordinate Frame rotation (geocen)\",\n" " ID[\"EPSG\",1056]],\n" " PARAMETER[\"X-axis translation\",-84.68,\n" @@ -885,7 +887,8 @@ TEST( " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" " AXIS[\"geodetic longitude (Lon)\",east,\n" " ORDER[2],\n" - " ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " ID[\"EPSG\",4749]]],\n" " TARGETCRS[\n" " GEOGCRS[\"NEA74 Noumea\",\n" " DATUM[\"NEA74 Noumea\",\n" @@ -899,7 +902,8 @@ TEST( " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" " AXIS[\"geodetic longitude (Lon)\",east,\n" " ORDER[2],\n" - " ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " ID[\"EPSG\",4644]]],\n" " METHOD[\"NTv2\",\n" " ID[\"EPSG\",9615]],\n" " PARAMETERFILE[\"Latitude and longitude difference " @@ -953,7 +957,8 @@ TEST(factory, AuthorityFactory_createCoordinateOperation_other_transformation) { " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" " AXIS[\"geodetic longitude (Lon)\",east,\n" " ORDER[2],\n" - " ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " ID[\"EPSG\",4818]]],\n" " TARGETCRS[\n" " GEOGCRS[\"S-JTSK\",\n" " DATUM[\"System of the Unified Trigonometrical Cadastral " @@ -968,7 +973,8 @@ TEST(factory, AuthorityFactory_createCoordinateOperation_other_transformation) { " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" " AXIS[\"geodetic longitude (Lon)\",east,\n" " ORDER[2],\n" - " ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " ID[\"EPSG\",4156]]],\n" " METHOD[\"Longitude rotation\",\n" " ID[\"EPSG\",9601]],\n" " PARAMETER[\"Longitude offset\",-17.6666666666667,\n" diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp index 9111b862..848fb23b 100644 --- a/test/unit/test_operation.cpp +++ b/test/unit/test_operation.cpp @@ -442,7 +442,6 @@ TEST(operation, concatenated_operation) { { auto formatter = WKTFormatter::create(WKTFormatter::Convention::WKT2_2018); - formatter->setOutputId(false); src_wkt = GeographicCRS::EPSG_4326->exportToWKT(formatter.get()); } @@ -450,7 +449,6 @@ TEST(operation, concatenated_operation) { { auto formatter = WKTFormatter::create(WKTFormatter::Convention::WKT2_2018); - formatter->setOutputId(false); dst_wkt = GeographicCRS::EPSG_4979->exportToWKT(formatter.get()); } @@ -458,7 +456,6 @@ TEST(operation, concatenated_operation) { { auto formatter = WKTFormatter::create(WKTFormatter::Convention::WKT2_2018); - formatter->setOutputId(false); step1_wkt = transf_1->exportToWKT(formatter.get()); } @@ -466,7 +463,6 @@ TEST(operation, concatenated_operation) { { auto formatter = WKTFormatter::create(WKTFormatter::Convention::WKT2_2018); - formatter->setOutputId(false); step2_wkt = transf_2->exportToWKT(formatter.get()); } -- cgit v1.2.3