diff options
| -rw-r--r-- | include/proj/crs.hpp | 7 | ||||
| -rw-r--r-- | scripts/reference_exported_symbols.txt | 3 | ||||
| -rw-r--r-- | src/iso19111/c_api.cpp | 42 | ||||
| -rw-r--r-- | src/iso19111/crs.cpp | 81 | ||||
| -rw-r--r-- | src/proj_experimental.h | 4 | ||||
| -rw-r--r-- | test/unit/test_c_api.cpp | 25 | ||||
| -rw-r--r-- | test/unit/test_crs.cpp | 44 |
7 files changed, 201 insertions, 5 deletions
diff --git a/include/proj/crs.hpp b/include/proj/crs.hpp index c07904c8..2b919c71 100644 --- a/include/proj/crs.hpp +++ b/include/proj/crs.hpp @@ -111,6 +111,9 @@ class PROJ_GCC_DLL CRS : public common::ObjectUsage, promoteTo3D(const std::string &newName, const io::DatabaseContextPtr &dbContext) const; + PROJ_DLL CRSNNPtr demoteTo2D(const std::string &newName, + const io::DatabaseContextPtr &dbContext) const; + PROJ_PRIVATE : //! @cond Doxygen_Suppress PROJ_INTERNAL const GeodeticCRS * @@ -588,6 +591,10 @@ class PROJ_GCC_DLL ProjectedCRS final : public DerivedCRS, PROJ_DLL std::list<std::pair<ProjectedCRSNNPtr, int>> identify(const io::AuthorityFactoryPtr &authorityFactory) const; + PROJ_DLL ProjectedCRSNNPtr + demoteTo2D(const std::string &newName, + const io::DatabaseContextPtr &dbContext) const; + PROJ_PRIVATE : //! @cond Doxygen_Suppress PROJ_INTERNAL void diff --git a/scripts/reference_exported_symbols.txt b/scripts/reference_exported_symbols.txt index 040ef88e..ceb1ebdf 100644 --- a/scripts/reference_exported_symbols.txt +++ b/scripts/reference_exported_symbols.txt @@ -109,6 +109,7 @@ osgeo::proj::crs::CRS::alterName(std::string const&) const osgeo::proj::crs::CRS::canonicalBoundCRS() const osgeo::proj::crs::CRS::createBoundCRSToWGS84IfPossible(std::shared_ptr<osgeo::proj::io::DatabaseContext> const&, osgeo::proj::operation::CoordinateOperationContext::IntermediateCRSUse) const osgeo::proj::crs::CRS::~CRS() +osgeo::proj::crs::CRS::demoteTo2D(std::string const&, std::shared_ptr<osgeo::proj::io::DatabaseContext> const&) const osgeo::proj::crs::CRS::extractGeodeticCRS() const osgeo::proj::crs::CRS::extractGeographicCRS() const osgeo::proj::crs::CRS::extractVerticalCRS() const @@ -162,6 +163,7 @@ osgeo::proj::crs::ProjectedCRS::alterParametersLinearUnit(osgeo::proj::common::U osgeo::proj::crs::ProjectedCRS::baseCRS() const osgeo::proj::crs::ProjectedCRS::coordinateSystem() const osgeo::proj::crs::ProjectedCRS::create(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::GeodeticCRS> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::operation::Conversion> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::cs::CartesianCS> > const&) +osgeo::proj::crs::ProjectedCRS::demoteTo2D(std::string const&, std::shared_ptr<osgeo::proj::io::DatabaseContext> const&) const osgeo::proj::crs::ProjectedCRS::identify(std::shared_ptr<osgeo::proj::io::AuthorityFactory> const&) const osgeo::proj::crs::ProjectedCRS::~ProjectedCRS() osgeo::proj::crs::SingleCRS::coordinateSystem() const @@ -904,6 +906,7 @@ proj_crs_create_bound_crs proj_crs_create_bound_crs_to_WGS84 proj_crs_create_bound_vertical_crs_to_WGS84 proj_crs_create_projected_3D_crs_from_2D +proj_crs_demote_to_2D proj_crs_get_coordinate_system proj_crs_get_coordoperation proj_crs_get_datum diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index ac3a5f11..4fedbe05 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -3388,6 +3388,48 @@ PJ *proj_crs_create_projected_3D_crs_from_2D(PJ_CONTEXT *ctx, // --------------------------------------------------------------------------- +/** \brief Create a 2D CRS from an existing 3D CRS. + * + * See osgeo::proj::crs::CRS::demoteTo2D(). + * + * The returned object must be unreferenced with proj_destroy() after + * use. + * It should be used by at most one thread at a time. + * + * @param ctx PROJ context, or NULL for default context + * @param crs_2D_name CRS name. Or NULL (in which case the name of crs_3D + * will be used) + * @param crs_3D 3D CRS to be "demoted" to 2D. Must not be NULL. + * + * @return Object that must be unreferenced with + * proj_destroy(), or NULL in case of error. + * @since 7.0 + */ +PJ *proj_crs_demote_to_2D(PJ_CONTEXT *ctx, const char *crs_2D_name, + const PJ *crs_3D) { + SANITIZE_CTX(ctx); + auto cpp_3D_crs = dynamic_cast<const CRS *>(crs_3D->iso_obj.get()); + if (!cpp_3D_crs) { + proj_log_error(ctx, __FUNCTION__, "crs_3D is not a CRS"); + return nullptr; + } + try { + auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); + return pj_obj_create( + ctx, cpp_3D_crs->demoteTo2D(crs_2D_name ? std::string(crs_2D_name) + : cpp_3D_crs->nameStr(), + dbContext)); + } catch (const std::exception &e) { + proj_log_error(ctx, __FUNCTION__, e.what()); + if (ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } + return nullptr; + } +} + +// --------------------------------------------------------------------------- + /** \brief Instantiate a EngineeringCRS with just a name * * The returned object must be unreferenced with proj_destroy() after diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index d0e23f95..b98e5937 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -843,6 +843,49 @@ CRSNNPtr CRS::promoteTo3D(const std::string &newName, return NN_NO_CHECK( std::static_pointer_cast<CRS>(shared_from_this().as_nullable())); } +// --------------------------------------------------------------------------- + +/** \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 7.0 + */ +CRSNNPtr CRS::demoteTo2D(const std::string &newName, + const io::DatabaseContextPtr &dbContext) const { + const auto geogCRS = dynamic_cast<const GeographicCRS *>(this); + if (geogCRS) { + return geogCRS->demoteTo2D(newName, dbContext); + } + + const auto projCRS = dynamic_cast<const ProjectedCRS *>(this); + if (projCRS) { + return projCRS->demoteTo2D(newName, dbContext); + } + + const auto boundCRS = dynamic_cast<const BoundCRS *>(this); + if (boundCRS) { + return BoundCRS::create( + boundCRS->baseCRS()->demoteTo2D(newName, dbContext), + boundCRS->hubCRS(), boundCRS->transformation()); + } + + const auto compoundCRS = dynamic_cast<const CompoundCRS *>(this); + if (compoundCRS) { + const auto &components = compoundCRS->componentReferenceSystems(); + if (components.size() >= 2) { + return components[0]; + } + } + + return NN_NO_CHECK( + std::static_pointer_cast<CRS>(shared_from_this().as_nullable())); +} // --------------------------------------------------------------------------- @@ -3650,6 +3693,44 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { // --------------------------------------------------------------------------- +/** \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 7.0 + */ +ProjectedCRSNNPtr +ProjectedCRS::demoteTo2D(const std::string &newName, + const io::DatabaseContextPtr &dbContext) const { + + const auto &axisList = coordinateSystem()->axisList(); + if (axisList.size() == 3) { + auto cs = cs::CartesianCS::create(util::PropertyMap(), axisList[0], + axisList[1]); + const auto &l_baseCRS = baseCRS(); + const auto geogCRS = + dynamic_cast<const GeographicCRS *>(l_baseCRS.get()); + const auto newBaseCRS = + geogCRS ? util::nn_static_pointer_cast<GeodeticCRS>( + geogCRS->demoteTo2D(std::string(), dbContext)) + : l_baseCRS; + return ProjectedCRS::create( + util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, + !newName.empty() ? newName : nameStr()), + newBaseCRS, derivingConversionRef(), cs); + } + + return NN_NO_CHECK(std::dynamic_pointer_cast<ProjectedCRS>( + shared_from_this().as_nullable())); +} + +// --------------------------------------------------------------------------- + //! @cond Doxygen_Suppress std::list<std::pair<CRSNNPtr, int>> diff --git a/src/proj_experimental.h b/src/proj_experimental.h index 9a1a6045..63b858d5 100644 --- a/src/proj_experimental.h +++ b/src/proj_experimental.h @@ -235,6 +235,10 @@ PJ PROJ_DLL *proj_crs_create_projected_3D_crs_from_2D(PJ_CONTEXT *ctx, const PJ* projected_2D_crs, const PJ* geog_3D_crs); +PJ PROJ_DLL *proj_crs_demote_to_2D(PJ_CONTEXT *ctx, + const char* crs_2D_name, + const PJ* crs_3D); + PJ PROJ_DLL *proj_create_engineering_crs(PJ_CONTEXT *ctx, const char *crsName); diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp index 40122cb2..d5475346 100644 --- a/test/unit/test_c_api.cpp +++ b/test/unit/test_c_api.cpp @@ -3949,6 +3949,31 @@ TEST_F(CApi, proj_crs_promote_to_3D) { // --------------------------------------------------------------------------- +TEST_F(CApi, proj_crs_demote_to_2D) { + + auto crs3D = + proj_create(m_ctxt, GeographicCRS::EPSG_4979 + ->exportToWKT(WKTFormatter::create().get()) + .c_str()); + ObjectKeeper keeper_crs3D(crs3D); + EXPECT_NE(crs3D, nullptr); + + auto crs2D = proj_crs_demote_to_2D(m_ctxt, nullptr, crs3D); + ObjectKeeper keeper_crs2D(crs2D); + EXPECT_NE(crs2D, nullptr); + + auto cs = proj_crs_get_coordinate_system(m_ctxt, crs2D); + ASSERT_NE(cs, nullptr); + ObjectKeeper keeperCs(cs); + EXPECT_EQ(proj_cs_get_axis_count(m_ctxt, cs), 2); + + auto code = proj_get_id_code(crs2D, 0); + ASSERT_TRUE(code != nullptr); + EXPECT_EQ(code, std::string("4326")); +} + +// --------------------------------------------------------------------------- + TEST_F(CApi, proj_crs_create_projected_3D_crs_from_2D) { auto projCRS = proj_create_from_database(m_ctxt, "EPSG", "32631", diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp index aee4dd73..24f4ba52 100644 --- a/test/unit/test_crs.cpp +++ b/test/unit/test_crs.cpp @@ -5232,7 +5232,7 @@ TEST(crs, getNonDeprecated) { // --------------------------------------------------------------------------- -TEST(crs, promoteTo3D) { +TEST(crs, promoteTo3D_and_demoteTo2D) { auto dbContext = DatabaseContext::create(); { auto crs = GeographicCRS::EPSG_4326; @@ -5250,6 +5250,10 @@ TEST(crs, promoteTo3D) { ASSERT_TRUE(crs3DAsGeog != nullptr); EXPECT_EQ(crs3DAsGeog->coordinateSystem()->axisList().size(), 3U); EXPECT_TRUE(!crs3DAsGeog->identifiers().empty()); + + auto demoted = crs3DAsGeog->demoteTo2D(std::string(), dbContext); + EXPECT_EQ(demoted->coordinateSystem()->axisList().size(), 2U); + EXPECT_TRUE(!demoted->identifiers().empty()); } { auto crs = createProjected(); @@ -5262,6 +5266,11 @@ TEST(crs, promoteTo3D) { 3U); EXPECT_TRUE(crs3D->promoteTo3D(std::string(), nullptr) ->isEquivalentTo(crs3D.get())); + + auto demoted = crs3DAsProjected->demoteTo2D(std::string(), nullptr); + EXPECT_EQ(demoted->coordinateSystem()->axisList().size(), 2U); + EXPECT_EQ(demoted->baseCRS()->coordinateSystem()->axisList().size(), + 2U); } { auto crs = createProjected(); @@ -5273,6 +5282,12 @@ TEST(crs, promoteTo3D) { crs3DAsProjected->baseCRS()->coordinateSystem()->axisList().size(), 3U); EXPECT_TRUE(!crs3DAsProjected->baseCRS()->identifiers().empty()); + + auto demoted = crs3DAsProjected->demoteTo2D(std::string(), dbContext); + EXPECT_EQ(demoted->coordinateSystem()->axisList().size(), 2U); + EXPECT_EQ(demoted->baseCRS()->coordinateSystem()->axisList().size(), + 2U); + EXPECT_TRUE(!demoted->baseCRS()->identifiers().empty()); } { auto crs = BoundCRS::createFromTOWGS84( @@ -5280,9 +5295,28 @@ TEST(crs, promoteTo3D) { auto crs3D = crs->promoteTo3D(std::string(), dbContext); auto crs3DAsBound = nn_dynamic_pointer_cast<BoundCRS>(crs3D); ASSERT_TRUE(crs3DAsBound != nullptr); - auto baseCRS = - nn_dynamic_pointer_cast<ProjectedCRS>(crs3DAsBound->baseCRS()); - ASSERT_TRUE(baseCRS != nullptr); - EXPECT_EQ(baseCRS->coordinateSystem()->axisList().size(), 3U); + { + auto baseCRS = + nn_dynamic_pointer_cast<ProjectedCRS>(crs3DAsBound->baseCRS()); + ASSERT_TRUE(baseCRS != nullptr); + EXPECT_EQ(baseCRS->coordinateSystem()->axisList().size(), 3U); + } + + auto demoted = crs3DAsBound->demoteTo2D(std::string(), nullptr); + auto crs2DAsBound = nn_dynamic_pointer_cast<BoundCRS>(demoted); + ASSERT_TRUE(crs2DAsBound != nullptr); + { + auto baseCRS = + nn_dynamic_pointer_cast<ProjectedCRS>(crs2DAsBound->baseCRS()); + ASSERT_TRUE(baseCRS != nullptr); + EXPECT_EQ(baseCRS->coordinateSystem()->axisList().size(), 2U); + } + } + + { + auto compoundCRS = createCompoundCRS(); + auto demoted = compoundCRS->demoteTo2D(std::string(), nullptr); + EXPECT_TRUE(dynamic_cast<const ProjectedCRS *>(demoted.get()) != + nullptr); } } |
