diff options
Diffstat (limited to 'src/iso19111/crs.cpp')
| -rw-r--r-- | src/iso19111/crs.cpp | 422 |
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 |
