aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2019-02-21 17:43:19 +0100
committerEven Rouault <even.rouault@spatialys.com>2019-02-21 17:43:19 +0100
commit9cbe9bc5d77310c5d85226bb7f030d6dc847b28f (patch)
treeb48ac00785b9107072de08b2d591e62c3143fe32
parent4d57661a52a2439e4e83c40847afdb14bea2e87b (diff)
parentbbf31d0e1052a40269547d0dec5d63a7e0534ccc (diff)
downloadPROJ-9cbe9bc5d77310c5d85226bb7f030d6dc847b28f.tar.gz
PROJ-9cbe9bc5d77310c5d85226bb7f030d6dc847b28f.zip
Merge branch 'master' into 6.0
-rw-r--r--data/sql/grid_alternatives.sql149
-rw-r--r--data/sql/grid_transformation_custom.sql28
-rw-r--r--src/apps/projinfo.cpp49
-rw-r--r--src/iso19111/coordinateoperation.cpp103
-rwxr-xr-xtest/cli/testprojinfo10
-rw-r--r--test/cli/testprojinfo_out.dist62
-rw-r--r--test/unit/test_operation.cpp3
7 files changed, 369 insertions, 35 deletions
diff --git a/data/sql/grid_alternatives.sql b/data/sql/grid_alternatives.sql
index 99f20d0e..6d766b21 100644
--- a/data/sql/grid_alternatives.sql
+++ b/data/sql/grid_alternatives.sql
@@ -26,6 +26,12 @@ INSERT INTO grid_packages VALUES ('proj-datumgrid-europe',
1,
1);
+INSERT INTO grid_packages VALUES ('proj-datumgrid-oceania',
+ 'Package with grids of interest for Oceania',
+ 'https://download.osgeo.org/proj/proj-datumgrid-oceania-latest.zip',
+ 1,
+ 1);
+
-- not released yet at the time of writing
INSERT INTO grid_packages VALUES ('proj-datumgrid-world',
'Package with grids of global extent (too large to be included in proj-datumgrid)',
@@ -286,6 +292,74 @@ INSERT INTO grid_alternatives(original_grid_name,
'proj-datumgrid-europe',
NULL, NULL, NULL, NULL);
+-- Faroe islands height models
+
+INSERT INTO grid_alternatives(original_grid_name,
+ proj_grid_name,
+ proj_grid_format,
+ proj_method,
+ inverse_direction,
+ package_name,
+ url, direct_download, open_license, directory)
+ VALUES ('fvr09.gtx',
+ 'fvr09.gtx', -- no change. Just document the package
+ 'GTX',
+ 'vgridshift',
+ 0, -- do not reverse here as grid_transformation_custom does reference from VerticalCRS height to Geographic height
+ 'proj-datumgrid-europe',
+ NULL, NULL, NULL, NULL);
+
+-- Sweden height models
+
+INSERT INTO grid_alternatives(original_grid_name,
+ proj_grid_name,
+ proj_grid_format,
+ proj_method,
+ inverse_direction,
+ package_name,
+ url, direct_download, open_license, directory)
+ VALUES ('SWEN17_RH2000.gtx',
+ 'SWEN17_RH2000.gtx', -- no change. Just document the package
+ 'GTX',
+ 'vgridshift',
+ 0, -- do not reverse here as grid_transformation_custom does reference from VerticalCRS height to Geographic height
+ 'proj-datumgrid-europe',
+ NULL, NULL, NULL, NULL);
+
+-- Ireland: OSGM15 height, Malin head datum -> ETRS89 ellipsoidal heights
+
+INSERT INTO grid_alternatives(original_grid_name,
+ proj_grid_name,
+ proj_grid_format,
+ proj_method,
+ inverse_direction,
+ package_name,
+ url, direct_download, open_license, directory)
+ VALUES ('OSGM15_Malin.gri',
+ 'OSGM15_Malin.gtx',
+ 'GTX',
+ 'vgridshift',
+ 1,
+ 'proj-datumgrid-europe',
+ NULL, NULL, NULL, NULL);
+
+-- Northern Ireland: OSGM15 height, Belfast height -> ETRS89 ellipsoidal heights
+
+INSERT INTO grid_alternatives(original_grid_name,
+ proj_grid_name,
+ proj_grid_format,
+ proj_method,
+ inverse_direction,
+ package_name,
+ url, direct_download, open_license, directory)
+ VALUES ('OSGM15_Belfast.gri',
+ 'OSGM15_Belfast.gtx',
+ 'GTX',
+ 'vgridshift',
+ 1,
+ 'proj-datumgrid-europe',
+ NULL, NULL, NULL, NULL);
+
----------------------------
-- US GEOID12B height models
----------------------------
@@ -302,7 +376,7 @@ INSERT INTO grid_alternatives(original_grid_name,
'g2012bu0.gtx',
'GTX',
'vgridshift',
- 0,
+ 1,
'proj-datumgrid-north-america',
NULL, NULL, NULL, NULL);
@@ -318,7 +392,7 @@ INSERT INTO grid_alternatives(original_grid_name,
'g2012ba0.gtx',
'GTX',
'vgridshift',
- 0,
+ 1,
'proj-datumgrid-north-america',
NULL, NULL, NULL, NULL);
@@ -334,7 +408,7 @@ INSERT INTO grid_alternatives(original_grid_name,
'g2012bp0.gtx',
'GTX',
'vgridshift',
- 0,
+ 1,
'proj-datumgrid-north-america',
NULL, NULL, NULL, NULL);
@@ -350,7 +424,7 @@ INSERT INTO grid_alternatives(original_grid_name,
'g2012bg0.gtx',
'GTX',
'vgridshift',
- 0,
+ 1,
'proj-datumgrid-north-america',
NULL, NULL, NULL, NULL);
@@ -366,7 +440,7 @@ INSERT INTO grid_alternatives(original_grid_name,
'g2012bs0.gtx',
'GTX',
'vgridshift',
- 0,
+ 1,
'proj-datumgrid-north-america',
NULL, NULL, NULL, NULL);
@@ -644,3 +718,68 @@ INSERT INTO grid_alternatives(original_grid_name,
1,
'proj-datumgrid-europe',
NULL, NULL, NULL, NULL);
+
+
+----------------------------
+-- Australian grids
+----------------------------
+
+INSERT INTO grid_alternatives(original_grid_name,
+ proj_grid_name,
+ proj_grid_format,
+ proj_method,
+ inverse_direction,
+ package_name,
+ url, direct_download, open_license, directory)
+ VALUES ('A66 National (13.09.01).gsb',
+ 'A66_National_13_09_01.gsb',
+ 'NTv2',
+ 'hgridshift',
+ 0,
+ 'proj-datumgrid-oceania',
+ NULL, NULL, NULL, NULL);
+
+INSERT INTO grid_alternatives(original_grid_name,
+ proj_grid_name,
+ proj_grid_format,
+ proj_method,
+ inverse_direction,
+ package_name,
+ url, direct_download, open_license, directory)
+ VALUES ('National 84 (02.07.01).gsb',
+ 'National_84_02_07_01.gsb',
+ 'NTv2',
+ 'hgridshift',
+ 0,
+ 'proj-datumgrid-oceania',
+ NULL, NULL, NULL, NULL);
+
+INSERT INTO grid_alternatives(original_grid_name,
+ proj_grid_name,
+ proj_grid_format,
+ proj_method,
+ inverse_direction,
+ package_name,
+ url, direct_download, open_license, directory)
+ VALUES ('GDA94_GDA2020_conformal.gsb',
+ 'GDA94_GDA2020_conformal.gsb',
+ 'NTv2',
+ 'hgridshift',
+ 0,
+ 'proj-datumgrid-oceania',
+ NULL, NULL, NULL, NULL);
+
+INSERT INTO grid_alternatives(original_grid_name,
+ proj_grid_name,
+ proj_grid_format,
+ proj_method,
+ inverse_direction,
+ package_name,
+ url, direct_download, open_license, directory)
+ VALUES ('GDA94_GDA2020_conformal_and_distortion.gsb',
+ 'GDA94_GDA2020_conformal_and_distortion.gsb',
+ 'NTv2',
+ 'hgridshift',
+ 0,
+ 'proj-datumgrid-oceania',
+ NULL, NULL, NULL, NULL);
diff --git a/data/sql/grid_transformation_custom.sql b/data/sql/grid_transformation_custom.sql
index a43312fe..3c47273e 100644
--- a/data/sql/grid_transformation_custom.sql
+++ b/data/sql/grid_transformation_custom.sql
@@ -1,5 +1,7 @@
-- This file is hand generated.
+-- Denmark
+
INSERT INTO "grid_transformation" VALUES(
'PROJ','EPSG_5799_TO_EPSG_4937','DVR90 height to ETRS89',
NULL,NULL,
@@ -21,3 +23,29 @@ INSERT INTO "grid_transformation" VALUES(
NULL,
'EPSG','8666','Geoid (height correction) model file','dnn.gtx',
NULL,NULL,NULL,NULL,NULL,NULL,0);
+
+-- Faroe Islands
+
+INSERT INTO "grid_transformation" VALUES(
+ 'PROJ','EPSG_5317_TO_EPSG_4937','FVR09 height to ETRS89',
+ NULL,NULL,
+ 'PROJ','HEIGHT_TO_GEOGRAPHIC3D','GravityRelatedHeight to Geographic3D',
+ 'EPSG','5317', -- source CRS (FVR09 height)
+ 'EPSG','4937', -- target CRS (ETRS89)
+ 'EPSG','3248', -- area of use: Faroe Islands - onshore
+ NULL,
+ 'EPSG','8666','Geoid (height correction) model file','fvr09.gtx',
+ NULL,NULL,NULL,NULL,NULL,NULL,0);
+
+-- Sweden
+
+INSERT INTO "grid_transformation" VALUES(
+ 'PROJ','EPSG_5613_TO_EPSG_4377','RH2000 height to SWEREF99',
+ NULL,NULL,
+ 'PROJ','HEIGHT_TO_GEOGRAPHIC3D','GravityRelatedHeight to Geographic3D',
+ 'EPSG','5613', -- source CRS (RH2000 height)
+ 'EPSG','4377', -- target CRS (SWEREF99)
+ 'EPSG','3313', -- area of use: Sweden onshore
+ NULL,
+ 'EPSG','8666','Geoid (height correction) model file','SWEN17_RH2000.gtx',
+ NULL,NULL,NULL,NULL,NULL,NULL,0);
diff --git a/src/apps/projinfo.cpp b/src/apps/projinfo.cpp
index 094587e2..7e401887 100644
--- a/src/apps/projinfo.cpp
+++ b/src/apps/projinfo.cpp
@@ -464,11 +464,42 @@ static void outputObject(
}
}
}
+
+ auto op = dynamic_cast<CoordinateOperation *>(obj.get());
+ if (op && dbContext && getenv("PROJINFO_NO_GRID_CHECK") == nullptr) {
+ try {
+ auto setGrids = op->gridsNeeded(dbContext);
+ bool firstWarning = true;
+ for (const auto &grid : setGrids) {
+ if (!grid.available) {
+ if (firstWarning) {
+ std::cout << std::endl;
+ firstWarning = false;
+ }
+ std::cout << "Grid " << grid.shortName
+ << " needed but not found on the system.";
+ if (!grid.packageName.empty()) {
+ std::cout << " Can be obtained from the "
+ << grid.packageName << " package";
+ if (!grid.url.empty()) {
+ std::cout << " at " << grid.url;
+ }
+ } else if (!grid.url.empty()) {
+ std::cout << " Can be obtained at " << grid.url;
+ }
+ std::cout << std::endl;
+ }
+ }
+ } catch (const std::exception &e) {
+ std::cerr << "Error in gridsNeeded(): " << e.what() << std::endl;
+ }
+ }
}
// ---------------------------------------------------------------------------
-static void outputOperationSummary(const CoordinateOperationNNPtr &op) {
+static void outputOperationSummary(const CoordinateOperationNNPtr &op,
+ const DatabaseContextPtr &dbContext) {
auto ids = op->identifiers();
if (!ids.empty()) {
std::cout << *(ids[0]->codeSpace()) << ":" << ids[0]->code();
@@ -512,6 +543,18 @@ static void outputOperationSummary(const CoordinateOperationNNPtr &op) {
std::cout << ", has ballpark transformation";
}
+ if (dbContext && getenv("PROJINFO_NO_GRID_CHECK") == nullptr) {
+ try {
+ auto setGrids = op->gridsNeeded(dbContext);
+ for (const auto &grid : setGrids) {
+ if (!grid.available) {
+ std::cout << ", at least one grid missing";
+ break;
+ }
+ }
+ } catch (const std::exception &) {
+ }
+ }
std::cout << std::endl;
}
@@ -598,7 +641,7 @@ static void outputOperations(
}
if (summary) {
for (const auto &op : list) {
- outputOperationSummary(op);
+ outputOperationSummary(op, dbContext);
}
} else {
bool first = true;
@@ -613,7 +656,7 @@ static void outputOperations(
"\xC2\xB0"
<< (i + 1) << ":" << std::endl
<< std::endl;
- outputOperationSummary(op);
+ outputOperationSummary(op, dbContext);
std::cout << std::endl;
outputObject(dbContext, op, allowUseIntermediateCRS, outputOpt);
}
diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp
index 224b19ef..7b0adc6f 100644
--- a/src/iso19111/coordinateoperation.cpp
+++ b/src/iso19111/coordinateoperation.cpp
@@ -106,6 +106,7 @@ constexpr double UTM_SOUTH_FALSE_NORTHING = 10000000.0;
static const std::string INVERSE_OF = "Inverse of ";
static const char *BALLPARK_GEOCENTRIC_TRANSLATION =
"Ballpark geocentric translation";
+static const char *NULL_GEOGRAPHIC_OFFSET = "Null geographic offset";
static const char *BALLPARK_GEOGRAPHIC_OFFSET = "Ballpark geographic offset";
static const char *BALLPARK_VERTICAL_TRANSFORMATION_PREFIX =
" (ballpark vertical transformation";
@@ -7189,6 +7190,8 @@ createPropertiesForInverse(const CoordinateOperation *op, bool derivedFrom,
opType = BALLPARK_GEOCENTRIC_TRANSLATION;
} else if (starts_with(forwardName, BALLPARK_GEOGRAPHIC_OFFSET)) {
opType = BALLPARK_GEOGRAPHIC_OFFSET;
+ } else if (starts_with(forwardName, NULL_GEOGRAPHIC_OFFSET)) {
+ opType = NULL_GEOGRAPHIC_OFFSET;
} else if (dynamic_cast<const Transformation *>(op) ||
starts_with(forwardName, "Transformation from ")) {
opType = "Transformation";
@@ -10182,6 +10185,8 @@ struct FilterResults {
const bool isNullTransformation =
op->nameStr().find(BALLPARK_GEOGRAPHIC_OFFSET) !=
std::string::npos ||
+ op->nameStr().find(NULL_GEOGRAPHIC_OFFSET) !=
+ std::string::npos ||
op->nameStr().find(BALLPARK_GEOCENTRIC_TRANSLATION) !=
std::string::npos;
map[op.get()] = PrecomputedOpCharacteristics(
@@ -10206,6 +10211,7 @@ struct FilterResults {
if (hasOpThatContainsAreaOfInterest && res.size() > 1) {
const std::string &name = res.back()->nameStr();
if (name.find(BALLPARK_GEOGRAPHIC_OFFSET) != std::string::npos ||
+ name.find(NULL_GEOGRAPHIC_OFFSET) != std::string::npos ||
name.find(BALLPARK_GEOCENTRIC_TRANSLATION) !=
std::string::npos) {
std::vector<CoordinateOperationNNPtr> resTemp;
@@ -10622,7 +10628,18 @@ static std::vector<CoordinateOperationNNPtr> findsOpsInRegistryWithIntermediate(
static TransformationNNPtr
createBallparkGeographicOffset(const crs::CRSNNPtr &sourceCRS,
const crs::CRSNNPtr &targetCRS) {
- std::string name(BALLPARK_GEOGRAPHIC_OFFSET);
+
+ const crs::GeographicCRS *geogSrc =
+ dynamic_cast<const crs::GeographicCRS *>(sourceCRS.get());
+ const crs::GeographicCRS *geogDst =
+ dynamic_cast<const crs::GeographicCRS *>(targetCRS.get());
+ const bool isSameDatum =
+ geogSrc && geogDst && geogSrc->datum() && geogDst->datum() &&
+ geogSrc->datum()->_isEquivalentTo(
+ geogDst->datum().get(), util::IComparable::Criterion::EQUIVALENT);
+
+ std::string name(isSameDatum ? NULL_GEOGRAPHIC_OFFSET
+ : BALLPARK_GEOGRAPHIC_OFFSET);
name += " from ";
name += sourceCRS->nameStr();
name += " to ";
@@ -10641,6 +10658,12 @@ createBallparkGeographicOffset(const crs::CRSNNPtr &sourceCRS,
sameExtent ? NN_NO_CHECK(sourceCRSExtent)
: metadata::Extent::WORLD);
const common::Angle angle0(0);
+
+ std::vector<metadata::PositionalAccuracyNNPtr> accuracies;
+ if (isSameDatum) {
+ accuracies.emplace_back(metadata::PositionalAccuracy::create("0"));
+ }
+
if (dynamic_cast<const crs::SingleCRS *>(sourceCRS.get())
->coordinateSystem()
->axisList()
@@ -10650,10 +10673,11 @@ createBallparkGeographicOffset(const crs::CRSNNPtr &sourceCRS,
->axisList()
.size() == 3) {
return Transformation::createGeographic3DOffsets(
- map, sourceCRS, targetCRS, angle0, angle0, common::Length(0), {});
+ map, sourceCRS, targetCRS, angle0, angle0, common::Length(0),
+ accuracies);
} else {
return Transformation::createGeographic2DOffsets(
- map, sourceCRS, targetCRS, angle0, angle0, {});
+ map, sourceCRS, targetCRS, angle0, angle0, accuracies);
}
}
//! @endcond
@@ -10829,30 +10853,58 @@ static CoordinateOperationNNPtr createHorizVerticalPROJBased(
auto exportable = util::nn_make_shared<MyPROJStringExportableHorizVertical>(
horizTransform, verticalTransform, geogDst);
- bool dummy = false;
- auto ops = std::vector<CoordinateOperationNNPtr>{horizTransform,
- verticalTransform};
- auto extent = getExtent(ops, true, dummy);
- auto properties = util::PropertyMap();
- properties.set(common::IdentifiedObject::NAME_KEY,
- computeConcatenatedName(ops));
-
- if (extent) {
- properties.set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY,
- NN_NO_CHECK(extent));
+ bool horizTransformIsNoOp = horizTransform->sourceCRS()->_isEquivalentTo(
+ horizTransform->targetCRS().get());
+ if (!horizTransformIsNoOp) {
+ const crs::GeographicCRS *geogSrc =
+ dynamic_cast<const crs::GeographicCRS *>(
+ horizTransform->sourceCRS().get());
+ if (geogSrc) {
+ horizTransformIsNoOp =
+ geogSrc->is2DPartOf3D(NN_NO_CHECK(geogDst.get()));
+ }
}
- std::vector<metadata::PositionalAccuracyNNPtr> accuracies;
- const double accuracy = getAccuracy(ops);
- if (accuracy >= 0.0) {
- accuracies.emplace_back(
- metadata::PositionalAccuracy::create(toString(accuracy)));
- }
+ if (horizTransformIsNoOp) {
+ auto properties = util::PropertyMap();
+ properties.set(common::IdentifiedObject::NAME_KEY,
+ verticalTransform->nameStr());
+ bool dummy = false;
+ auto extent = getExtent(verticalTransform, true, dummy);
+ if (extent) {
+ properties.set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY,
+ NN_NO_CHECK(extent));
+ }
+ return createPROJBased(
+ properties, exportable, sourceCRS, targetCRS,
+ verticalTransform->coordinateOperationAccuracies(),
+ verticalTransform->hasBallparkTransformation());
+ } else {
+ bool dummy = false;
+ auto ops = std::vector<CoordinateOperationNNPtr>{horizTransform,
+ verticalTransform};
+ auto extent = getExtent(ops, true, dummy);
+ auto properties = util::PropertyMap();
+ properties.set(common::IdentifiedObject::NAME_KEY,
+ computeConcatenatedName(ops));
- return createPROJBased(properties, exportable, sourceCRS, targetCRS,
- accuracies,
- horizTransform->hasBallparkTransformation() ||
- verticalTransform->hasBallparkTransformation());
+ if (extent) {
+ properties.set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY,
+ NN_NO_CHECK(extent));
+ }
+
+ std::vector<metadata::PositionalAccuracyNNPtr> accuracies;
+ const double accuracy = getAccuracy(ops);
+ if (accuracy >= 0.0) {
+ accuracies.emplace_back(
+ metadata::PositionalAccuracy::create(toString(accuracy)));
+ }
+
+ return createPROJBased(
+ properties, exportable, sourceCRS, targetCRS, accuracies,
+ horizTransform->hasBallparkTransformation() ||
+ verticalTransform->hasBallparkTransformation());
+ }
}
// ---------------------------------------------------------------------------
@@ -11147,7 +11199,8 @@ findCandidateGeodCRSForDatum(const io::AuthorityFactoryPtr &authFactory,
static bool isNullTransformation(const std::string &name) {
return starts_with(name, BALLPARK_GEOCENTRIC_TRANSLATION) ||
- starts_with(name, BALLPARK_GEOGRAPHIC_OFFSET);
+ starts_with(name, BALLPARK_GEOGRAPHIC_OFFSET) ||
+ starts_with(name, NULL_GEOGRAPHIC_OFFSET);
}
// ---------------------------------------------------------------------------
diff --git a/test/cli/testprojinfo b/test/cli/testprojinfo
index 24a1fdd5..0f987850 100755
--- a/test/cli/testprojinfo
+++ b/test/cli/testprojinfo
@@ -29,6 +29,8 @@ OUT=testprojinfo_out
rm -f ${OUT}
+export PROJINFO_NO_GRID_CHECK=YES
+
echo "Testing projinfo EPSG:4326" >> ${OUT}
$EXE EPSG:4326 >>${OUT}
echo "" >>${OUT}
@@ -102,6 +104,14 @@ echo "Testing CRS with towgs84: projinfo -o PROJ EPSG:25832" >> ${OUT}
$EXE -o PROJ EPSG:25832 >>${OUT} 2>&1
echo "" >>${OUT}
+echo "Testing RH2000 height to SWEREF99: projinfo -s EPSG:5613 -t EPSG:4377" >> ${OUT}
+$EXE -s EPSG:5613 -t EPSG:4377 >>${OUT} 2>&1
+echo "" >>${OUT}
+
+echo "Testing NAD83(2011) + NAVD88 height -> NAD83(2011) : projinfo -s EPSG:6349 -t EPSG:6319 --spatial-test intersects -o PROJ" >> ${OUT}
+$EXE -s EPSG:6349 -t EPSG:6319 --spatial-test intersects -o PROJ >>${OUT} 2>&1
+echo "" >>${OUT}
+
# do 'diff' with distribution results
echo "diff ${OUT} with testprojinfo_out.dist"
diff -u ${OUT} ${TEST_CLI_DIR}/testprojinfo_out.dist
diff --git a/test/cli/testprojinfo_out.dist b/test/cli/testprojinfo_out.dist
index 674e9631..b22766d9 100644
--- a/test/cli/testprojinfo_out.dist
+++ b/test/cli/testprojinfo_out.dist
@@ -694,3 +694,65 @@ Testing CRS with towgs84: projinfo -o PROJ EPSG:25832
PROJ.4 string:
+proj=utm +zone=32 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs
+Testing RH2000 height to SWEREF99: projinfo -s EPSG:5613 -t EPSG:4377
+Candidate operations found: 1
+-------------------------------------
+Operation n°1:
+
+PROJ:EPSG_5613_TO_EPSG_4377, RH2000 height to SWEREF99, unknown accuracy, Sweden - onshore
+
+PROJ string:
++proj=vgridshift +grids=SWEN17_RH2000.gtx +multiplier=1
+
+WKT2_2018 string:
+COORDINATEOPERATION["RH2000 height to SWEREF99",
+ SOURCECRS[
+ VERTCRS["RH2000 height",
+ VDATUM["Rikets hojdsystem 2000"],
+ CS[vertical,1],
+ AXIS["gravity-related height (H)",up,
+ LENGTHUNIT["metre",1]]]],
+ TARGETCRS[
+ GEOGCRS["SWEREF99 (3D)",
+ DATUM["SWEREF99",
+ ELLIPSOID["GRS 1980",6378137,298.257222101,
+ LENGTHUNIT["metre",1]]],
+ PRIMEM["Greenwich",0,
+ ANGLEUNIT["degree",0.0174532925199433]],
+ CS[ellipsoidal,3],
+ AXIS["geodetic latitude (Lat)",north,
+ ORDER[1],
+ ANGLEUNIT["degree minute second hemisphere",0.0174532925199433]],
+ AXIS["geodetic longitude (Long)",east,
+ ORDER[2],
+ ANGLEUNIT["degree minute second hemisphere",0.0174532925199433]],
+ AXIS["ellipsoidal height (h)",up,
+ ORDER[3],
+ LENGTHUNIT["metre",1]]]],
+ METHOD["GravityRelatedHeight to Geographic3D",
+ ID["PROJ","HEIGHT_TO_GEOGRAPHIC3D"]],
+ PARAMETERFILE["Geoid (height correction) model file","SWEN17_RH2000.gtx"],
+ USAGE[
+ SCOPE["unknown"],
+ AREA["Sweden - onshore"],
+ BBOX[55.28,10.93,69.07,24.17]],
+ ID["PROJ","EPSG_5613_TO_EPSG_4377"]]
+
+Testing NAD83(2011) + NAVD88 height -> NAD83(2011) : projinfo -s EPSG:6349 -t EPSG:6319 --spatial-test intersects -o PROJ
+Candidate operations found: 2
+-------------------------------------
+Operation n°1:
+
+unknown id, Inverse of NAD83(2011) to NAVD88 height (1), 0.1 m, USA - CONUS - onshore
+
+PROJ string:
++proj=pipeline +step +proj=axisswap +order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=vgridshift +grids=g2012bu0.gtx +multiplier=1 +step +proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1
+
+-------------------------------------
+Operation n°2:
+
+unknown id, Inverse of NAD83(2011) to NAVD88 height (2), 0.2 m, USA - Alaska
+
+PROJ string:
++proj=pipeline +step +proj=axisswap +order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=vgridshift +grids=g2012ba0.gtx +multiplier=1 +step +proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1
+
diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp
index 112b46e3..e71ac716 100644
--- a/test/unit/test_operation.cpp
+++ b/test/unit/test_operation.cpp
@@ -4507,8 +4507,7 @@ TEST(operation, geogCRS_to_geogCRS_noop) {
auto op = CoordinateOperationFactory::create()->createOperation(
GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4326);
ASSERT_TRUE(op != nullptr);
- EXPECT_EQ(op->nameStr(),
- "Ballpark geographic offset from WGS 84 to WGS 84");
+ EXPECT_EQ(op->nameStr(), "Null geographic offset from WGS 84 to WGS 84");
EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), "");
EXPECT_EQ(op->inverse()->nameStr(), op->nameStr());
}