From 33372ff7280e20f9d5b314da9a655e38c1633b51 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 27 Oct 2025 20:35:40 +0100 Subject: [PATCH] projinfo: make it honour '-k crs' --- src/apps/projinfo.cpp | 71 +++++++++++++++++++++++++++++++++---- test/cli/test_projinfo.yaml | 23 ++++++++++++ 2 files changed, 87 insertions(+), 7 deletions(-) diff --git a/src/apps/projinfo.cpp b/src/apps/projinfo.cpp index 75825b68a0..fc35eb306d 100644 --- a/src/apps/projinfo.cpp +++ b/src/apps/projinfo.cpp @@ -346,7 +346,7 @@ static BaseObjectNNPtr buildObject( } } } - } else if (dbContext && !kind.empty() && kind != "crs" && + } else if (dbContext && !kind.empty() && kind != "crs_preferred" && l_user_string.find(':') == std::string::npos) { std::vector allowedTypes; if (kind == "operation") @@ -360,6 +360,8 @@ static BaseObjectNNPtr buildObject( else if (kind == "ensemble") allowedTypes.push_back( AuthorityFactory::ObjectType::DATUM_ENSEMBLE); + else if (kind == "crs") + allowedTypes.push_back(AuthorityFactory::ObjectType::CRS); constexpr size_t limitResultCount = 10; auto factory = AuthorityFactory::create(NN_NO_CHECK(dbContext), std::string()); @@ -406,6 +408,61 @@ static BaseObjectNNPtr buildObject( } else { obj = createFromUserInput(l_user_string, dbContext).as_nullable(); + if (kind == "crs_preferred" && + dynamic_cast(obj.get()) == nullptr) { + auto factory = AuthorityFactory::create( + NN_NO_CHECK(dbContext), std::string()); + auto res = factory->createObjectsFromName( + l_user_string, {AuthorityFactory::ObjectType::CRS}, + /* approximateMatch = */ false, + /* limitResultCount = */ 2); + if (res.size() == 1) { + obj = res.front().as_nullable(); + } else if (res.empty()) { + res = factory->createObjectsFromName( + l_user_string, {AuthorityFactory::ObjectType::CRS}, + /* approximateMatch = */ true, + /* limitResultCount = */ 2); + if (res.size() == 1) { + obj = res.front().as_nullable(); + } else { + bool severalCandidates = false; + BaseObjectPtr objCandidate; + for (const auto &l_obj : res) { + if (Identifier::isEquivalentName( + l_obj->nameStr().c_str(), + l_user_string.c_str())) { + if (objCandidate) { + severalCandidates = true; + break; + } else { + objCandidate = l_obj.as_nullable(); + } + } + } + if (severalCandidates) { + std::string msg( + "several objects matching this name: "); + bool first = true; + for (const auto &l_obj : res) { + if (msg.size() > 200) { + msg += ", ..."; + break; + } + if (!first) { + msg += ", "; + } + first = false; + msg += l_obj->nameStr(); + } + std::cerr << context << ": " << msg + << std::endl; + std::exit(1); + } else if (objCandidate) + obj = objCandidate; + } + } + } } } } catch (const std::exception &e) { @@ -880,9 +937,9 @@ static void outputOperations( bool showSuperseded, bool promoteTo3D, bool normalizeAxisOrder, double minimumAccuracy, const OutputOptions &outputOpt, bool summary) { auto sourceObj = buildObject( - dbContext, sourceCRSStr, sourceEpoch, "crs", "source CRS", false, - CoordinateOperationContext::IntermediateCRSUse::NEVER, promoteTo3D, - normalizeAxisOrder, outputOpt.quiet); + dbContext, sourceCRSStr, sourceEpoch, "crs_preferred", "source CRS", + false, CoordinateOperationContext::IntermediateCRSUse::NEVER, + promoteTo3D, normalizeAxisOrder, outputOpt.quiet); auto sourceCRS = nn_dynamic_pointer_cast(sourceObj); CoordinateMetadataPtr sourceCoordinateMetadata; if (!sourceCRS) { @@ -901,9 +958,9 @@ static void outputOperations( } auto targetObj = buildObject( - dbContext, targetCRSStr, targetEpoch, "crs", "target CRS", false, - CoordinateOperationContext::IntermediateCRSUse::NEVER, promoteTo3D, - normalizeAxisOrder, outputOpt.quiet); + dbContext, targetCRSStr, targetEpoch, "crs_preferred", "target CRS", + false, CoordinateOperationContext::IntermediateCRSUse::NEVER, + promoteTo3D, normalizeAxisOrder, outputOpt.quiet); auto targetCRS = nn_dynamic_pointer_cast(targetObj); CoordinateMetadataPtr targetCoordinateMetadata; if (!targetCRS) { diff --git a/test/cli/test_projinfo.yaml b/test/cli/test_projinfo.yaml index dd112407cf..f6c2066c22 100644 --- a/test/cli/test_projinfo.yaml +++ b/test/cli/test_projinfo.yaml @@ -1940,3 +1940,26 @@ tests: out: | Candidate operations found: 1 unknown id, Inverse of Saba to WGS 84 (1) + Saba to Saba height (1) + Saba Transverse Mercator 2020, 1.2 m, Bonaire, Sint Eustatius and Saba (BES Islands or Caribbean Netherlands) - Saba - onshore. +- comment: Test that 'projinfo EGM2008' by default resolves to the datum + args: EGM2008 + out: | + WKT2:2019 string: + VDATUM["EGM2008 geoid", + ID["EPSG",1027]] +- comment: Test that 'projinfo -k crs EGM2008' resolves to the CRS + args: -k crs EGM2008 + out: | + PROJ.4 string: + +geoidgrids=us_nga_egm08_25.tif +geoid_crs=WGS84 +vunits=m +no_defs +type=crs + + WKT2:2019 string: + VERTCRS["EGM2008 height", + VDATUM["EGM2008 geoid"], + CS[vertical,1], + AXIS["gravity-related height (H)",up, + LENGTHUNIT["metre",1]], + USAGE[ + SCOPE["Geodesy."], + AREA["World."], + BBOX[-90,-180,90,180]], + ID["EPSG",3855]]