diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2018-12-04 14:46:41 +0100 |
|---|---|---|
| committer | Even Rouault <even.rouault@spatialys.com> | 2018-12-04 14:46:41 +0100 |
| commit | d06c1c55c1c3fc7209abdbdfbf2e3cf34f18cf98 (patch) | |
| tree | 98fe336a41f1397280ee665d08040339967d711b /src | |
| parent | addf30e4446fd39891fd5bdcb22413ed41e0913b (diff) | |
| download | PROJ-d06c1c55c1c3fc7209abdbdfbf2e3cf34f18cf98.tar.gz PROJ-d06c1c55c1c3fc7209abdbdfbf2e3cf34f18cf98.zip | |
Improve recognition of WKT1 datum names
Diffstat (limited to 'src')
| -rw-r--r-- | src/c_api.cpp | 10 | ||||
| -rw-r--r-- | src/factory.cpp | 100 | ||||
| -rw-r--r-- | src/io.cpp | 38 | ||||
| -rw-r--r-- | src/metadata.cpp | 60 |
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]; } // --------------------------------------------------------------------------- @@ -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; } |
