diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2020-05-25 19:47:43 +0200 |
|---|---|---|
| committer | Even Rouault <even.rouault@spatialys.com> | 2020-05-25 19:47:51 +0200 |
| commit | 172744361c83d2a40c11600a398a0e1668052031 (patch) | |
| tree | 84a034e942372ada367eb832b48061ee6d11e11b | |
| parent | cfdd474dd8aef197297c488c6133f7484fe421fd (diff) | |
| download | PROJ-172744361c83d2a40c11600a398a0e1668052031.tar.gz PROJ-172744361c83d2a40c11600a398a0e1668052031.zip | |
Fix identification of (one of the) ESRI WKT formulations of EPSG:3035
Fixes https://github.com/qgis/QGIS/issues/36111
| -rw-r--r-- | include/proj/io.hpp | 8 | ||||
| -rw-r--r-- | src/iso19111/crs.cpp | 9 | ||||
| -rw-r--r-- | src/iso19111/factory.cpp | 116 | ||||
| -rw-r--r-- | test/unit/test_crs.cpp | 26 |
4 files changed, 116 insertions, 43 deletions
diff --git a/include/proj/io.hpp b/include/proj/io.hpp index 1620c1fe..d1f79aab 100644 --- a/include/proj/io.hpp +++ b/include/proj/io.hpp @@ -1172,6 +1172,14 @@ class PROJ_GCC_DLL AuthorityFactory { const metadata::ExtentPtr &intersectingExtent1, const metadata::ExtentPtr &intersectingExtent2) const; + typedef std::pair<common::IdentifiedObjectNNPtr, std::string> + PairObjectName; + PROJ_INTERNAL std::list<PairObjectName> + createObjectsFromNameEx(const std::string &name, + const std::vector<ObjectType> &allowedObjectTypes = + std::vector<ObjectType>(), + bool approximateMatch = true, + size_t limitResultCount = 0) const; //! @endcond protected: diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index 35e11d84..b0ec35b9 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -3927,15 +3927,16 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { } else if (!unsignificantName) { for (int ipass = 0; ipass < 2; ipass++) { const bool approximateMatch = ipass == 1; - auto objects = authorityFactory->createObjectsFromName( + auto objects = authorityFactory->createObjectsFromNameEx( thisName, {io::AuthorityFactory::ObjectType::PROJECTED_CRS}, approximateMatch); - for (const auto &obj : objects) { - auto crs = util::nn_dynamic_pointer_cast<ProjectedCRS>(obj); + for (const auto &pairObjName : objects) { + auto crs = util::nn_dynamic_pointer_cast<ProjectedCRS>( + pairObjName.first); assert(crs); auto crsNN = NN_NO_CHECK(crs); const bool eqName = metadata::Identifier::isEquivalentName( - thisName.c_str(), crs->nameStr().c_str()); + thisName.c_str(), pairObjName.second.c_str()); foundEquivalentName |= eqName; addCRS(crsNN, eqName); diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 8fa5a24d..de8e1362 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -56,6 +56,7 @@ #include <map> #include <memory> #include <sstream> // std::ostringstream +#include <stdexcept> #include <string> #include "proj_constants.h" @@ -5452,7 +5453,7 @@ std::string AuthorityFactory::getOfficialNameFromAlias( // --------------------------------------------------------------------------- -/** \brief Return a list of objects by their name +/** \brief Return a list of objects, identified by their name * * @param searchedName Searched name. Must be at least 2 character long. * @param allowedObjectTypes List of object types into which to search. If @@ -5468,7 +5469,39 @@ AuthorityFactory::createObjectsFromName( const std::string &searchedName, const std::vector<ObjectType> &allowedObjectTypes, bool approximateMatch, size_t limitResultCount) const { + std::list<common::IdentifiedObjectNNPtr> res; + const auto resTmp(createObjectsFromNameEx( + searchedName, allowedObjectTypes, approximateMatch, limitResultCount)); + for (const auto &pair : resTmp) { + res.emplace_back(pair.first); + } + return res; +} + +// --------------------------------------------------------------------------- + +//! @cond Doxygen_Suppress +/** \brief Return a list of objects, identifier by their name, with the name + * on which the match occured. + * + * The name on which the match occured might be different from the object name, + * if the match has been done on an alias name of that object. + * + * @param searchedName Searched name. Must be at least 2 character long. + * @param allowedObjectTypes List of object types into which to search. If + * empty, all object types will be searched. + * @param approximateMatch Whether approximate name identification is allowed. + * @param limitResultCount Maximum number of results to return. + * Or 0 for unlimited. + * @return list of matched objects. + * @throw FactoryException + */ +std::list<AuthorityFactory::PairObjectName> +AuthorityFactory::createObjectsFromNameEx( + const std::string &searchedName, + const std::vector<ObjectType> &allowedObjectTypes, bool approximateMatch, + size_t limitResultCount) const { std::string searchedNameWithoutDeprecated(searchedName); bool deprecated = false; if (ends_with(searchedNameWithoutDeprecated, " (deprecated)")) { @@ -5657,7 +5690,7 @@ AuthorityFactory::createObjectsFromName( sql += toString(static_cast<int>(limitResultCount)); } - std::list<common::IdentifiedObjectNNPtr> res; + std::list<PairObjectName> res; std::set<std::pair<std::string, std::string>> setIdentified; // Querying geodetic datum is a super hot path when importing from WKT1 @@ -5693,7 +5726,9 @@ AuthorityFactory::createObjectsFromName( } setIdentified.insert(key); auto factory = d->createFactory(auth_name); - res.emplace_back(factory->createGeodeticDatum(code)); + const auto &name = row[3]; + res.emplace_back( + PairObjectName(factory->createGeodeticDatum(code), name)); if (limitResultCount > 0 && res.size() == limitResultCount) { break; } @@ -5724,7 +5759,8 @@ AuthorityFactory::createObjectsFromName( } setIdentified.insert(key); auto factory = d->createFactory(auth_name); - res.emplace_back(factory->createGeodeticDatum(code)); + res.emplace_back(PairObjectName( + factory->createGeodeticDatum(code), name)); if (limitResultCount > 0 && res.size() == limitResultCount) { break; @@ -5773,43 +5809,45 @@ AuthorityFactory::createObjectsFromName( break; } auto factory = d->createFactory(auth_name); - if (table_name == "prime_meridian") { - res.emplace_back(factory->createPrimeMeridian(code)); - } else if (table_name == "ellipsoid") { - res.emplace_back(factory->createEllipsoid(code)); - } else if (table_name == "geodetic_datum") { - res.emplace_back(factory->createGeodeticDatum(code)); - } else if (table_name == "vertical_datum") { - res.emplace_back(factory->createVerticalDatum(code)); - } else if (table_name == "geodetic_crs") { - res.emplace_back(factory->createGeodeticCRS(code)); - } else if (table_name == "projected_crs") { - res.emplace_back(factory->createProjectedCRS(code)); - } else if (table_name == "vertical_crs") { - res.emplace_back(factory->createVerticalCRS(code)); - } else if (table_name == "compound_crs") { - res.emplace_back(factory->createCompoundCRS(code)); - } else if (table_name == "conversion") { - res.emplace_back(factory->createConversion(code)); - } else if (table_name == "grid_transformation" || - table_name == "helmert_transformation" || - table_name == "other_transformation" || - table_name == "concatenated_operation") { - res.emplace_back( - factory->createCoordinateOperation(code, true)); - } else { - assert(false); - } + auto getObject = [&factory]( + const std::string &l_table_name, + const std::string &l_code) -> common::IdentifiedObjectNNPtr { + if (l_table_name == "prime_meridian") { + return factory->createPrimeMeridian(l_code); + } else if (l_table_name == "ellipsoid") { + return factory->createEllipsoid(l_code); + } else if (l_table_name == "geodetic_datum") { + return factory->createGeodeticDatum(l_code); + } else if (l_table_name == "vertical_datum") { + return factory->createVerticalDatum(l_code); + } else if (l_table_name == "geodetic_crs") { + return factory->createGeodeticCRS(l_code); + } else if (l_table_name == "projected_crs") { + return factory->createProjectedCRS(l_code); + } else if (l_table_name == "vertical_crs") { + return factory->createVerticalCRS(l_code); + } else if (l_table_name == "compound_crs") { + return factory->createCompoundCRS(l_code); + } else if (l_table_name == "conversion") { + return factory->createConversion(l_code); + } else if (l_table_name == "grid_transformation" || + l_table_name == "helmert_transformation" || + l_table_name == "other_transformation" || + l_table_name == "concatenated_operation") { + return factory->createCoordinateOperation(l_code, true); + } + throw std::runtime_error("Unsupported table_name"); + }; + res.emplace_back(PairObjectName(getObject(table_name, code), name)); if (limitResultCount > 0 && res.size() == limitResultCount) { break; } } } - auto sortLambda = [](const common::IdentifiedObjectNNPtr &a, - const common::IdentifiedObjectNNPtr &b) { - const auto &aName = a->nameStr(); - const auto &bName = b->nameStr(); + auto sortLambda = [](const PairObjectName &a, const PairObjectName &b) { + const auto &aName = a.first->nameStr(); + const auto &bName = b.first->nameStr(); if (aName.size() < bName.size()) { return true; } @@ -5817,8 +5855,8 @@ AuthorityFactory::createObjectsFromName( return false; } - const auto &aIds = a->identifiers(); - const auto &bIds = b->identifiers(); + const auto &aIds = a.first->identifiers(); + const auto &bIds = b.first->identifiers(); if (aIds.size() < bIds.size()) { return true; } @@ -5845,13 +5883,15 @@ AuthorityFactory::createObjectsFromName( return false; } } - return strcmp(typeid(a.get()).name(), typeid(b.get()).name()) < 0; + return strcmp(typeid(a.first.get()).name(), + typeid(b.first.get()).name()) < 0; }; res.sort(sortLambda); return res; } +//! @endcond // --------------------------------------------------------------------------- diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp index cf285b0a..8fdd8a01 100644 --- a/test/unit/test_crs.cpp +++ b/test/unit/test_crs.cpp @@ -2523,7 +2523,7 @@ TEST(crs, projectedCRS_identify_db) { auto res = crs->identify(factoryAll); ASSERT_EQ(res.size(), 1U); EXPECT_EQ(res.front().first->getEPSGCode(), 6670); - EXPECT_EQ(res.front().second, 70); + EXPECT_EQ(res.front().second, 90); } { // Test case of https://github.com/OSGeo/PROJ/issues/2116 @@ -2616,6 +2616,30 @@ TEST(crs, projectedCRS_identify_db) { EXPECT_EQ(res.front().first->getEPSGCode(), 32631); EXPECT_EQ(res.front().second, 60); } + { + // Test case of https://github.com/qgis/QGIS/issues/36111 + // The name of the CRS to identify is + // ETRS89_LAEA_Europe + // We identify it through a registered EPSG alias "ETRS89 / LAEA Europe" + auto obj = WKTParser().attachDatabaseContext(dbContext).createFromWKT( + "PROJCS[\"ETRS89_LAEA_Europe\"," + "GEOGCS[\"GCS_ETRS_1989\",DATUM[\"D_ETRS_1989\"," + "SPHEROID[\"GRS_1980\",6378137.0,298.257222101]]," + "PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]]," + "PROJECTION[\"Lambert_Azimuthal_Equal_Area\"]," + "PARAMETER[\"false_easting\",4321000.0]," + "PARAMETER[\"false_northing\",3210000.0]," + "PARAMETER[\"central_meridian\",10.0]," + "PARAMETER[\"latitude_of_origin\",52.0]," + "UNIT[\"Meter\",1.0]]"); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + auto factoryAll = AuthorityFactory::create(dbContext, std::string()); + auto res = crs->identify(factoryAll); + ASSERT_EQ(res.size(), 1U); + EXPECT_EQ(res.front().first->getEPSGCode(), 3035); + EXPECT_EQ(res.front().second, 90); + } } // --------------------------------------------------------------------------- |
