aboutsummaryrefslogtreecommitdiff
path: root/src/iso19111/crs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/iso19111/crs.cpp')
-rw-r--r--src/iso19111/crs.cpp266
1 files changed, 205 insertions, 61 deletions
diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp
index 01a588e3..476bc72b 100644
--- a/src/iso19111/crs.cpp
+++ b/src/iso19111/crs.cpp
@@ -1,7 +1,7 @@
/******************************************************************************
*
* Project: PROJ
- * Purpose: ISO19111:2018 implementation
+ * Purpose: ISO19111:2019 implementation
* Author: Even Rouault <even dot rouault at spatialys dot com>
*
******************************************************************************
@@ -453,7 +453,7 @@ CRSNNPtr CRS::createBoundCRSToWGS84IfPossible(
auto transf =
util::nn_dynamic_pointer_cast<operation::Transformation>(
op);
- if (transf && !starts_with(transf->nameStr(), "Null geo")) {
+ if (transf && !starts_with(transf->nameStr(), "Ballpark geo")) {
try {
transf->getTOWGS84Parameters();
} catch (const std::exception &) {
@@ -486,7 +486,7 @@ CRSNNPtr CRS::createBoundCRSToWGS84IfPossible(
operation::Transformation>(subops[1]);
if (transf &&
!starts_with(transf->nameStr(),
- "Null geo")) {
+ "Ballpark geo")) {
try {
transf->getTOWGS84Parameters();
} catch (const std::exception &) {
@@ -591,6 +591,104 @@ CRSNNPtr CRS::alterId(const std::string &authName,
// ---------------------------------------------------------------------------
+//! @cond Doxygen_Suppress
+
+static bool isAxisListNorthEast(
+ const std::vector<cs::CoordinateSystemAxisNNPtr> &axisList) {
+ const auto &dir0 = axisList[0]->direction();
+ const auto &dir1 = axisList[1]->direction();
+ return (&dir0 == &cs::AxisDirection::NORTH &&
+ &dir1 == &cs::AxisDirection::EAST);
+}
+// ---------------------------------------------------------------------------
+
+bool CRS::mustAxisOrderBeSwitchedForVisualization() const {
+
+ const CompoundCRS *compoundCRS = dynamic_cast<const CompoundCRS *>(this);
+ if (compoundCRS) {
+ const auto &comps = compoundCRS->componentReferenceSystems();
+ if (!comps.empty()) {
+ return comps[0]->mustAxisOrderBeSwitchedForVisualization();
+ }
+ }
+
+ const GeographicCRS *geogCRS = dynamic_cast<const GeographicCRS *>(this);
+ if (geogCRS) {
+ return isAxisListNorthEast(geogCRS->coordinateSystem()->axisList());
+ }
+
+ const ProjectedCRS *projCRS = dynamic_cast<const ProjectedCRS *>(this);
+ if (projCRS) {
+ return isAxisListNorthEast(projCRS->coordinateSystem()->axisList());
+ }
+
+ return false;
+}
+
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+
+CRSNNPtr CRS::normalizeForVisualization() const {
+ auto props = util::PropertyMap().set(
+ common::IdentifiedObject::NAME_KEY,
+ nameStr() + " (with axis order normalized for visualization)");
+
+ const CompoundCRS *compoundCRS = dynamic_cast<const CompoundCRS *>(this);
+ if (compoundCRS) {
+ const auto &comps = compoundCRS->componentReferenceSystems();
+ if (!comps.empty()) {
+ std::vector<CRSNNPtr> newComps;
+ newComps.emplace_back(comps[0]->normalizeForVisualization());
+ for (size_t i = 1; i < comps.size(); i++) {
+ newComps.emplace_back(comps[i]);
+ }
+ return util::nn_static_pointer_cast<CRS>(
+ CompoundCRS::create(props, newComps));
+ }
+ }
+
+ const GeographicCRS *geogCRS = dynamic_cast<const GeographicCRS *>(this);
+ if (geogCRS) {
+ const auto &axisList = geogCRS->coordinateSystem()->axisList();
+ if (isAxisListNorthEast(axisList)) {
+ auto cs = axisList.size() == 2
+ ? cs::EllipsoidalCS::create(util::PropertyMap(),
+ axisList[1], axisList[0])
+ : cs::EllipsoidalCS::create(util::PropertyMap(),
+ axisList[1], axisList[0],
+ axisList[2]);
+ return util::nn_static_pointer_cast<CRS>(GeographicCRS::create(
+ props, geogCRS->datum(), geogCRS->datumEnsemble(), cs));
+ }
+ }
+
+ const ProjectedCRS *projCRS = dynamic_cast<const ProjectedCRS *>(this);
+ if (projCRS) {
+ const auto &axisList = projCRS->coordinateSystem()->axisList();
+ if (isAxisListNorthEast(axisList)) {
+ auto cs =
+ axisList.size() == 2
+ ? cs::CartesianCS::create(util::PropertyMap(), axisList[1],
+ axisList[0])
+ : cs::CartesianCS::create(util::PropertyMap(), axisList[1],
+ axisList[0], axisList[2]);
+ return util::nn_static_pointer_cast<CRS>(
+ ProjectedCRS::create(props, projCRS->baseCRS(),
+ projCRS->derivingConversionRef(), cs));
+ }
+ }
+
+ return NN_NO_CHECK(
+ std::static_pointer_cast<CRS>(shared_from_this().as_nullable()));
+}
+
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
/** \brief Identify the CRS with reference CRSs.
*
* The candidate CRSs are either hard-coded, or looked in the database when
@@ -1289,6 +1387,36 @@ GeodeticCRSNNPtr GeodeticCRS::createEPSG_4978() {
// ---------------------------------------------------------------------------
+//! @cond Doxygen_Suppress
+
+static bool hasCodeCompatibleOfAuthorityFactory(
+ const common::IdentifiedObject *obj,
+ const io::AuthorityFactoryPtr &authorityFactory) {
+ const auto &ids = obj->identifiers();
+ if (!ids.empty() && authorityFactory->getAuthority().empty()) {
+ return true;
+ }
+ for (const auto &id : ids) {
+ if (*(id->codeSpace()) == authorityFactory->getAuthority()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool hasCodeCompatibleOfAuthorityFactory(
+ const metadata::IdentifierNNPtr &id,
+ const io::AuthorityFactoryPtr &authorityFactory) {
+ if (authorityFactory->getAuthority().empty()) {
+ return true;
+ }
+ return *(id->codeSpace()) == authorityFactory->getAuthority();
+}
+
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
/** \brief Identify the CRS with reference CRSs.
*
* The candidate CRSs are either hard-coded, or looked in the database when
@@ -1432,19 +1560,22 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
searchByEllipsoid();
}
}
- } else if (!identifiers().empty()) {
+ } else if (hasCodeCompatibleOfAuthorityFactory(this,
+ authorityFactory)) {
// If the CRS has already an id, check in the database for the
// official object, and verify that they are equivalent.
for (const auto &id : identifiers()) {
- try {
- auto crs = io::AuthorityFactory::create(
- authorityFactory->databaseContext(),
- *id->codeSpace())
- ->createGeodeticCRS(id->code());
- bool match = _isEquivalentTo(crs.get(), crsCriterion);
- res.emplace_back(crs, match ? 100 : 25);
- return res;
- } catch (const std::exception &) {
+ if (hasCodeCompatibleOfAuthorityFactory(id, authorityFactory)) {
+ try {
+ auto crs = io::AuthorityFactory::create(
+ authorityFactory->databaseContext(),
+ *id->codeSpace())
+ ->createGeodeticCRS(id->code());
+ bool match = _isEquivalentTo(crs.get(), crsCriterion);
+ res.emplace_back(crs, match ? 100 : 25);
+ return res;
+ } catch (const std::exception &) {
+ }
}
}
} else {
@@ -1892,9 +2023,7 @@ void GeographicCRS::addAngularUnitConvertAndAxisSwap(
if (order[0] && order[1] && (order[0] != one || order[1] != two)) {
formatter->addStep("axisswap");
char orderStr[10];
- strcpy(orderStr, order[0]);
- strcat(orderStr, ",");
- strcat(orderStr, order[1]);
+ sprintf(orderStr, "%.2s,%.2s", order[0], order[1]);
formatter->addParam("order", orderStr);
}
}
@@ -2096,7 +2225,6 @@ void VerticalCRS::addLinearUnitConvert(
auto &axisList = coordinateSystem()->axisList();
if (!axisList.empty()) {
- auto projUnit = axisList[0]->unit().exportToPROJString();
if (axisList[0]->unit().conversionToSI() != 1.0) {
formatter->addStep("unitconvert");
formatter->addParam("z_in", "m");
@@ -2204,20 +2332,23 @@ VerticalCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const bool unsignificantName = thisName.empty() ||
ci_equal(thisName, "unknown") ||
ci_equal(thisName, "unnamed");
- if (!identifiers().empty()) {
+ if (hasCodeCompatibleOfAuthorityFactory(this, authorityFactory)) {
// If the CRS has already an id, check in the database for the
// official object, and verify that they are equivalent.
for (const auto &id : identifiers()) {
- try {
- auto crs = io::AuthorityFactory::create(
- authorityFactory->databaseContext(),
- *id->codeSpace())
- ->createVerticalCRS(id->code());
- bool match = _isEquivalentTo(
- crs.get(), util::IComparable::Criterion::EQUIVALENT);
- res.emplace_back(crs, match ? 100 : 25);
- return res;
- } catch (const std::exception &) {
+ if (hasCodeCompatibleOfAuthorityFactory(id, authorityFactory)) {
+ try {
+ auto crs = io::AuthorityFactory::create(
+ authorityFactory->databaseContext(),
+ *id->codeSpace())
+ ->createVerticalCRS(id->code());
+ bool match = _isEquivalentTo(
+ crs.get(),
+ util::IComparable::Criterion::EQUIVALENT);
+ res.emplace_back(crs, match ? 100 : 25);
+ return res;
+ } catch (const std::exception &) {
+ }
}
}
} else if (!unsignificantName) {
@@ -2422,16 +2553,21 @@ void DerivedCRS::setDerivingConversionCRS() {
// ---------------------------------------------------------------------------
-void DerivedCRS::baseExportToWKT(io::WKTFormatter *&formatter,
+void DerivedCRS::baseExportToWKT(io::WKTFormatter *formatter,
const std::string &keyword,
const std::string &baseKeyword) const {
formatter->startNode(keyword, !identifiers().empty());
formatter->addQuotedString(nameStr());
const auto &l_baseCRS = d->baseCRS_;
- formatter->startNode(baseKeyword, !l_baseCRS->identifiers().empty());
+ formatter->startNode(baseKeyword, formatter->use2018Keywords() &&
+ !l_baseCRS->identifiers().empty());
formatter->addQuotedString(l_baseCRS->nameStr());
l_baseCRS->exportDatumOrDatumEnsembleToWkt(formatter);
+ if (formatter->use2018Keywords() &&
+ !(formatter->idOnTopLevelOnly() && formatter->topLevelHasId())) {
+ l_baseCRS->formatID(formatter);
+ }
formatter->endNode();
formatter->setUseDerivingConversion(true);
@@ -2659,7 +2795,7 @@ void ProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const {
dynamic_cast<const GeographicCRS *>(l_baseCRS.get()))
? io::WKTConstants::BASEGEOGCRS
: io::WKTConstants::BASEGEODCRS,
- !l_baseCRS->identifiers().empty());
+ formatter->use2018Keywords() && !l_baseCRS->identifiers().empty());
formatter->addQuotedString(l_baseCRS->nameStr());
l_baseCRS->exportDatumOrDatumEnsembleToWkt(formatter);
// insert ellipsoidal cs unit when the units of the map
@@ -2670,6 +2806,10 @@ void ProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const {
geodeticCRSAxisList[0]->unit()._exportToWKT(formatter);
}
l_baseCRS->primeMeridian()->_exportToWKT(formatter);
+ if (formatter->use2018Keywords() &&
+ !(formatter->idOnTopLevelOnly() && formatter->topLevelHasId())) {
+ l_baseCRS->formatID(formatter);
+ }
formatter->endNode();
} else {
const auto oldAxisOutputRule = formatter->outputAxis();
@@ -2843,9 +2983,7 @@ void ProjectedCRS::addUnitConvertAndAxisSwap(io::PROJStringFormatter *formatter,
if (order[0] && order[1]) {
formatter->addStep("axisswap");
char orderStr[10];
- strcpy(orderStr, order[0]);
- strcat(orderStr, ",");
- strcat(orderStr, order[1]);
+ sprintf(orderStr, "%.2s,%.2s", order[0], order[1]);
formatter->addParam("order", orderStr);
}
} else {
@@ -2998,21 +3136,24 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
ci_equal(thisName, "unnamed");
bool foundEquivalentName = false;
- if (!identifiers().empty()) {
+ if (hasCodeCompatibleOfAuthorityFactory(this, authorityFactory)) {
// If the CRS has already an id, check in the database for the
// official object, and verify that they are equivalent.
for (const auto &id : identifiers()) {
- try {
- auto crs = io::AuthorityFactory::create(
- authorityFactory->databaseContext(),
- *id->codeSpace())
- ->createProjectedCRS(id->code());
- bool match = _isEquivalentTo(
- crs.get(), util::IComparable::Criterion::
- EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
- res.emplace_back(crs, match ? 100 : 25);
- return res;
- } catch (const std::exception &) {
+ if (hasCodeCompatibleOfAuthorityFactory(id, authorityFactory)) {
+ try {
+ auto crs = io::AuthorityFactory::create(
+ authorityFactory->databaseContext(),
+ *id->codeSpace())
+ ->createProjectedCRS(id->code());
+ bool match = _isEquivalentTo(
+ crs.get(),
+ util::IComparable::Criterion::
+ EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
+ res.emplace_back(crs, match ? 100 : 25);
+ return res;
+ } catch (const std::exception &) {
+ }
}
}
} else if (!unsignificantName) {
@@ -3087,8 +3228,8 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
// Sort results
res.sort(lambdaSort);
- if (identifiers().empty() && !foundEquivalentName &&
- (res.empty() || res.front().second < 50)) {
+ if (!hasCodeCompatibleOfAuthorityFactory(this, authorityFactory) &&
+ !foundEquivalentName && (res.empty() || res.front().second < 50)) {
std::set<std::pair<std::string, std::string>> alreadyKnown;
for (const auto &pair : res) {
const auto &ids = pair.first->identifiers();
@@ -3346,20 +3487,23 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
ci_equal(thisName, "unnamed");
bool foundEquivalentName = false;
- if (!identifiers().empty()) {
+ if (hasCodeCompatibleOfAuthorityFactory(this, authorityFactory)) {
// If the CRS has already an id, check in the database for the
// official object, and verify that they are equivalent.
for (const auto &id : identifiers()) {
- try {
- auto crs = io::AuthorityFactory::create(
- authorityFactory->databaseContext(),
- *id->codeSpace())
- ->createCompoundCRS(id->code());
- bool match = _isEquivalentTo(
- crs.get(), util::IComparable::Criterion::EQUIVALENT);
- res.emplace_back(crs, match ? 100 : 25);
- return res;
- } catch (const std::exception &) {
+ if (hasCodeCompatibleOfAuthorityFactory(id, authorityFactory)) {
+ try {
+ auto crs = io::AuthorityFactory::create(
+ authorityFactory->databaseContext(),
+ *id->codeSpace())
+ ->createCompoundCRS(id->code());
+ bool match = _isEquivalentTo(
+ crs.get(),
+ util::IComparable::Criterion::EQUIVALENT);
+ res.emplace_back(crs, match ? 100 : 25);
+ return res;
+ } catch (const std::exception &) {
+ }
}
}
} else if (!unsignificantName) {
@@ -3855,7 +3999,7 @@ BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const {
for (const auto &op : ops) {
std::string opTransfPROJString;
bool opTransfPROJStringValid = false;
- if (op->nameStr().find("Null geographic") == 0) {
+ if (op->nameStr().find("Ballpark geographic") == 0) {
if (isTOWGS84Compatible()) {
auto params =
transformation()->getTOWGS84Parameters();
@@ -4849,7 +4993,7 @@ DerivedCRSTemplate<DerivedCRSTraits>::create(
// ---------------------------------------------------------------------------
-static void DerivedCRSTemplateCheckExportToWKT(io::WKTFormatter *&formatter,
+static void DerivedCRSTemplateCheckExportToWKT(io::WKTFormatter *formatter,
const std::string &crsName,
bool wkt2_2018_only) {
const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2;