aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/proj/crs.hpp7
-rw-r--r--scripts/reference_exported_symbols.txt3
-rw-r--r--src/iso19111/c_api.cpp42
-rw-r--r--src/iso19111/crs.cpp81
-rw-r--r--src/proj_experimental.h4
-rw-r--r--test/unit/test_c_api.cpp25
-rw-r--r--test/unit/test_crs.cpp44
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);
}
}