Skip to content

Commit

Permalink
WKT ESRI export: fix issue with some projected CRS, such as EPSG:3800…
Browse files Browse the repository at this point in the history
…, that have the same name as a deprecated one
  • Loading branch information
rouault committed Jul 9, 2022
1 parent a390c57 commit 0574b26
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 45 deletions.
92 changes: 63 additions & 29 deletions src/iso19111/crs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2004,7 +2004,7 @@ void GeodeticCRS::_exportToWKT(io::WKTFormatter *formatter) const {
}

if (!isWKT2 && formatter->isStrict() && isGeographic &&
axisList.size() != 2 &&
axisList.size() == 3 &&
oldAxisOutputRule != io::WKTFormatter::OutputAxisRule::NO) {

auto geogCRS2D = demoteTo2D(std::string(), dbContext);
Expand Down Expand Up @@ -2048,49 +2048,66 @@ void GeodeticCRS::_exportToWKT(io::WKTFormatter *formatter) const {
"WKT1 does not support Geographic 3D CRS.");
}

const auto &l_identifiers = identifiers();
formatter->startNode(isWKT2
? ((formatter->use2019Keywords() && isGeographic)
? io::WKTConstants::GEOGCRS
: io::WKTConstants::GEODCRS)
: isGeocentric() ? io::WKTConstants::GEOCCS
: io::WKTConstants::GEOGCS,
!identifiers().empty());
!l_identifiers.empty());

if (formatter->useESRIDialect()) {
if (l_name == "WGS 84") {
l_name = "GCS_WGS_1984";
} else {
bool aliasFound = false;
std::string l_esri_name;
if (dbContext) {
auto l_alias = dbContext->getAliasFromOfficialName(
l_name, "geodetic_crs", "ESRI");
if (!l_alias.empty()) {
l_name = l_alias;
aliasFound = true;
const auto tableName = isGeographic && axisList.size() == 3
? "geographic_3D_crs"
: "geodetic_crs";
if (!l_identifiers.empty()) {
// Try to find the ESRI alias from the CRS identified by its
// id
const auto aliases =
dbContext->getAliases(*(l_identifiers[0]->codeSpace()),
l_identifiers[0]->code(),
std::string(), // officialName,
tableName, "ESRI");
if (aliases.size() == 1)
l_esri_name = aliases.front();
}
if (l_esri_name.empty()) {
// Then find the ESRI alias from the CRS name
l_esri_name = dbContext->getAliasFromOfficialName(
l_name, tableName, "ESRI");
}
if (l_esri_name.empty()) {
// Then try to build an ESRI CRS from the CRS name, and if
// there's one, the ESRI name is the CRS name
auto authFactory = io::AuthorityFactory::create(
NN_NO_CHECK(dbContext), "ESRI");
const bool found = authFactory
->createObjectsFromName(
l_name,
{io::AuthorityFactory::
ObjectType::GEODETIC_CRS},
false // approximateMatch
)
.size() == 1;
if (found)
l_esri_name = l_name;
}
}
if (!aliasFound && dbContext) {
auto authFactory = io::AuthorityFactory::create(
NN_NO_CHECK(dbContext), "ESRI");
aliasFound =
authFactory
->createObjectsFromName(
l_name,
{io::AuthorityFactory::ObjectType::GEODETIC_CRS},
false // approximateMatch
)
.size() == 1;
}
if (!aliasFound) {
l_name = io::WKTFormatter::morphNameToESRI(l_name);
if (!starts_with(l_name, "GCS_")) {
l_name = "GCS_" + l_name;
if (l_esri_name.empty()) {
l_esri_name = io::WKTFormatter::morphNameToESRI(l_name);
if (!starts_with(l_esri_name, "GCS_")) {
l_esri_name = "GCS_" + l_esri_name;
}
}
l_name = l_esri_name;
}
}

if (!isWKT2 && !formatter->useESRIDialect() && isDeprecated()) {
} else if (!isWKT2 && isDeprecated()) {
l_name += " (deprecated)";
}
formatter->addQuotedString(l_name);
Expand Down Expand Up @@ -4114,9 +4131,24 @@ void ProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const {

std::string l_esri_name;
if (formatter->useESRIDialect() && dbContext) {
l_esri_name = dbContext->getAliasFromOfficialName(
l_name, "projected_crs", "ESRI");

if (!l_identifiers.empty()) {
// Try to find the ESRI alias from the CRS identified by its id
const auto aliases = dbContext->getAliases(
*(l_identifiers[0]->codeSpace()), l_identifiers[0]->code(),
std::string(), // officialName,
"projected_crs", "ESRI");
if (aliases.size() == 1)
l_esri_name = aliases.front();
}
if (l_esri_name.empty()) {
// Then find the ESRI alias from the CRS name
l_esri_name = dbContext->getAliasFromOfficialName(
l_name, "projected_crs", "ESRI");
}
if (l_esri_name.empty()) {
// Then try to build an ESRI CRS from the CRS name, and if there's
// one, the ESRI name is the CRS name
auto authFactory =
io::AuthorityFactory::create(NN_NO_CHECK(dbContext), "ESRI");
const bool found =
Expand All @@ -4134,6 +4166,8 @@ void ProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const {
if (!isWKT2 && !l_identifiers.empty() &&
*(l_identifiers[0]->codeSpace()) == "ESRI") {
try {
// If the id of the objet is in the ESRI namespace, then
// try to find the full ESRI WKT from the database
const auto definition = dbContext->getTextDefinition(
"projected_crs", "ESRI", l_identifiers[0]->code());
if (starts_with(definition, "PROJCS")) {
Expand Down
54 changes: 38 additions & 16 deletions src/iso19111/factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3377,7 +3377,10 @@ std::string DatabaseContext::getOldProjGridName(const std::string &gridName) {
/** \brief Gets the alias name from an official name.
*
* @param officialName Official name. Mandatory
* @param tableName Table name/category. Mandatory
* @param tableName Table name/category. Mandatory.
* "geographic_2D_crs" and "geographic_3D_crs" are also
* accepted as special names to add a constraint on the "type"
* column of the "geodetic_crs" table.
* @param source Source of the alias. Mandatory
* @return Alias name (or empty if not found).
* @throw FactoryException
Expand All @@ -3387,29 +3390,38 @@ DatabaseContext::getAliasFromOfficialName(const std::string &officialName,
const std::string &tableName,
const std::string &source) const {
std::string sql("SELECT auth_name, code FROM \"");
sql += replaceAll(tableName, "\"", "\"\"");
const auto genuineTableName =
tableName == "geographic_2D_crs" || tableName == "geographic_3D_crs"
? "geodetic_crs"
: tableName;
sql += replaceAll(genuineTableName, "\"", "\"\"");
sql += "\" WHERE name = ?";
if (tableName == "geodetic_crs") {
if (tableName == "geodetic_crs" || tableName == "geographic_2D_crs") {
sql += " AND type = " GEOG_2D_SINGLE_QUOTED;
} else if (tableName == "geographic_3D_crs") {
sql += " AND type = " GEOG_3D_SINGLE_QUOTED;
}
sql += " ORDER BY deprecated";
auto res = d->run(sql, {officialName});
if (res.empty()) {
res = d->run(
"SELECT auth_name, code FROM alias_name WHERE table_name = ? AND "
"alt_name = ? AND source IN ('EPSG', 'PROJ')",
{tableName, officialName});
{genuineTableName, officialName});
if (res.size() != 1) {
return std::string();
}
}
const auto &row = res.front();
res = d->run("SELECT alt_name FROM alias_name WHERE table_name = ? AND "
"auth_name = ? AND code = ? AND source = ?",
{tableName, row[0], row[1], source});
if (res.empty()) {
return std::string();
for (const auto &row : res) {
auto res2 =
d->run("SELECT alt_name FROM alias_name WHERE table_name = ? AND "
"auth_name = ? AND code = ? AND source = ?",
{genuineTableName, row[0], row[1], source});
if (!res2.empty()) {
return res2.front()[0];
}
}
return res.front()[0];
return std::string();
}

// ---------------------------------------------------------------------------
Expand All @@ -3421,7 +3433,10 @@ DatabaseContext::getAliasFromOfficialName(const std::string &officialName,
* @param authName Authority.
* @param code Code.
* @param officialName Official name.
* @param tableName Table name/category. Mandatory
* @param tableName Table name/category. Mandatory.
* "geographic_2D_crs" and "geographic_3D_crs" are also
* accepted as special names to add a constraint on the "type"
* column of the "geodetic_crs" table.
* @param source Source of the alias. May be empty.
* @return Aliases
*/
Expand All @@ -3438,19 +3453,26 @@ std::list<std::string> DatabaseContext::getAliases(

std::string resolvedAuthName(authName);
std::string resolvedCode(code);
const auto genuineTableName =
tableName == "geographic_2D_crs" || tableName == "geographic_3D_crs"
? "geodetic_crs"
: tableName;
if (authName.empty() || code.empty()) {
std::string sql("SELECT auth_name, code FROM \"");
sql += replaceAll(tableName, "\"", "\"\"");
sql += replaceAll(genuineTableName, "\"", "\"\"");
sql += "\" WHERE name = ?";
if (tableName == "geodetic_crs") {
if (tableName == "geodetic_crs" || tableName == "geographic_2D_crs") {
sql += " AND type = " GEOG_2D_SINGLE_QUOTED;
} else if (tableName == "geographic_3D_crs") {
sql += " AND type = " GEOG_3D_SINGLE_QUOTED;
}
sql += " ORDER BY deprecated";
auto resSql = d->run(sql, {officialName});
if (resSql.empty()) {
resSql = d->run("SELECT auth_name, code FROM alias_name WHERE "
"table_name = ? AND "
"alt_name = ? AND source IN ('EPSG', 'PROJ')",
{tableName, officialName});
{genuineTableName, officialName});
if (resSql.size() != 1) {
d->cacheAliasNames_.insert(key, res);
return res;
Expand All @@ -3462,7 +3484,7 @@ std::list<std::string> DatabaseContext::getAliases(
}
std::string sql("SELECT alt_name FROM alias_name WHERE table_name = ? AND "
"auth_name = ? AND code = ?");
ListOfParams params{tableName, resolvedAuthName, resolvedCode};
ListOfParams params{genuineTableName, resolvedAuthName, resolvedCode};
if (!source.empty()) {
sql += " AND source = ?";
params.emplace_back(source);
Expand Down
39 changes: 39 additions & 0 deletions test/unit/test_crs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2263,6 +2263,45 @@ TEST(crs,

// ---------------------------------------------------------------------------

TEST(crs, projectedCRS_with_other_deprecated_crs_of_same_name_as_WKT1_ESRI) {
auto dbContext = DatabaseContext::create();
// EPSG:3800 is the non-deprecated version of EPSG:3774
// This used to cause an issue when looking for the ESRI CRS name
auto crs =
AuthorityFactory::create(dbContext, "EPSG")->createProjectedCRS("3800");

auto esri_wkt =
"PROJCS[\"NAD_1927_3TM_120\",GEOGCS[\"GCS_North_American_1927\","
"DATUM[\"D_North_American_1927\","
"SPHEROID[\"Clarke_1866\",6378206.4,294.978698213898]],"
"PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]],"
"PROJECTION[\"Transverse_Mercator\"],"
"PARAMETER[\"False_Easting\",0.0],"
"PARAMETER[\"False_Northing\",0.0],"
"PARAMETER[\"Central_Meridian\",-120.0],"
"PARAMETER[\"Scale_Factor\",0.9999],"
"PARAMETER[\"Latitude_Of_Origin\",0.0],UNIT[\"Meter\",1.0]]";
EXPECT_EQ(
crs->exportToWKT(
WKTFormatter::create(WKTFormatter::Convention::WKT1_ESRI, dbContext)
.get()),
esri_wkt);

auto obj =
WKTParser().attachDatabaseContext(dbContext).createFromWKT(esri_wkt);
auto crs2 = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
ASSERT_TRUE(crs2 != nullptr);
EXPECT_EQ(crs2->nameStr(), "NAD27 / Alberta 3TM ref merid 120 W");

EXPECT_EQ(
crs2->exportToWKT(
WKTFormatter::create(WKTFormatter::Convention::WKT1_ESRI, dbContext)
.get()),
esri_wkt);
}

// ---------------------------------------------------------------------------

TEST(crs, projectedCRS_with_ESRI_code_as_WKT1_ESRI) {
auto dbContext = DatabaseContext::create();
auto crs = AuthorityFactory::create(dbContext, "ESRI")
Expand Down

0 comments on commit 0574b26

Please sign in to comment.