diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2020-05-19 12:42:50 +0200 |
|---|---|---|
| committer | Even Rouault <even.rouault@spatialys.com> | 2020-05-19 14:20:55 +0200 |
| commit | ec3fdd00f133736560f807765dd73367c85f4bdc (patch) | |
| tree | 619de908565fbb03e18d90da94b88a3eb5fef76b | |
| parent | 1a715234754146ebe224fb849a87ca6575fdc88f (diff) | |
| download | PROJ-ec3fdd00f133736560f807765dd73367c85f4bdc.tar.gz PROJ-ec3fdd00f133736560f807765dd73367c85f4bdc.zip | |
WKT1 ingestion: fix ingestion of COMPD_CS with ellipsoidal vertical datum and non metre units (contributes to fixes #2232)
| -rw-r--r-- | include/proj/crs.hpp | 19 | ||||
| -rw-r--r-- | src/iso19111/crs.cpp | 100 | ||||
| -rw-r--r-- | test/unit/test_io.cpp | 38 |
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; |
