aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2018-12-04 14:46:41 +0100
committerEven Rouault <even.rouault@spatialys.com>2018-12-04 14:46:41 +0100
commitd06c1c55c1c3fc7209abdbdfbf2e3cf34f18cf98 (patch)
tree98fe336a41f1397280ee665d08040339967d711b /src
parentaddf30e4446fd39891fd5bdcb22413ed41e0913b (diff)
downloadPROJ-d06c1c55c1c3fc7209abdbdfbf2e3cf34f18cf98.tar.gz
PROJ-d06c1c55c1c3fc7209abdbdfbf2e3cf34f18cf98.zip
Improve recognition of WKT1 datum names
Diffstat (limited to 'src')
-rw-r--r--src/c_api.cpp10
-rw-r--r--src/factory.cpp100
-rw-r--r--src/io.cpp38
-rw-r--r--src/metadata.cpp60
4 files changed, 176 insertions, 32 deletions
diff --git a/src/c_api.cpp b/src/c_api.cpp
index 7a991765..5c873dcf 100644
--- a/src/c_api.cpp
+++ b/src/c_api.cpp
@@ -1878,6 +1878,16 @@ static GeodeticReferenceFrameNNPtr createGeodeticReferenceFrame(
datumName.c_str(), refDatum->nameStr().c_str())) {
datumName = refDatum->nameStr();
}
+ } else {
+ std::string outTableName;
+ std::string authNameFromAlias;
+ std::string codeFromAlias;
+ auto officialName = authFactory->getOfficialNameFromAlias(
+ datumName, "geodetic_datum", std::string(), true,
+ outTableName, authNameFromAlias, codeFromAlias);
+ if (!officialName.empty()) {
+ datumName = officialName;
+ }
}
}
}
diff --git a/src/factory.cpp b/src/factory.cpp
index 7bc9c4d2..802b50aa 100644
--- a/src/factory.cpp
+++ b/src/factory.cpp
@@ -3567,6 +3567,10 @@ AuthorityFactory::getDescriptionText(const std::string &code) const {
* Or empty otherwise.
* @param source Source of the alias. Can help in case of ambiguities.
* Or empty otherwise.
+ * @param tryEquivalentNameSpelling whether the comparison of aliasedName with
+ * the alt_name column of the alis_name table should be done with using
+ * metadata::Identifier::isEquivalentName() rather than strict string
+ * comparison;
* @param outTableName Table name in which the official name has been found.
* @param outAuthName Authority name of the official name that has been found.
* @param outCode Code of the official name that has been found.
@@ -3575,34 +3579,78 @@ AuthorityFactory::getDescriptionText(const std::string &code) const {
*/
std::string AuthorityFactory::getOfficialNameFromAlias(
const std::string &aliasedName, const std::string &tableName,
- const std::string &source, std::string &outTableName,
- std::string &outAuthName, std::string &outCode) const {
- std::string sql("SELECT table_name, auth_name, code FROM alias_name WHERE "
- "alt_name = ?");
- std::vector<SQLValues> params{aliasedName};
- if (!tableName.empty()) {
- sql += " AND table_name = ?";
- params.push_back(tableName);
- }
- if (!source.empty()) {
- sql += " AND source = ?";
- params.push_back(source);
- }
- auto res = d->run(sql, params);
- if (res.empty()) {
- return std::string();
- }
- outTableName = res[0][0];
- outAuthName = res[0][1];
- outCode = res[0][2];
- sql = "SELECT name FROM \"";
- sql += replaceAll(outTableName, "\"", "\"\"");
- sql += "\" WHERE auth_name = ? AND code = ?";
- res = d->run(sql, {outAuthName, outCode});
- if (res.empty()) { // shouldn't happen normally
+ const std::string &source, bool tryEquivalentNameSpelling,
+ std::string &outTableName, std::string &outAuthName,
+ std::string &outCode) const {
+
+ if (tryEquivalentNameSpelling) {
+ std::string sql(
+ "SELECT table_name, auth_name, code, alt_name FROM alias_name");
+ std::vector<SQLValues> params;
+ if (!tableName.empty()) {
+ sql += " WHERE table_name = ?";
+ params.push_back(tableName);
+ }
+ if (!source.empty()) {
+ if (!tableName.empty()) {
+ sql += " AND ";
+ } else {
+ sql += " WHERE ";
+ }
+ sql += "source = ?";
+ params.push_back(source);
+ }
+ auto res = d->run(sql, params);
+ if (res.empty()) {
+ return std::string();
+ }
+ for (const auto &row : res) {
+ const auto &alt_name = row[3];
+ if (metadata::Identifier::isEquivalentName(alt_name.c_str(),
+ aliasedName.c_str())) {
+ outTableName = row[0];
+ outAuthName = row[1];
+ outCode = row[2];
+ sql = "SELECT name FROM \"";
+ sql += replaceAll(outTableName, "\"", "\"\"");
+ sql += "\" WHERE auth_name = ? AND code = ?";
+ res = d->run(sql, {outAuthName, outCode});
+ if (res.empty()) { // shouldn't happen normally
+ return std::string();
+ }
+ return res[0][0];
+ }
+ }
return std::string();
+ } else {
+ std::string sql(
+ "SELECT table_name, auth_name, code FROM alias_name WHERE "
+ "alt_name = ?");
+ std::vector<SQLValues> params{aliasedName};
+ if (!tableName.empty()) {
+ sql += " AND table_name = ?";
+ params.push_back(tableName);
+ }
+ if (!source.empty()) {
+ sql += " AND source = ?";
+ params.push_back(source);
+ }
+ auto res = d->run(sql, params);
+ if (res.empty()) {
+ return std::string();
+ }
+ outTableName = res[0][0];
+ outAuthName = res[0][1];
+ outCode = res[0][2];
+ sql = "SELECT name FROM \"";
+ sql += replaceAll(outTableName, "\"", "\"\"");
+ sql += "\" WHERE auth_name = ? AND code = ?";
+ res = d->run(sql, {outAuthName, outCode});
+ if (res.empty()) { // shouldn't happen normally
+ return std::string();
+ }
+ return res[0][0];
}
- return res[0][0];
}
// ---------------------------------------------------------------------------
diff --git a/src/io.cpp b/src/io.cpp
index 11e4748e..e0f74f10 100644
--- a/src/io.cpp
+++ b/src/io.cpp
@@ -1475,7 +1475,7 @@ PropertyMap &WKTParser::Private::buildProperties(const WKTNodeNNPtr &node) {
auto authFactory = AuthorityFactory::create(NN_NO_CHECK(dbContext_),
std::string());
auto officialName = authFactory->getOfficialNameFromAlias(
- name, tableNameForAlias, "ESRI", outTableName,
+ name, tableNameForAlias, "ESRI", false, outTableName,
authNameFromAlias, codeFromAlias);
if (!officialName.empty()) {
name = officialName;
@@ -1708,7 +1708,7 @@ UnitOfMeasure WKTParser::Private::buildUnit(const WKTNodeNNPtr &node,
auto authFactory = AuthorityFactory::create(NN_NO_CHECK(dbContext_),
std::string());
auto officialName = authFactory->getOfficialNameFromAlias(
- unitName, "unit_of_measure", "ESRI", outTableName,
+ unitName, "unit_of_measure", "ESRI", false, outTableName,
authNameFromAlias, codeFromAlias);
if (!officialName.empty()) {
unitName = officialName;
@@ -1918,7 +1918,7 @@ GeodeticReferenceFrameNNPtr WKTParser::Private::buildGeodeticReferenceFrame(
auto authFactory = AuthorityFactory::create(NN_NO_CHECK(dbContext_),
std::string());
auto officialName = authFactory->getOfficialNameFromAlias(
- name, tableNameForAlias, "ESRI", outTableName,
+ name, tableNameForAlias, "ESRI", false, outTableName,
authNameFromAlias, codeFromAlias);
if (!officialName.empty()) {
if (primeMeridian->nameStr() !=
@@ -1951,10 +1951,12 @@ GeodeticReferenceFrameNNPtr WKTParser::Private::buildGeodeticReferenceFrame(
auto res = authFactory->createObjectsFromName(
name, {AuthorityFactory::ObjectType::GEODETIC_REFERENCE_FRAME},
true, 1);
+ bool foundDatumName = false;
if (!res.empty()) {
const auto &refDatum = res.front();
if (metadata::Identifier::isEquivalentName(
name.c_str(), refDatum->nameStr().c_str())) {
+ foundDatumName = true;
properties.set(IdentifiedObject::NAME_KEY,
refDatum->nameStr());
if (properties.find(Identifier::CODESPACE_KEY) ==
@@ -1972,6 +1974,34 @@ GeodeticReferenceFrameNNPtr WKTParser::Private::buildGeodeticReferenceFrame(
identifiers);
}
}
+ } else {
+ // Get official name from database if AUTHORITY is present
+ auto &idNode = nodeP->lookForChild(WKTConstants::AUTHORITY);
+ if (!isNull(idNode)) {
+ try {
+ auto id = buildId(idNode);
+ auto authFactory2 = AuthorityFactory::create(
+ NN_NO_CHECK(dbContext_), *id->codeSpace());
+ auto dbDatum =
+ authFactory2->createGeodeticDatum(id->code());
+ foundDatumName = true;
+ properties.set(IdentifiedObject::NAME_KEY,
+ dbDatum->nameStr());
+ } catch (const std::exception &) {
+ }
+ }
+ }
+
+ if (!foundDatumName) {
+ std::string outTableName;
+ std::string authNameFromAlias;
+ std::string codeFromAlias;
+ auto officialName = authFactory->getOfficialNameFromAlias(
+ name, "geodetic_datum", std::string(), true, outTableName,
+ authNameFromAlias, codeFromAlias);
+ if (!officialName.empty()) {
+ properties.set(IdentifiedObject::NAME_KEY, officialName);
+ }
}
}
}
@@ -3371,7 +3401,7 @@ WKTParser::Private::buildProjectedCRS(const WKTNodeNNPtr &node) {
auto authFactory = AuthorityFactory::create(NN_NO_CHECK(dbContext_),
std::string());
auto officialName = authFactory->getOfficialNameFromAlias(
- projCRSName, "projected_crs", "ESRI", outTableName,
+ projCRSName, "projected_crs", "ESRI", false, outTableName,
authNameFromAlias, codeFromAlias);
if (!officialName.empty()) {
props.set(IdentifiedObject::NAME_KEY, officialName);
diff --git a/src/metadata.cpp b/src/metadata.cpp
index 033782c9..af8dc1fe 100644
--- a/src/metadata.cpp
+++ b/src/metadata.cpp
@@ -1106,6 +1106,40 @@ static bool isIgnoredChar(char ch) {
// ---------------------------------------------------------------------------
//! @cond Doxygen_Suppress
+static const struct utf8_to_lower {
+ const char *utf8;
+ char ascii;
+} map_utf8_to_lower[] = {
+ {"\xc3\xa1", 'a'}, // a acute
+ {"\xc3\xa4", 'a'}, // a tremma
+
+ {"\xc4\x9b", 'e'}, // e reverse circumflex
+ {"\xc3\xa8", 'e'}, // e grave
+ {"\xc3\xa9", 'e'}, // e acute
+ {"\xc3\xab", 'e'}, // e tremma
+
+ {"\xc3\xad", 'i'}, // i grave
+
+ {"\xc3\xb4", 'o'}, // o circumflex
+ {"\xc3\xb6", 'o'}, // o tremma
+
+ {"\xc3\xa7", 'c'}, // c cedilla
+};
+
+static const struct utf8_to_lower *get_ascii_replacement(const char *c_str) {
+ for (const auto &pair : map_utf8_to_lower) {
+ if (*c_str == pair.utf8[0] &&
+ strncmp(c_str, pair.utf8, strlen(pair.utf8)) == 0) {
+ return &pair;
+ }
+ }
+ return nullptr;
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
std::string Identifier::canonicalizeName(const std::string &str) {
std::string res;
const char *c_str = str.c_str();
@@ -1121,6 +1155,14 @@ std::string Identifier::canonicalizeName(const std::string &str) {
++i;
continue;
}
+ if (static_cast<unsigned char>(ch) > 127) {
+ const auto *replacement = get_ascii_replacement(c_str + i);
+ if (replacement) {
+ res.push_back(replacement->ascii);
+ i += strlen(replacement->utf8) - 1;
+ continue;
+ }
+ }
if (!isIgnoredChar(ch)) {
res.push_back(static_cast<char>(::tolower(ch)));
}
@@ -1142,8 +1184,8 @@ bool Identifier::isEquivalentName(const char *a, const char *b) noexcept {
char lastValidA = 0;
char lastValidB = 0;
while (a[i] != 0 && b[j] != 0) {
- const char aCh = a[i];
- const char bCh = b[j];
+ char aCh = a[i];
+ char bCh = b[j];
if (aCh == ' ' && a[i + 1] == '+' && a[i + 2] == ' ') {
i += 3;
continue;
@@ -1172,6 +1214,20 @@ bool Identifier::isEquivalentName(const char *a, const char *b) noexcept {
lastValidB = '9';
continue;
}
+ if (static_cast<unsigned char>(aCh) > 127) {
+ const auto *replacement = get_ascii_replacement(a + i);
+ if (replacement) {
+ aCh = replacement->ascii;
+ i += strlen(replacement->utf8) - 1;
+ }
+ }
+ if (static_cast<unsigned char>(bCh) > 127) {
+ const auto *replacement = get_ascii_replacement(b + j);
+ if (replacement) {
+ bCh = replacement->ascii;
+ j += strlen(replacement->utf8) - 1;
+ }
+ }
if (::tolower(aCh) != ::tolower(bCh)) {
return false;
}