aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2020-05-25 19:47:43 +0200
committerEven Rouault <even.rouault@spatialys.com>2020-05-25 19:47:51 +0200
commit172744361c83d2a40c11600a398a0e1668052031 (patch)
tree84a034e942372ada367eb832b48061ee6d11e11b
parentcfdd474dd8aef197297c488c6133f7484fe421fd (diff)
downloadPROJ-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.hpp8
-rw-r--r--src/iso19111/crs.cpp9
-rw-r--r--src/iso19111/factory.cpp116
-rw-r--r--test/unit/test_crs.cpp26
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);
+ }
}
// ---------------------------------------------------------------------------