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.cpp422
1 files changed, 268 insertions, 154 deletions
diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp
index 49cc050f..d71d527e 100644
--- a/src/iso19111/crs.cpp
+++ b/src/iso19111/crs.cpp
@@ -49,6 +49,7 @@
#include <algorithm>
#include <cassert>
+#include <cmath>
#include <cstring>
#include <iostream>
#include <memory>
@@ -390,7 +391,7 @@ CRSNNPtr CRS::createBoundCRSToWGS84IfPossible(
if (boundCRS) {
if (boundCRS->hubCRS()->_isEquivalentTo(
GeographicCRS::EPSG_4326.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT, dbContext)) {
return NN_NO_CHECK(boundCRS);
}
}
@@ -399,16 +400,16 @@ CRSNNPtr CRS::createBoundCRSToWGS84IfPossible(
auto geogCRS = extractGeographicCRS();
auto hubCRS = util::nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4326);
if (geodCRS && !geogCRS) {
- if (geodCRS->_isEquivalentTo(
- GeographicCRS::EPSG_4978.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ if (geodCRS->_isEquivalentTo(GeographicCRS::EPSG_4978.get(),
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
return thisAsCRS;
}
hubCRS = util::nn_static_pointer_cast<CRS>(GeodeticCRS::EPSG_4978);
} else if (!geogCRS ||
geogCRS->_isEquivalentTo(
GeographicCRS::EPSG_4326.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT, dbContext)) {
return thisAsCRS;
} else {
geodCRS = geogCRS;
@@ -595,12 +596,44 @@ CRSNNPtr CRS::alterId(const std::string &authName,
//! @cond Doxygen_Suppress
-static bool isAxisListNorthEast(
+static bool mustAxisOrderBeSwitchedForVisualizationInternal(
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);
+ if (&dir0 == &cs::AxisDirection::NORTH &&
+ &dir1 == &cs::AxisDirection::EAST) {
+ return true;
+ }
+
+ // Address EPSG:32661 "WGS 84 / UPS North (N,E)"
+ if (&dir0 == &cs::AxisDirection::SOUTH &&
+ &dir1 == &cs::AxisDirection::SOUTH) {
+ const auto &meridian0 = axisList[0]->meridian();
+ const auto &meridian1 = axisList[1]->meridian();
+ return meridian0 != nullptr && meridian1 != nullptr &&
+ std::abs(meridian0->longitude().convertToUnit(
+ common::UnitOfMeasure::DEGREE) -
+ 180.0) < 1e-10 &&
+ std::abs(meridian1->longitude().convertToUnit(
+ common::UnitOfMeasure::DEGREE) -
+ 90.0) < 1e-10;
+ }
+
+ // Address EPSG:32761 "WGS 84 / UPS South (N,E)"
+ if (&dir0 == &cs::AxisDirection::NORTH &&
+ &dir1 == &cs::AxisDirection::NORTH) {
+ const auto &meridian0 = axisList[0]->meridian();
+ const auto &meridian1 = axisList[1]->meridian();
+ return meridian0 != nullptr && meridian1 != nullptr &&
+ std::abs(meridian0->longitude().convertToUnit(
+ common::UnitOfMeasure::DEGREE) -
+ 0.0) < 1e-10 &&
+ std::abs(meridian1->longitude().convertToUnit(
+ common::UnitOfMeasure::DEGREE) -
+ 90.0) < 1e-10;
+ }
+
+ return false;
}
// ---------------------------------------------------------------------------
@@ -616,12 +649,14 @@ bool CRS::mustAxisOrderBeSwitchedForVisualization() const {
const GeographicCRS *geogCRS = dynamic_cast<const GeographicCRS *>(this);
if (geogCRS) {
- return isAxisListNorthEast(geogCRS->coordinateSystem()->axisList());
+ return mustAxisOrderBeSwitchedForVisualizationInternal(
+ geogCRS->coordinateSystem()->axisList());
}
const ProjectedCRS *projCRS = dynamic_cast<const ProjectedCRS *>(this);
if (projCRS) {
- return isAxisListNorthEast(projCRS->coordinateSystem()->axisList());
+ return mustAxisOrderBeSwitchedForVisualizationInternal(
+ projCRS->coordinateSystem()->axisList());
}
return false;
@@ -655,7 +690,7 @@ CRSNNPtr CRS::normalizeForVisualization() const {
const GeographicCRS *geogCRS = dynamic_cast<const GeographicCRS *>(this);
if (geogCRS) {
const auto &axisList = geogCRS->coordinateSystem()->axisList();
- if (isAxisListNorthEast(axisList)) {
+ if (mustAxisOrderBeSwitchedForVisualizationInternal(axisList)) {
auto cs = axisList.size() == 2
? cs::EllipsoidalCS::create(util::PropertyMap(),
axisList[1], axisList[0])
@@ -670,7 +705,7 @@ CRSNNPtr CRS::normalizeForVisualization() const {
const ProjectedCRS *projCRS = dynamic_cast<const ProjectedCRS *>(this);
if (projCRS) {
const auto &axisList = projCRS->coordinateSystem()->axisList();
- if (isAxisListNorthEast(axisList)) {
+ if (mustAxisOrderBeSwitchedForVisualizationInternal(axisList)) {
auto cs =
axisList.size() == 2
? cs::CartesianCS::create(util::PropertyMap(), axisList[1],
@@ -695,6 +730,10 @@ CRSNNPtr CRS::normalizeForVisualization() const {
* The candidate CRSs are either hard-coded, or looked in the database when
* authorityFactory is not null.
*
+ * Note that the implementation uses a set of heuristics to have a good
+ * compromise of successful identifications over execution time. It might miss
+ * legitimate matches in some circumstances.
+ *
* The method returns a list of matching reference CRS, and the percentage
* (0-100) of confidence in the match. The list is sorted by decreasing
* confidence.
@@ -987,18 +1026,19 @@ const cs::CoordinateSystemNNPtr &SingleCRS::coordinateSystem() PROJ_PURE_DEFN {
// ---------------------------------------------------------------------------
bool SingleCRS::baseIsEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherSingleCRS = dynamic_cast<const SingleCRS *>(other);
if (otherSingleCRS == nullptr ||
(criterion == util::IComparable::Criterion::STRICT &&
- !ObjectUsage::_isEquivalentTo(other, criterion))) {
+ !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) {
return false;
}
const auto &thisDatum = d->datum;
const auto &otherDatum = otherSingleCRS->d->datum;
if (thisDatum) {
- if (!thisDatum->_isEquivalentTo(otherDatum.get(), criterion)) {
+ if (!thisDatum->_isEquivalentTo(otherDatum.get(), criterion,
+ dbContext)) {
return false;
}
} else {
@@ -1009,7 +1049,8 @@ bool SingleCRS::baseIsEquivalentTo(
// TODO test DatumEnsemble
return d->coordinateSystem->_isEquivalentTo(
- otherSingleCRS->d->coordinateSystem.get(), criterion) &&
+ otherSingleCRS->d->coordinateSystem.get(), criterion,
+ dbContext) &&
getExtensionProj4() == otherSingleCRS->getExtensionProj4();
}
@@ -1544,13 +1585,13 @@ getStandardCriterion(util::IComparable::Criterion criterion) {
//! @cond Doxygen_Suppress
bool GeodeticCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
const auto standardCriterion = getStandardCriterion(criterion);
auto otherGeodCRS = dynamic_cast<const GeodeticCRS *>(other);
// TODO test velocityModel
return otherGeodCRS != nullptr &&
- SingleCRS::baseIsEquivalentTo(other, standardCriterion);
+ SingleCRS::baseIsEquivalentTo(other, standardCriterion, dbContext);
}
//! @endcond
@@ -1611,6 +1652,10 @@ static bool hasCodeCompatibleOfAuthorityFactory(
* The candidate CRSs are either hard-coded, or looked in the database when
* authorityFactory is not null.
*
+ * Note that the implementation uses a set of heuristics to have a good
+ * compromise of successful identifications over execution time. It might miss
+ * legitimate matches in some circumstances.
+ *
* The method returns a list of matching reference CRS, and the percentage
* (0-100) of confidence in the match:
* <ul>
@@ -1651,6 +1696,9 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
std::list<Pair> res;
const auto &thisName(nameStr());
+ io::DatabaseContextPtr dbContext =
+ authorityFactory ? authorityFactory->databaseContext().as_nullable()
+ : nullptr;
const bool l_implicitCS = CRS::getPrivate()->implicitCS_;
const auto crsCriterion =
l_implicitCS
@@ -1664,7 +1712,7 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const bool nameEquivalent = metadata::Identifier::isEquivalentName(
thisName.c_str(), crs->nameStr().c_str());
const bool nameEqual = thisName == crs->nameStr();
- const bool isEq = _isEquivalentTo(crs.get(), crsCriterion);
+ const bool isEq = _isEquivalentTo(crs.get(), crsCriterion, dbContext);
if (nameEquivalent && isEq && (!authorityFactory || nameEqual)) {
res.emplace_back(util::nn_static_pointer_cast<GeodeticCRS>(crs),
nameEqual ? 100 : 90);
@@ -1699,13 +1747,14 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &thisDatum(datum());
auto searchByDatum = [this, &authorityFactory, &res, &thisDatum,
- &geodetic_crs_type, crsCriterion]() {
+ &geodetic_crs_type, crsCriterion, &dbContext]() {
for (const auto &id : thisDatum->identifiers()) {
try {
auto tempRes = authorityFactory->createGeodeticCRSFromDatum(
*id->codeSpace(), id->code(), geodetic_crs_type);
for (const auto &crs : tempRes) {
- if (_isEquivalentTo(crs.get(), crsCriterion)) {
+ if (_isEquivalentTo(crs.get(), crsCriterion,
+ dbContext)) {
res.emplace_back(crs, 70);
}
}
@@ -1717,7 +1766,7 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &thisEllipsoid(ellipsoid());
auto searchByEllipsoid = [this, &authorityFactory, &res, &thisDatum,
&thisEllipsoid, &geodetic_crs_type,
- l_implicitCS]() {
+ l_implicitCS, &dbContext]() {
const auto ellipsoids =
thisEllipsoid->identifiers().empty()
? authorityFactory->createEllipsoidFromExisting(
@@ -1735,15 +1784,17 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
if (crsDatum &&
crsDatum->ellipsoid()->_isEquivalentTo(
ellps.get(),
- util::IComparable::Criterion::EQUIVALENT) &&
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext) &&
crsDatum->primeMeridian()->_isEquivalentTo(
thisDatum->primeMeridian().get(),
- util::IComparable::Criterion::EQUIVALENT) &&
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext) &&
(!l_implicitCS ||
coordinateSystem()->_isEquivalentTo(
crs->coordinateSystem().get(),
- util::IComparable::Criterion::
- EQUIVALENT))) {
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext))) {
res.emplace_back(crs, 60);
}
}
@@ -1776,7 +1827,8 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
authorityFactory->databaseContext(),
*id->codeSpace())
->createGeodeticCRS(id->code());
- bool match = _isEquivalentTo(crs.get(), crsCriterion);
+ bool match =
+ _isEquivalentTo(crs.get(), crsCriterion, dbContext);
res.emplace_back(crs, match ? 100 : 25);
return res;
} catch (const std::exception &) {
@@ -1794,7 +1846,7 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
auto crs = util::nn_dynamic_pointer_cast<GeodeticCRS>(obj);
assert(crs);
auto crsNN = NN_NO_CHECK(crs);
- if (_isEquivalentTo(crs.get(), crsCriterion)) {
+ if (_isEquivalentTo(crs.get(), crsCriterion, dbContext)) {
if (crs->nameStr() == thisName) {
res.clear();
res.emplace_back(crsNN, 100);
@@ -1824,8 +1876,8 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &thisCS(coordinateSystem());
// Sort results
- res.sort([&thisName, &thisDatum, &thisCS](const Pair &a,
- const Pair &b) {
+ res.sort([&thisName, &thisDatum, &thisCS, &dbContext](const Pair &a,
+ const Pair &b) {
// First consider confidence
if (a.second > b.second) {
return true;
@@ -1849,9 +1901,11 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &bDatum(b.first->datum());
if (thisDatum && aDatum && bDatum) {
const auto thisEquivADatum(thisDatum->_isEquivalentTo(
- aDatum.get(), util::IComparable::Criterion::EQUIVALENT));
+ aDatum.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext));
const auto thisEquivBDatum(thisDatum->_isEquivalentTo(
- bDatum.get(), util::IComparable::Criterion::EQUIVALENT));
+ bDatum.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext));
if (thisEquivADatum && !thisEquivBDatum) {
return true;
@@ -1865,9 +1919,11 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &aCS(a.first->coordinateSystem());
const auto &bCS(b.first->coordinateSystem());
const auto thisEquivACs(thisCS->_isEquivalentTo(
- aCS.get(), util::IComparable::Criterion::EQUIVALENT));
+ aCS.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext));
const auto thisEquivBCs(thisCS->_isEquivalentTo(
- bCS.get(), util::IComparable::Criterion::EQUIVALENT));
+ bCS.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext));
if (thisEquivACs && !thisEquivBCs) {
return true;
}
@@ -2084,14 +2140,14 @@ bool GeographicCRS::is2DPartOf3D(util::nn<const GeographicCRS *> other)
//! @cond Doxygen_Suppress
bool GeographicCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherGeogCRS = dynamic_cast<const GeographicCRS *>(other);
if (otherGeogCRS == nullptr) {
return false;
}
const auto standardCriterion = getStandardCriterion(criterion);
- if (GeodeticCRS::_isEquivalentTo(other, standardCriterion)) {
+ if (GeodeticCRS::_isEquivalentTo(other, standardCriterion, dbContext)) {
return true;
}
if (criterion !=
@@ -2110,7 +2166,7 @@ bool GeographicCRS::_isEquivalentTo(
cs::EllipsoidalCS::AxisOrder::LONG_EAST_LAT_NORTH
? cs::EllipsoidalCS::createLatitudeLongitude(unit)
: cs::EllipsoidalCS::createLongitudeLatitude(unit))
- ->GeodeticCRS::_isEquivalentTo(other, standardCriterion);
+ ->GeodeticCRS::_isEquivalentTo(other, standardCriterion, dbContext);
}
return false;
}
@@ -2682,12 +2738,12 @@ VerticalCRS::create(const util::PropertyMap &properties,
//! @cond Doxygen_Suppress
bool VerticalCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherVertCRS = dynamic_cast<const VerticalCRS *>(other);
// TODO test geoidModel and velocityModel
return otherVertCRS != nullptr &&
- SingleCRS::baseIsEquivalentTo(other, criterion);
+ SingleCRS::baseIsEquivalentTo(other, criterion, dbContext);
}
//! @endcond
@@ -2698,6 +2754,10 @@ bool VerticalCRS::_isEquivalentTo(
* The candidate CRSs are looked in the database when
* authorityFactory is not null.
*
+ * Note that the implementation uses a set of heuristics to have a good
+ * compromise of successful identifications over execution time. It might miss
+ * legitimate matches in some circumstances.
+ *
* The method returns a list of matching reference CRS, and the percentage
* (0-100) of confidence in the match.
* 100% means that the name of the reference entry
@@ -2722,6 +2782,8 @@ VerticalCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &thisName(nameStr());
if (authorityFactory) {
+ const io::DatabaseContextNNPtr &dbContext =
+ authorityFactory->databaseContext();
const bool unsignificantName = thisName.empty() ||
ci_equal(thisName, "unknown") ||
@@ -2733,12 +2795,11 @@ VerticalCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
if (hasCodeCompatibleOfAuthorityFactory(id, authorityFactory)) {
try {
auto crs = io::AuthorityFactory::create(
- authorityFactory->databaseContext(),
- *id->codeSpace())
+ dbContext, *id->codeSpace())
->createVerticalCRS(id->code());
bool match = _isEquivalentTo(
- crs.get(),
- util::IComparable::Criterion::EQUIVALENT);
+ crs.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext);
res.emplace_back(crs, match ? 100 : 25);
return res;
} catch (const std::exception &) {
@@ -2756,8 +2817,8 @@ VerticalCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
assert(crs);
auto crsNN = NN_NO_CHECK(crs);
if (_isEquivalentTo(
- crs.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ crs.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
if (crs->nameStr() == thisName) {
res.clear();
res.emplace_back(crsNN, 100);
@@ -2922,19 +2983,20 @@ DerivedCRS::derivingConversionRef() PROJ_PURE_DEFN {
// ---------------------------------------------------------------------------
-bool DerivedCRS::_isEquivalentTo(const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+bool DerivedCRS::_isEquivalentTo(
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDerivedCRS = dynamic_cast<const DerivedCRS *>(other);
const auto standardCriterion = getStandardCriterion(criterion);
if (otherDerivedCRS == nullptr ||
- !SingleCRS::baseIsEquivalentTo(other, standardCriterion)) {
+ !SingleCRS::baseIsEquivalentTo(other, standardCriterion, dbContext)) {
return false;
}
return d->baseCRS_->_isEquivalentTo(otherDerivedCRS->d->baseCRS_.get(),
- criterion) &&
+ criterion, dbContext) &&
d->derivingConversion_->_isEquivalentTo(
- otherDerivedCRS->d->derivingConversion_.get(),
- standardCriterion);
+ otherDerivedCRS->d->derivingConversion_.get(), standardCriterion,
+ dbContext);
}
// ---------------------------------------------------------------------------
@@ -3367,11 +3429,11 @@ ProjectedCRS::create(const util::PropertyMap &properties,
// ---------------------------------------------------------------------------
bool ProjectedCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherProjCRS = dynamic_cast<const ProjectedCRS *>(other);
return otherProjCRS != nullptr &&
- DerivedCRS::_isEquivalentTo(other, criterion);
+ DerivedCRS::_isEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -3394,23 +3456,33 @@ void ProjectedCRS::addUnitConvertAndAxisSwap(io::PROJStringFormatter *formatter,
bool axisSpecFound) const {
const auto &axisList = d->coordinateSystem()->axisList();
const auto &unit = axisList[0]->unit();
+ const auto *zUnit = axisList.size() == 3 ? &(axisList[2]->unit()) : nullptr;
if (!unit._isEquivalentTo(common::UnitOfMeasure::METRE,
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT) ||
+ (zUnit &&
+ !zUnit->_isEquivalentTo(common::UnitOfMeasure::METRE,
+ util::IComparable::Criterion::EQUIVALENT))) {
auto projUnit = unit.exportToPROJString();
const double toSI = unit.conversionToSI();
if (!formatter->getCRSExport()) {
formatter->addStep("unitconvert");
formatter->addParam("xy_in", "m");
- if (!formatter->omitZUnitConversion())
+ if (zUnit)
formatter->addParam("z_in", "m");
+
if (projUnit.empty()) {
formatter->addParam("xy_out", toSI);
- if (!formatter->omitZUnitConversion())
- formatter->addParam("z_out", toSI);
} else {
formatter->addParam("xy_out", projUnit);
- if (!formatter->omitZUnitConversion())
- formatter->addParam("z_out", projUnit);
+ }
+ if (zUnit) {
+ auto projZUnit = zUnit->exportToPROJString();
+ const double zToSI = zUnit->conversionToSI();
+ if (projZUnit.empty()) {
+ formatter->addParam("z_out", zToSI);
+ } else {
+ formatter->addParam("z_out", projZUnit);
+ }
}
} else {
if (projUnit.empty()) {
@@ -3479,6 +3551,10 @@ void ProjectedCRS::addUnitConvertAndAxisSwap(io::PROJStringFormatter *formatter,
* The candidate CRSs are either hard-coded, or looked in the database when
* authorityFactory is not null.
*
+ * Note that the implementation uses a set of heuristics to have a good
+ * compromise of successful identifications over execution time. It might miss
+ * legitimate matches in some circumstances.
+ *
* The method returns a list of matching reference CRS, and the percentage
* (0-100) of confidence in the match. The list is sorted by decreasing
* confidence.
@@ -3545,14 +3621,19 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &conv = derivingConversionRef();
const auto &cs = coordinateSystem();
+ io::DatabaseContextPtr dbContext =
+ authorityFactory ? authorityFactory->databaseContext().as_nullable()
+ : nullptr;
+
if (baseRes.size() == 1 && baseRes.front().second >= 70 &&
conv->isUTM(zone, north) &&
cs->_isEquivalentTo(
cs::CartesianCS::createEastingNorthing(common::UnitOfMeasure::METRE)
- .get())) {
+ .get(),
+ util::IComparable::Criterion::EQUIVALENT, dbContext)) {
if (baseRes.front().first->_isEquivalentTo(
GeographicCRS::EPSG_4326.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT, dbContext)) {
std::string crsName(
computeUTMCRSName("WGS 84 / UTM zone ", zone, north));
res.emplace_back(
@@ -3566,7 +3647,7 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
north &&
baseRes.front().first->_isEquivalentTo(
GeographicCRS::EPSG_4267.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT, dbContext)) {
std::string crsName(
computeUTMCRSName("NAD27 / UTM zone ", zone, north));
res.emplace_back(
@@ -3581,7 +3662,7 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
north &&
baseRes.front().first->_isEquivalentTo(
GeographicCRS::EPSG_4269.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT, dbContext)) {
std::string crsName(
computeUTMCRSName("NAD83 / UTM zone ", zone, north));
res.emplace_back(
@@ -3613,9 +3694,9 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
*id->codeSpace())
->createProjectedCRS(id->code());
bool match = _isEquivalentTo(
- crs.get(),
- util::IComparable::Criterion::
- EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
+ crs.get(), util::IComparable::Criterion::
+ EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS,
+ dbContext);
res.emplace_back(crs, match ? 100 : 25);
return res;
} catch (const std::exception &) {
@@ -3636,9 +3717,9 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
thisName.c_str(), crs->nameStr().c_str());
foundEquivalentName |= eqName;
if (_isEquivalentTo(
- crs.get(),
- util::IComparable::Criterion::
- EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS)) {
+ crs.get(), util::IComparable::Criterion::
+ EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS,
+ dbContext)) {
if (crs->nameStr() == thisName) {
res.clear();
res.emplace_back(crsNN, 100);
@@ -3650,10 +3731,12 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
l_baseCRS->_isEquivalentTo(
crs->baseCRS().get(),
util::IComparable::Criterion::
- EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS) &&
+ EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS,
+ dbContext) &&
derivingConversionRef()->_isEquivalentTo(
crs->derivingConversionRef().get(),
- util::IComparable::Criterion::EQUIVALENT) &&
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext) &&
objects.size() == 1) {
res.clear();
res.emplace_back(crsNN, 100);
@@ -3720,17 +3803,21 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
if (_isEquivalentTo(crs.get(),
util::IComparable::Criterion::
- EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS)) {
+ EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS,
+ dbContext)) {
res.emplace_back(crs, unsignificantName ? 90 : 70);
} else if (ellipsoid->_isEquivalentTo(
crs->baseCRS()->ellipsoid().get(),
- util::IComparable::Criterion::EQUIVALENT) &&
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext) &&
derivingConversionRef()->_isEquivalentTo(
crs->derivingConversionRef().get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
if (coordinateSystem()->_isEquivalentTo(
crs->coordinateSystem().get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
res.emplace_back(crs, 70);
} else {
res.emplace_back(crs, 50);
@@ -3960,12 +4047,12 @@ void CompoundCRS::_exportToPROJString(
// ---------------------------------------------------------------------------
bool CompoundCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherCompoundCRS = dynamic_cast<const CompoundCRS *>(other);
if (otherCompoundCRS == nullptr ||
(criterion == util::IComparable::Criterion::STRICT &&
- !ObjectUsage::_isEquivalentTo(other, criterion))) {
+ !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) {
return false;
}
const auto &components = componentReferenceSystems();
@@ -3974,8 +4061,8 @@ bool CompoundCRS::_isEquivalentTo(
return false;
}
for (size_t i = 0; i < components.size(); i++) {
- if (!components[i]->_isEquivalentTo(otherComponents[i].get(),
- criterion)) {
+ if (!components[i]->_isEquivalentTo(otherComponents[i].get(), criterion,
+ dbContext)) {
return false;
}
}
@@ -3989,6 +4076,10 @@ bool CompoundCRS::_isEquivalentTo(
* The candidate CRSs are looked in the database when
* authorityFactory is not null.
*
+ * Note that the implementation uses a set of heuristics to have a good
+ * compromise of successful identifications over execution time. It might miss
+ * legitimate matches in some circumstances.
+ *
* The method returns a list of matching reference CRS, and the percentage
* (0-100) of confidence in the match. The list is sorted by decreasing
* confidence.
@@ -4015,6 +4106,8 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &thisName(nameStr());
if (authorityFactory) {
+ const io::DatabaseContextNNPtr &dbContext =
+ authorityFactory->databaseContext();
const bool unsignificantName = thisName.empty() ||
ci_equal(thisName, "unknown") ||
@@ -4028,12 +4121,11 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
if (hasCodeCompatibleOfAuthorityFactory(id, authorityFactory)) {
try {
auto crs = io::AuthorityFactory::create(
- authorityFactory->databaseContext(),
- *id->codeSpace())
+ dbContext, *id->codeSpace())
->createCompoundCRS(id->code());
bool match = _isEquivalentTo(
- crs.get(),
- util::IComparable::Criterion::EQUIVALENT);
+ crs.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext);
res.emplace_back(crs, match ? 100 : 25);
return res;
} catch (const std::exception &) {
@@ -4054,8 +4146,8 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
thisName.c_str(), crs->nameStr().c_str());
foundEquivalentName |= eqName;
if (_isEquivalentTo(
- crs.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ crs.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
if (crs->nameStr() == thisName) {
res.clear();
res.emplace_back(crsNN, 100);
@@ -4122,7 +4214,8 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
}
if (_isEquivalentTo(crs.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
res.emplace_back(crs, unsignificantName ? 90 : 70);
} else {
res.emplace_back(crs, 25);
@@ -4494,20 +4587,22 @@ void BoundCRS::_exportToPROJString(
// ---------------------------------------------------------------------------
bool BoundCRS::_isEquivalentTo(const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherBoundCRS = dynamic_cast<const BoundCRS *>(other);
if (otherBoundCRS == nullptr ||
(criterion == util::IComparable::Criterion::STRICT &&
- !ObjectUsage::_isEquivalentTo(other, criterion))) {
+ !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) {
return false;
}
const auto standardCriterion = getStandardCriterion(criterion);
return d->baseCRS_->_isEquivalentTo(otherBoundCRS->d->baseCRS_.get(),
- criterion) &&
+ criterion, dbContext) &&
d->hubCRS_->_isEquivalentTo(otherBoundCRS->d->hubCRS_.get(),
- criterion) &&
+ criterion, dbContext) &&
d->transformation_->_isEquivalentTo(
- otherBoundCRS->d->transformation_.get(), standardCriterion);
+ otherBoundCRS->d->transformation_.get(), standardCriterion,
+ dbContext);
}
// ---------------------------------------------------------------------------
@@ -4518,10 +4613,36 @@ std::list<std::pair<CRSNNPtr, int>>
BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const {
typedef std::pair<CRSNNPtr, int> Pair;
std::list<Pair> res;
- if (authorityFactory &&
- d->hubCRS_->_isEquivalentTo(GeographicCRS::EPSG_4326.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ if (!authorityFactory)
+ return res;
+ std::list<Pair> resMatchOfTransfToWGS84;
+ const io::DatabaseContextNNPtr &dbContext =
+ authorityFactory->databaseContext();
+ if (d->hubCRS_->_isEquivalentTo(GeographicCRS::EPSG_4326.get(),
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
auto resTemp = d->baseCRS_->identify(authorityFactory);
+
+ std::string refTransfPROJString;
+ bool refTransfPROJStringValid = false;
+ auto refTransf = d->transformation_->normalizeForVisualization();
+ try {
+ refTransfPROJString = refTransf->exportToPROJString(
+ io::PROJStringFormatter::create().get());
+ refTransfPROJString = replaceAll(
+ refTransfPROJString,
+ " +rx=0 +ry=0 +rz=0 +s=0 +convention=position_vector", "");
+ refTransfPROJStringValid = true;
+ } catch (const std::exception &) {
+ }
+ bool refIsNullTransform = false;
+ if (isTOWGS84Compatible()) {
+ auto params = transformation()->getTOWGS84Parameters();
+ if (params == std::vector<double>{0, 0, 0, 0, 0, 0, 0}) {
+ refIsNullTransform = true;
+ }
+ }
+
for (const auto &pair : resTemp) {
const auto &candidateBaseCRS = pair.first;
auto projCRS =
@@ -4532,54 +4653,47 @@ BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const {
if (geodCRS) {
auto context = operation::CoordinateOperationContext::create(
authorityFactory, nullptr, 0.0);
+ context->setSpatialCriterion(
+ operation::CoordinateOperationContext::SpatialCriterion::
+ PARTIAL_INTERSECTION);
auto ops =
operation::CoordinateOperationFactory::create()
->createOperations(NN_NO_CHECK(geodCRS),
GeographicCRS::EPSG_4326, context);
- std::string refTransfPROJString;
- bool refTransfPROJStringValid = false;
- try {
- refTransfPROJString =
- d->transformation_->exportToPROJString(
- io::PROJStringFormatter::create().get());
- refTransfPROJStringValid = true;
- if (refTransfPROJString == "+proj=axisswap +order=2,1") {
- refTransfPROJString.clear();
- }
- } catch (const std::exception &) {
- }
+
bool foundOp = false;
for (const auto &op : ops) {
+ auto opNormalized = op->normalizeForVisualization();
std::string opTransfPROJString;
bool opTransfPROJStringValid = false;
if (op->nameStr().find("Ballpark geographic") == 0) {
- if (isTOWGS84Compatible()) {
- auto params =
- transformation()->getTOWGS84Parameters();
- if (params ==
- std::vector<double>{0, 0, 0, 0, 0, 0, 0}) {
- res.emplace_back(create(candidateBaseCRS,
- d->hubCRS_,
- transformation()),
- pair.second);
- foundOp = true;
- break;
- }
+ if (refIsNullTransform) {
+ res.emplace_back(create(candidateBaseCRS,
+ d->hubCRS_,
+ transformation()),
+ pair.second);
+ foundOp = true;
+ break;
}
continue;
}
try {
- opTransfPROJString = op->exportToPROJString(
+ opTransfPROJString = opNormalized->exportToPROJString(
io::PROJStringFormatter::create().get());
opTransfPROJStringValid = true;
+ opTransfPROJString = replaceAll(
+ opTransfPROJString, " +rx=0 +ry=0 +rz=0 +s=0 "
+ "+convention=position_vector",
+ "");
} catch (const std::exception &) {
}
if ((refTransfPROJStringValid && opTransfPROJStringValid &&
refTransfPROJString == opTransfPROJString) ||
- op->_isEquivalentTo(
- d->transformation_.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
- res.emplace_back(
+ opNormalized->_isEquivalentTo(
+ refTransf.get(),
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
+ resMatchOfTransfToWGS84.emplace_back(
create(candidateBaseCRS, d->hubCRS_,
NN_NO_CHECK(util::nn_dynamic_pointer_cast<
operation::Transformation>(op))),
@@ -4596,7 +4710,7 @@ BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const {
}
}
}
- return res;
+ return !resMatchOfTransfToWGS84.empty() ? resMatchOfTransfToWGS84 : res;
}
// ---------------------------------------------------------------------------
@@ -4758,11 +4872,11 @@ void DerivedGeodeticCRS::_exportToPROJString(
// ---------------------------------------------------------------------------
bool DerivedGeodeticCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDerivedCRS = dynamic_cast<const DerivedGeodeticCRS *>(other);
return otherDerivedCRS != nullptr &&
- DerivedCRS::_isEquivalentTo(other, criterion);
+ DerivedCRS::_isEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -4908,11 +5022,11 @@ void DerivedGeographicCRS::_exportToPROJString(
// ---------------------------------------------------------------------------
bool DerivedGeographicCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDerivedCRS = dynamic_cast<const DerivedGeographicCRS *>(other);
return otherDerivedCRS != nullptr &&
- DerivedCRS::_isEquivalentTo(other, criterion);
+ DerivedCRS::_isEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -5055,11 +5169,11 @@ void DerivedProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const {
// ---------------------------------------------------------------------------
bool DerivedProjectedCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDerivedCRS = dynamic_cast<const DerivedProjectedCRS *>(other);
return otherDerivedCRS != nullptr &&
- DerivedCRS::_isEquivalentTo(other, criterion);
+ DerivedCRS::_isEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -5185,11 +5299,11 @@ void TemporalCRS::_exportToJSON(
// ---------------------------------------------------------------------------
bool TemporalCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherTemporalCRS = dynamic_cast<const TemporalCRS *>(other);
return otherTemporalCRS != nullptr &&
- SingleCRS::baseIsEquivalentTo(other, criterion);
+ SingleCRS::baseIsEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -5323,11 +5437,11 @@ void EngineeringCRS::_exportToJSON(
// ---------------------------------------------------------------------------
bool EngineeringCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherEngineeringCRS = dynamic_cast<const EngineeringCRS *>(other);
return otherEngineeringCRS != nullptr &&
- SingleCRS::baseIsEquivalentTo(other, criterion);
+ SingleCRS::baseIsEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -5455,11 +5569,11 @@ void ParametricCRS::_exportToJSON(
// ---------------------------------------------------------------------------
bool ParametricCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherParametricCRS = dynamic_cast<const ParametricCRS *>(other);
return otherParametricCRS != nullptr &&
- SingleCRS::baseIsEquivalentTo(other, criterion);
+ SingleCRS::baseIsEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -5560,11 +5674,11 @@ void DerivedVerticalCRS::_exportToPROJString(
// ---------------------------------------------------------------------------
bool DerivedVerticalCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDerivedCRS = dynamic_cast<const DerivedVerticalCRS *>(other);
return otherDerivedCRS != nullptr &&
- DerivedCRS::_isEquivalentTo(other, criterion);
+ DerivedCRS::_isEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -5681,11 +5795,11 @@ void DerivedCRSTemplate<DerivedCRSTraits>::_exportToWKT(
template <class DerivedCRSTraits>
bool DerivedCRSTemplate<DerivedCRSTraits>::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDerivedCRS = dynamic_cast<const DerivedCRSTemplate *>(other);
return otherDerivedCRS != nullptr &&
- DerivedCRS::_isEquivalentTo(other, criterion);
+ DerivedCRS::_isEquivalentTo(other, criterion, dbContext);
}
//! @endcond