From f20ff2b8226029cb8918d858614ec52a8b36a4bd Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 16 Aug 2021 11:00:05 +0200 Subject: [PATCH] Implement promoteTo3D() / demoteTo2D() for DerivedGeographicCRS (fixes #2803) --- include/proj/crs.hpp | 4 ++ scripts/reference_exported_symbols.txt | 1 + src/iso19111/crs.cpp | 76 +++++++++++++++++++++----- test/unit/test_crs.cpp | 22 ++++++++ 4 files changed, 89 insertions(+), 14 deletions(-) diff --git a/include/proj/crs.hpp b/include/proj/crs.hpp index 0f8c5e42c9..44dda0c548 100644 --- a/include/proj/crs.hpp +++ b/include/proj/crs.hpp @@ -1168,6 +1168,10 @@ class PROJ_GCC_DLL DerivedGeographicCRS final : public GeographicCRS, const operation::ConversionNNPtr &derivingConversionIn, const cs::EllipsoidalCSNNPtr &csIn); + PROJ_DLL DerivedGeographicCRSNNPtr + demoteTo2D(const std::string &newName, + const io::DatabaseContextPtr &dbContext) const; + //! @cond Doxygen_Suppress PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter) const override; // throw(io::FormattingException) diff --git a/scripts/reference_exported_symbols.txt b/scripts/reference_exported_symbols.txt index 4f853090cb..f806639480 100644 --- a/scripts/reference_exported_symbols.txt +++ b/scripts/reference_exported_symbols.txt @@ -131,6 +131,7 @@ osgeo::proj::crs::DerivedGeodeticCRS::~DerivedGeodeticCRS() osgeo::proj::crs::DerivedGeodeticCRS::_exportToWKT(osgeo::proj::io::WKTFormatter*) const osgeo::proj::crs::DerivedGeographicCRS::baseCRS() const osgeo::proj::crs::DerivedGeographicCRS::create(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn > const&, dropbox::oxygen::nn > const&, dropbox::oxygen::nn > const&) +osgeo::proj::crs::DerivedGeographicCRS::demoteTo2D(std::string const&, std::shared_ptr const&) const osgeo::proj::crs::DerivedGeographicCRS::~DerivedGeographicCRS() osgeo::proj::crs::DerivedProjectedCRS::baseCRS() const osgeo::proj::crs::DerivedProjectedCRS::create(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn > const&, dropbox::oxygen::nn > const&, dropbox::oxygen::nn > const&) diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index 28f55ffe7f..62bb24c3d1 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -1079,8 +1079,24 @@ CRSNNPtr CRS::promoteTo3D(const std::string &newName, return props; }; - const auto geogCRS = dynamic_cast(this); - if (geogCRS) { + if (auto derivedGeogCRS = + dynamic_cast(this)) { + const auto &axisList = derivedGeogCRS->coordinateSystem()->axisList(); + if (axisList.size() == 2) { + auto cs = cs::EllipsoidalCS::create( + util::PropertyMap(), axisList[0], axisList[1], + verticalAxisIfNotAlreadyPresent); + auto baseGeog3DCRS = util::nn_dynamic_pointer_cast( + derivedGeogCRS->baseCRS()->promoteTo3D( + std::string(), dbContext, verticalAxisIfNotAlreadyPresent)); + return util::nn_static_pointer_cast( + DerivedGeographicCRS::create( + createProperties(), NN_CHECK_THROW(baseGeog3DCRS), + derivedGeogCRS->derivingConversion(), cs)); + } + } + + else if (auto geogCRS = dynamic_cast(this)) { const auto &axisList = geogCRS->coordinateSystem()->axisList(); if (axisList.size() == 2) { const auto &l_identifiers = identifiers(); @@ -1120,8 +1136,7 @@ CRSNNPtr CRS::promoteTo3D(const std::string &newName, } } - const auto projCRS = dynamic_cast(this); - if (projCRS) { + else if (auto projCRS = dynamic_cast(this)) { const auto &axisList = projCRS->coordinateSystem()->axisList(); if (axisList.size() == 2) { auto base3DCRS = @@ -1137,8 +1152,7 @@ CRSNNPtr CRS::promoteTo3D(const std::string &newName, } } - const auto boundCRS = dynamic_cast(this); - if (boundCRS) { + else if (auto boundCRS = dynamic_cast(this)) { auto base3DCRS = boundCRS->baseCRS()->promoteTo3D( newName, dbContext, verticalAxisIfNotAlreadyPresent); auto transf = boundCRS->transformation(); @@ -1174,18 +1188,21 @@ CRSNNPtr CRS::promoteTo3D(const std::string &newName, */ CRSNNPtr CRS::demoteTo2D(const std::string &newName, const io::DatabaseContextPtr &dbContext) const { - const auto geogCRS = dynamic_cast(this); - if (geogCRS) { + + if (auto derivedGeogCRS = + dynamic_cast(this)) { + return derivedGeogCRS->demoteTo2D(newName, dbContext); + } + + else if (auto geogCRS = dynamic_cast(this)) { return geogCRS->demoteTo2D(newName, dbContext); } - const auto projCRS = dynamic_cast(this); - if (projCRS) { + else if (auto projCRS = dynamic_cast(this)) { return projCRS->demoteTo2D(newName, dbContext); } - const auto boundCRS = dynamic_cast(this); - if (boundCRS) { + else if (auto boundCRS = dynamic_cast(this)) { auto base2DCRS = boundCRS->baseCRS()->demoteTo2D(newName, dbContext); auto transf = boundCRS->transformation(); try { @@ -1199,8 +1216,7 @@ CRSNNPtr CRS::demoteTo2D(const std::string &newName, } } - const auto compoundCRS = dynamic_cast(this); - if (compoundCRS) { + else if (auto compoundCRS = dynamic_cast(this)) { const auto &components = compoundCRS->componentReferenceSystems(); if (components.size() >= 2) { return components[0]; @@ -5898,6 +5914,38 @@ bool DerivedGeographicCRS::_isEquivalentTo( // --------------------------------------------------------------------------- +/** \brief Return a variant of this CRS "demoted" to a 2D one, if not already + * the case. + * + * + * @param newName Name of the new CRS. If empty, nameStr() will be used. + * @param dbContext Database context to look for potentially already registered + * 2D CRS. May be nullptr. + * @return a new CRS demoted to 2D, or the current one if already 2D or not + * applicable. + * @since 8.1.1 + */ +DerivedGeographicCRSNNPtr DerivedGeographicCRS::demoteTo2D( + const std::string &newName, const io::DatabaseContextPtr &dbContext) const { + + const auto &axisList = coordinateSystem()->axisList(); + if (axisList.size() == 3) { + auto cs = cs::EllipsoidalCS::create(util::PropertyMap(), axisList[0], + axisList[1]); + auto baseGeog2DCRS = util::nn_dynamic_pointer_cast( + baseCRS()->demoteTo2D(std::string(), dbContext)); + return DerivedGeographicCRS::create( + util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, + !newName.empty() ? newName : nameStr()), + NN_CHECK_THROW(baseGeog2DCRS), derivingConversion(), cs); + } + + return NN_NO_CHECK(std::dynamic_pointer_cast( + shared_from_this().as_nullable())); +} + +// --------------------------------------------------------------------------- + //! @cond Doxygen_Suppress std::list> diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp index 10b21ee580..91424b2383 100644 --- a/test/unit/test_crs.cpp +++ b/test/unit/test_crs.cpp @@ -6403,6 +6403,28 @@ TEST(crs, promoteTo3D_and_demoteTo2D) { EXPECT_TRUE(dynamic_cast(demoted.get()) != nullptr); } + + { + auto crs = createDerivedGeographicCRS(); + auto crs3D = crs->promoteTo3D(std::string(), dbContext); + auto crs3DAsDerivedGeog = + nn_dynamic_pointer_cast(crs3D); + ASSERT_TRUE(crs3DAsDerivedGeog != nullptr); + EXPECT_EQ(crs3DAsDerivedGeog->baseCRS() + ->coordinateSystem() + ->axisList() + .size(), + 3U); + EXPECT_EQ(crs3DAsDerivedGeog->coordinateSystem()->axisList().size(), + 3U); + + auto demoted = crs3DAsDerivedGeog->demoteTo2D(std::string(), dbContext); + EXPECT_EQ(demoted->baseCRS()->coordinateSystem()->axisList().size(), + 2U); + EXPECT_EQ(demoted->coordinateSystem()->axisList().size(), 2U); + EXPECT_TRUE(demoted->isEquivalentTo( + crs.get(), IComparable::Criterion::EQUIVALENT)); + } } // ---------------------------------------------------------------------------