diff options
Diffstat (limited to 'src/iso19111/crs.cpp')
| -rw-r--r-- | src/iso19111/crs.cpp | 142 |
1 files changed, 120 insertions, 22 deletions
diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index b0f5bcf9..77261211 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -93,6 +93,7 @@ struct CRS::Private { BoundCRSPtr canonicalBoundCRS_{}; std::string extensionProj4_{}; bool implicitCS_ = false; + bool allowNonConformantWKT1Export_ = false; void setImplicitCS(const util::PropertyMap &properties) { const auto pVal = properties.get("IMPLICIT_CS"); @@ -559,6 +560,18 @@ CRSNNPtr CRS::shallowClone() const { return _shallowClone(); } //! @cond Doxygen_Suppress +CRSNNPtr CRS::allowNonConformantWKT1Export() const { + auto crs = shallowClone(); + crs->d->allowNonConformantWKT1Export_ = true; + return crs; +} + +//! @endcond + +// --------------------------------------------------------------------------- + +//! @cond Doxygen_Suppress + CRSNNPtr CRS::alterName(const std::string &newName) const { auto crs = shallowClone(); auto newNameMod(newName); @@ -1334,30 +1347,49 @@ void GeodeticCRS::_exportToWKT(io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; const bool isGeographic = dynamic_cast<const GeographicCRS *>(this) != nullptr; - formatter->startNode(isWKT2 - ? ((formatter->use2019Keywords() && isGeographic) - ? io::WKTConstants::GEOGCRS - : io::WKTConstants::GEODCRS) - : isGeocentric() ? io::WKTConstants::GEOCCS - : io::WKTConstants::GEOGCS, - !identifiers().empty()); - auto l_name = nameStr(); + const auto &cs = coordinateSystem(); const auto &axisList = cs->axisList(); - const auto oldAxisOutputRule = formatter->outputAxis(); + auto l_name = nameStr(); + const auto &dbContext = formatter->databaseContext(); if (formatter->useESRIDialect()) { if (axisList.size() != 2) { io::FormattingException::Throw( "Only export of Geographic 2D CRS is supported in WKT1_ESRI"); } + } + + if (!isWKT2 && formatter->isStrict() && isGeographic && + axisList.size() != 2 && + oldAxisOutputRule != io::WKTFormatter::OutputAxisRule::NO) { + if (CRS::getPrivate()->allowNonConformantWKT1Export_) { + formatter->startNode(io::WKTConstants::COMPD_CS, false); + formatter->addQuotedString(l_name + " + " + l_name); + auto geogCRS = demoteTo2D(std::string(), dbContext); + geogCRS->_exportToWKT(formatter); + geogCRS->_exportToWKT(formatter); + formatter->endNode(); + return; + } + io::FormattingException::Throw( + "WKT1 does not support Geographic 3D CRS."); + } + formatter->startNode(isWKT2 + ? ((formatter->use2019Keywords() && isGeographic) + ? io::WKTConstants::GEOGCRS + : io::WKTConstants::GEODCRS) + : isGeocentric() ? io::WKTConstants::GEOCCS + : io::WKTConstants::GEOGCS, + !identifiers().empty()); + + if (formatter->useESRIDialect()) { if (l_name == "WGS 84") { l_name = "GCS_WGS_1984"; } else { bool aliasFound = false; - const auto &dbContext = formatter->databaseContext(); if (dbContext) { auto l_alias = dbContext->getAliasFromOfficialName( l_name, "geodetic_crs", "ESRI"); @@ -1373,11 +1405,6 @@ void GeodeticCRS::_exportToWKT(io::WKTFormatter *formatter) const { } } } - } else if (!isWKT2 && formatter->isStrict() && isGeographic && - axisList.size() != 2 && - oldAxisOutputRule != io::WKTFormatter::OutputAxisRule::NO) { - io::FormattingException::Throw( - "WKT1 does not support Geographic 3D CRS."); } if (!isWKT2 && !formatter->useESRIDialect() && isDeprecated()) { @@ -3163,6 +3190,36 @@ void ProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const { const auto &dbContext = formatter->databaseContext(); auto l_name = nameStr(); + const auto &l_coordinateSystem = d->coordinateSystem(); + const auto &axisList = l_coordinateSystem->axisList(); + if (axisList.size() == 3 && !(isWKT2 && formatter->use2019Keywords())) { + if (!formatter->useESRIDialect() && + CRS::getPrivate()->allowNonConformantWKT1Export_) { + formatter->startNode(io::WKTConstants::COMPD_CS, false); + formatter->addQuotedString(l_name + " + " + baseCRS()->nameStr()); + auto projCRS2D = demoteTo2D(std::string(), dbContext); + if (dbContext) { + const auto res = + projCRS2D->identify(io::AuthorityFactory::create( + NN_NO_CHECK(dbContext), "EPSG")); + if (res.size() == 1) { + const auto &front = res.front(); + if (front.second == 100) { + projCRS2D = front.first; + } + } + } + projCRS2D->_exportToWKT(formatter); + baseCRS() + ->demoteTo2D(std::string(), dbContext) + ->_exportToWKT(formatter); + formatter->endNode(); + return; + } + io::FormattingException::Throw( + "Projected 3D CRS can only be exported since WKT2:2019"); + } + std::string l_alias; if (formatter->useESRIDialect() && dbContext) { l_alias = dbContext->getAliasFromOfficialName(l_name, "projected_crs", @@ -3214,13 +3271,6 @@ void ProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const { } } - const auto &l_coordinateSystem = d->coordinateSystem(); - const auto &axisList = l_coordinateSystem->axisList(); - if (axisList.size() == 3 && !(isWKT2 && formatter->use2019Keywords())) { - io::FormattingException::Throw( - "Projected 3D CRS can only be exported since WKT2:2019"); - } - const auto exportAxis = [&l_coordinateSystem, &axisList, &formatter]() { const auto oldAxisOutputRule = formatter->outputAxis(); if (oldAxisOutputRule == @@ -4091,6 +4141,54 @@ CompoundCRSNNPtr CompoundCRS::create(const util::PropertyMap &properties, // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress + +/** \brief Instantiate a CompoundCRS, a Geographic 3D CRS or a Projected CRS + * from a vector of CRS. + * + * Be a bit "lax", in allowing formulations like EPSG:4326+4326 or + * EPSG:32631+4326 to express Geographic 3D CRS / Projected3D CRS. + * + * @param properties See \ref general_properties. + * At minimum the name should be defined. + * @param components the component CRS of the CompoundCRS. + * @return new CRS. + * @throw InvalidCompoundCRSException + */ +CRSNNPtr CompoundCRS::createLax(const util::PropertyMap &properties, + const std::vector<CRSNNPtr> &components, + const io::DatabaseContextPtr &dbContext) { + + if (components.size() == 2) { + auto comp0 = components[0].get(); + auto comp1 = components[1].get(); + auto comp0Geog = dynamic_cast<const GeographicCRS *>(comp0); + auto comp0Proj = dynamic_cast<const ProjectedCRS *>(comp0); + auto comp1Geog = dynamic_cast<const GeographicCRS *>(comp1); + if ((comp0Geog != nullptr || comp0Proj != nullptr) && + comp1Geog != nullptr) { + const auto horizGeog = + (comp0Proj != nullptr) + ? comp0Proj->baseCRS().as_nullable().get() + : comp0Geog; + if (horizGeog->_isEquivalentTo( + comp1Geog->demoteTo2D(std::string(), dbContext).get())) { + return components[0] + ->promoteTo3D(std::string(), dbContext) + ->allowNonConformantWKT1Export(); + } + throw InvalidCompoundCRSException( + "The 'vertical' geographic CRS is not equivalent to the " + "geographic CRS of the horizontal part"); + } + } + + return create(properties, components); +} +//! @endcond + +// --------------------------------------------------------------------------- + +//! @cond Doxygen_Suppress void CompoundCRS::_exportToWKT(io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; formatter->startNode(isWKT2 ? io::WKTConstants::COMPOUNDCRS |
