aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/proj/crs.hpp19
-rw-r--r--src/iso19111/crs.cpp100
-rw-r--r--test/unit/test_io.cpp38
3 files changed, 107 insertions, 50 deletions
diff --git a/include/proj/crs.hpp b/include/proj/crs.hpp
index 7aa74c41..bbdc9565 100644
--- a/include/proj/crs.hpp
+++ b/include/proj/crs.hpp
@@ -68,6 +68,12 @@ using BoundCRSPtr = std::shared_ptr<BoundCRS>;
/** Non-null shared pointer of BoundCRS */
using BoundCRSNNPtr = util::nn<BoundCRSPtr>;
+class CompoundCRS;
+/** Shared pointer of CompoundCRS */
+using CompoundCRSPtr = std::shared_ptr<CompoundCRS>;
+/** Non-null shared pointer of CompoundCRS */
+using CompoundCRSNNPtr = util::nn<CompoundCRSPtr>;
+
// ---------------------------------------------------------------------------
class CRS;
@@ -141,7 +147,12 @@ class PROJ_GCC_DLL CRS : public common::ObjectUsage,
PROJ_INTERNAL CRSNNPtr allowNonConformantWKT1Export() const;
PROJ_INTERNAL CRSNNPtr
- attachOriginalVertCRS(const VerticalCRSNNPtr &vertCRS) const;
+ attachOriginalCompoundCRS(const CompoundCRSNNPtr &compoundCRS) const;
+
+ PROJ_INTERNAL CRSNNPtr promoteTo3D(
+ const std::string &newName, const io::DatabaseContextPtr &dbContext,
+ const cs::CoordinateSystemAxisNNPtr &verticalAxisIfNotAlreadyPresent)
+ const;
//! @endcond
@@ -855,12 +866,6 @@ class PROJ_GCC_DLL InvalidCompoundCRSException : public util::Exception {
// ---------------------------------------------------------------------------
-class CompoundCRS;
-/** Shared pointer of CompoundCRS */
-using CompoundCRSPtr = std::shared_ptr<CompoundCRS>;
-/** Non-null shared pointer of CompoundCRS */
-using CompoundCRSNNPtr = util::nn<CompoundCRSPtr>;
-
/** \brief A coordinate reference system describing the position of points
* through two or more independent single coordinate reference systems.
*
diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp
index bad7deea..35e11d84 100644
--- a/src/iso19111/crs.cpp
+++ b/src/iso19111/crs.cpp
@@ -98,7 +98,7 @@ struct CRS::Private {
bool allowNonConformantWKT1Export_ = false;
// for what was initially a COMPD_CS with a VERT_CS with a datum type ==
// ellipsoidal height / 2002
- VerticalCRSPtr originalVertCRS_{};
+ CompoundCRSPtr originalCompoundCRS_{};
void setImplicitCS(const util::PropertyMap &properties) {
const auto pVal = properties.get("IMPLICIT_CS");
@@ -583,17 +583,18 @@ CRSNNPtr CRS::allowNonConformantWKT1Export() const {
//! @cond Doxygen_Suppress
-CRSNNPtr CRS::attachOriginalVertCRS(const VerticalCRSNNPtr &vertCRS) const {
+CRSNNPtr
+CRS::attachOriginalCompoundCRS(const CompoundCRSNNPtr &compoundCRS) const {
const auto boundCRS = dynamic_cast<const BoundCRS *>(this);
if (boundCRS) {
return BoundCRS::create(
- boundCRS->baseCRS()->attachOriginalVertCRS(vertCRS),
+ boundCRS->baseCRS()->attachOriginalCompoundCRS(compoundCRS),
boundCRS->hubCRS(), boundCRS->transformation());
}
auto crs(shallowClone());
- crs->d->originalVertCRS_ = vertCRS.as_nullable();
+ crs->d->originalCompoundCRS_ = compoundCRS.as_nullable();
return crs;
}
@@ -868,7 +869,22 @@ CRS::getNonDeprecated(const io::DatabaseContextNNPtr &dbContext) const {
*/
CRSNNPtr CRS::promoteTo3D(const std::string &newName,
const io::DatabaseContextPtr &dbContext) const {
+ auto upAxis = cs::CoordinateSystemAxis::create(
+ util::PropertyMap().set(IdentifiedObject::NAME_KEY,
+ cs::AxisName::Ellipsoidal_height),
+ cs::AxisAbbreviation::h, cs::AxisDirection::UP,
+ common::UnitOfMeasure::METRE);
+ return promoteTo3D(newName, dbContext, upAxis);
+}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+CRSNNPtr CRS::promoteTo3D(const std::string &newName,
+ const io::DatabaseContextPtr &dbContext,
+ const cs::CoordinateSystemAxisNNPtr
+ &verticalAxisIfNotAlreadyPresent) const {
const auto geogCRS = dynamic_cast<const GeographicCRS *>(this);
if (geogCRS) {
const auto &axisList = geogCRS->coordinateSystem()->axisList();
@@ -886,21 +902,23 @@ CRSNNPtr CRS::promoteTo3D(const std::string &newName,
false);
if (!res.empty()) {
const auto &firstRes = res.front();
- if (geogCRS->is2DPartOf3D(NN_NO_CHECK(
- dynamic_cast<GeographicCRS *>(firstRes.get())))) {
+ const auto firstResGeog =
+ dynamic_cast<GeographicCRS *>(firstRes.get());
+ const auto &firstResAxisList =
+ firstResGeog->coordinateSystem()->axisList();
+ if (firstResAxisList[2]->_isEquivalentTo(
+ verticalAxisIfNotAlreadyPresent.get(),
+ util::IComparable::Criterion::EQUIVALENT) &&
+ geogCRS->is2DPartOf3D(NN_NO_CHECK(firstResGeog))) {
return NN_NO_CHECK(
util::nn_dynamic_pointer_cast<CRS>(firstRes));
}
}
}
- auto upAxis = cs::CoordinateSystemAxis::create(
- util::PropertyMap().set(IdentifiedObject::NAME_KEY,
- cs::AxisName::Ellipsoidal_height),
- cs::AxisAbbreviation::h, cs::AxisDirection::UP,
- common::UnitOfMeasure::METRE);
auto cs = cs::EllipsoidalCS::create(
- util::PropertyMap(), axisList[0], axisList[1], upAxis);
+ util::PropertyMap(), axisList[0], axisList[1],
+ verticalAxisIfNotAlreadyPresent);
return util::nn_static_pointer_cast<CRS>(GeographicCRS::create(
util::PropertyMap().set(common::IdentifiedObject::NAME_KEY,
!newName.empty() ? newName : nameStr()),
@@ -914,13 +932,9 @@ CRSNNPtr CRS::promoteTo3D(const std::string &newName,
if (axisList.size() == 2) {
auto base3DCRS =
projCRS->baseCRS()->promoteTo3D(std::string(), dbContext);
- auto upAxis = cs::CoordinateSystemAxis::create(
- util::PropertyMap().set(IdentifiedObject::NAME_KEY,
- cs::AxisName::Ellipsoidal_height),
- cs::AxisAbbreviation::h, cs::AxisDirection::UP,
- common::UnitOfMeasure::METRE);
auto cs = cs::CartesianCS::create(util::PropertyMap(), axisList[0],
- axisList[1], upAxis);
+ axisList[1],
+ verticalAxisIfNotAlreadyPresent);
return util::nn_static_pointer_cast<CRS>(ProjectedCRS::create(
util::PropertyMap().set(common::IdentifiedObject::NAME_KEY,
!newName.empty() ? newName : nameStr()),
@@ -932,7 +946,8 @@ CRSNNPtr CRS::promoteTo3D(const std::string &newName,
const auto boundCRS = dynamic_cast<const BoundCRS *>(this);
if (boundCRS) {
- auto base3DCRS = boundCRS->baseCRS()->promoteTo3D(newName, dbContext);
+ auto base3DCRS = boundCRS->baseCRS()->promoteTo3D(
+ newName, dbContext, verticalAxisIfNotAlreadyPresent);
auto transf = boundCRS->transformation();
try {
transf->getTOWGS84Parameters();
@@ -948,6 +963,9 @@ CRSNNPtr CRS::promoteTo3D(const std::string &newName,
return NN_NO_CHECK(
std::static_pointer_cast<CRS>(shared_from_this().as_nullable()));
}
+
+//! @endcond
+
// ---------------------------------------------------------------------------
/** \brief Return a variant of this CRS "demoted" to a 2D one, if not already
@@ -1436,14 +1454,9 @@ void GeodeticCRS::_exportToWKT(io::WKTFormatter *formatter) const {
return;
}
- auto &originalVertCRS = CRS::getPrivate()->originalVertCRS_;
- if (originalVertCRS) {
- formatter->startNode(io::WKTConstants::COMPD_CS, false);
- formatter->addQuotedString(l_name + " + " +
- originalVertCRS->nameStr());
- geogCRS2D->_exportToWKT(formatter);
- originalVertCRS->_exportToWKT(formatter);
- formatter->endNode();
+ auto &originalCompoundCRS = CRS::getPrivate()->originalCompoundCRS_;
+ if (originalCompoundCRS) {
+ originalCompoundCRS->_exportToWKT(formatter);
return;
}
@@ -3292,14 +3305,9 @@ void ProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const {
return;
}
- auto &originalVertCRS = CRS::getPrivate()->originalVertCRS_;
- if (!formatter->useESRIDialect() && originalVertCRS) {
- formatter->startNode(io::WKTConstants::COMPD_CS, false);
- formatter->addQuotedString(l_name + " + " +
- originalVertCRS->nameStr());
- projCRS2D->_exportToWKT(formatter);
- originalVertCRS->_exportToWKT(formatter);
- formatter->endNode();
+ auto &originalCompoundCRS = CRS::getPrivate()->originalCompoundCRS_;
+ if (!formatter->useESRIDialect() && originalCompoundCRS) {
+ originalCompoundCRS->_exportToWKT(formatter);
return;
}
@@ -4254,8 +4262,8 @@ CRSNNPtr CompoundCRS::createLax(const util::PropertyMap &properties,
auto comp1 = components[1].get();
auto comp0Geog = dynamic_cast<const GeographicCRS *>(comp0);
auto comp0Proj = dynamic_cast<const ProjectedCRS *>(comp0);
+ auto comp0Bound = dynamic_cast<const BoundCRS *>(comp0);
if (comp0Geog == nullptr && comp0Proj == nullptr) {
- auto comp0Bound = dynamic_cast<const BoundCRS *>(comp0);
if (comp0Bound) {
const auto *baseCRS = comp0Bound->baseCRS().get();
comp0Geog = dynamic_cast<const GeographicCRS *>(baseCRS);
@@ -4286,14 +4294,20 @@ CRSNNPtr CompoundCRS::createLax(const util::PropertyMap &properties,
if (comp1Vert != nullptr && comp1Vert->datum() &&
comp1Vert->datum()->getWKT1DatumType() == "2002") {
const auto &axis = comp1Vert->coordinateSystem()->axisList()[0];
- if (axis->unit()._isEquivalentTo(
- common::UnitOfMeasure::METRE,
- util::IComparable::Criterion::EQUIVALENT) &&
- &(axis->direction()) == &(cs::AxisDirection::UP)) {
- return components[0]
- ->promoteTo3D(std::string(), dbContext)
- ->attachOriginalVertCRS(NN_NO_CHECK(comp1Vert));
+ std::string name(components[0]->nameStr());
+ if (!(axis->unit()._isEquivalentTo(
+ common::UnitOfMeasure::METRE,
+ util::IComparable::Criterion::EQUIVALENT) &&
+ &(axis->direction()) == &(cs::AxisDirection::UP))) {
+ name += " (" + comp1Vert->nameStr() + ')';
}
+ return components[0]
+ ->promoteTo3D(name, dbContext, axis)
+ ->attachOriginalCompoundCRS(create(
+ properties,
+ comp0Bound ? std::vector<CRSNNPtr>{comp0Bound->baseCRS(),
+ components[1]}
+ : components));
}
}
diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp
index d62e5c59..17fddaaf 100644
--- a/test/unit/test_io.cpp
+++ b/test/unit/test_io.cpp
@@ -2614,6 +2614,44 @@ TEST(wkt_parse,
// ---------------------------------------------------------------------------
+TEST(wkt_parse,
+ COMPD_CS_horizontal_geog_plus_vertical_ellipsoidal_height_non_metre) {
+ // See https://github.com/OSGeo/PROJ/issues/2232
+ const char *wkt =
+ "COMPD_CS[\"NAD83 + Ellipsoid (US Feet)\",\n"
+ " GEOGCS[\"NAD83\",\n"
+ " DATUM[\"North_American_Datum_1983\",\n"
+ " SPHEROID[\"GRS 1980\",6378137,298.257222101,\n"
+ " AUTHORITY[\"EPSG\",\"7019\"]],\n"
+ " AUTHORITY[\"EPSG\",\"6269\"]],\n"
+ " PRIMEM[\"Greenwich\",0,\n"
+ " AUTHORITY[\"EPSG\",\"8901\"]],\n"
+ " UNIT[\"degree\",0.0174532925199433,\n"
+ " AUTHORITY[\"EPSG\",\"9122\"]],\n"
+ " AUTHORITY[\"EPSG\",\"4269\"]],\n"
+ " VERT_CS[\"Ellipsoid (US Feet)\",\n"
+ " VERT_DATUM[\"Ellipsoid\",2002],\n"
+ " UNIT[\"US survey foot\",0.304800609601219,\n"
+ " AUTHORITY[\"EPSG\",\"9003\"]],\n"
+ " AXIS[\"Up\",UP]]]";
+ auto dbContext = DatabaseContext::create();
+ auto obj = WKTParser().attachDatabaseContext(dbContext).createFromWKT(wkt);
+ auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->nameStr(), "NAD83 (Ellipsoid (US Feet))");
+ EXPECT_EQ(crs->coordinateSystem()->axisList().size(), 3U);
+ EXPECT_NEAR(crs->coordinateSystem()->axisList()[2]->unit().conversionToSI(),
+ 0.304800609601219, 1e-15);
+
+ EXPECT_EQ(
+ crs->exportToWKT(
+ WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL, dbContext)
+ .get()),
+ wkt);
+}
+
+// ---------------------------------------------------------------------------
+
TEST(wkt_parse, COORDINATEOPERATION) {
std::string src_wkt;