diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2020-04-04 12:23:07 +0200 |
|---|---|---|
| committer | Kristian Evers <kristianevers@gmail.com> | 2020-04-04 19:23:22 +0200 |
| commit | 8aab6fd948a5e9c16a352fa34a4e8cde54d5d924 (patch) | |
| tree | 6b258381e0e197f99bb5a9360408f508dd234eb5 | |
| parent | 670eab68f2345b2a77bfac8a109588770bceb740 (diff) | |
| download | PROJ-8aab6fd948a5e9c16a352fa34a4e8cde54d5d924.tar.gz PROJ-8aab6fd948a5e9c16a352fa34a4e8cde54d5d924.zip | |
createOperations(): improve results of compoundCRS to compoundCRS case (fixes #2115)
| -rw-r--r-- | docs/source/operations/operations_computation.rst | 93 | ||||
| -rw-r--r-- | src/iso19111/coordinateoperation.cpp | 196 | ||||
| -rw-r--r-- | test/unit/test_operation.cpp | 79 |
3 files changed, 265 insertions, 103 deletions
diff --git a/docs/source/operations/operations_computation.rst b/docs/source/operations/operations_computation.rst index ef84cfb2..1813a010 100644 --- a/docs/source/operations/operations_computation.rst +++ b/docs/source/operations/operations_computation.rst @@ -660,46 +660,73 @@ CompoundCRS to CompoundCRS --------------------------------------------------------------------------------- There is some similarity with the previous paragraph. We first research the -vertical transformations between the vertical CRS. If such tranformation has -a registered interpolation geographic CRS, then it is used. Otherwise we fallback -to the geographic CRS of the source CRS. +vertical transformations between the two vertical CRS. -Finally, a 3-level loop to create the final set of operations chaining together: +1. If there is such a tranformation, be it direct, or if both vertical CRS + relate to a common intermediate CRS. + If it has a registered interpolation geographic CRS, then it is used. + Otherwise we fallback to the geographic CRS of the source CRS. -- the horizontal transformation from the source CRS to the interpolation CRS -- the vertical transformation -- the horizontal transformation from the interpolation CRS to the target CRS. + Finally, a 3-level loop to create the final set of operations chaining together: -This is implemented by the ``createOperationsCompoundToGeog`` method + - the horizontal transformation from the source CRS to the interpolation CRS + - the vertical transformation + - the horizontal transformation from the interpolation CRS to the target CRS. -Example: + Example: -.. code-block:: shell + .. code-block:: shell + + $ projinfo -s "NAD27 + NGVD29 height (ftUS)" -t "NAD83 + NAVD88 height" --spatial-test intersects --summary + + Candidate operations found: 20 + unknown id, NGVD29 height (ftUS) to NAVD88 height (3) + NAD27 to NAD83 (1), 0.17 m, USA - CONUS east of 89°W - onshore + unknown id, NGVD29 height (ftUS) to NAVD88 height (2) + NAD27 to NAD83 (1), 0.17 m, USA - CONUS 89°W-107°W - onshore + unknown id, NGVD29 height (ftUS) to NAVD88 height (1) + NAD27 to NAD83 (1), 0.17 m, USA - CONUS west of 107°W - onshore + unknown id, NGVD29 height (ftUS) to NAVD88 height (3) + NAD27 to NAD83 (3), 1.02 m, unknown domain of validity + unknown id, NGVD29 height (ftUS) to NAVD88 height (2) + NAD27 to NAD83 (3), 1.02 m, unknown domain of validity + unknown id, NGVD29 height (ftUS) to NAVD88 height (1) + NAD27 to NAD83 (3), 1.02 m, unknown domain of validity + unknown id, NGVD29 height (ftUS) to NAVD88 height (3) + NAD27 to NAD83 (5), 1.02 m, unknown domain of validity, at least one grid missing + unknown id, NGVD29 height (ftUS) to NAVD88 height (3) + NAD27 to NAD83 (6), 1.52 m, unknown domain of validity, at least one grid missing + unknown id, NGVD29 height (ftUS) to NAVD88 height (2) + NAD27 to NAD83 (9), 1.52 m, unknown domain of validity, at least one grid missing + unknown id, NGVD29 height (ftUS) to NAVD88 height (1) + NAD27 to NAD83 (9), 1.52 m, unknown domain of validity, at least one grid missing + unknown id, NGVD29 height (ftUS) to NAVD88 height (3) + Ballpark geographic offset from NAD27 to NAD83, unknown accuracy, USA - CONUS east of 89°W - onshore, has ballpark transformation + unknown id, NGVD29 height (ftUS) to NAVD88 height (2) + Ballpark geographic offset from NAD27 to NAD83, unknown accuracy, USA - CONUS 89°W-107°W - onshore, has ballpark transformation + unknown id, NGVD29 height (ftUS) to NAVD88 height (1) + Ballpark geographic offset from NAD27 to NAD83, unknown accuracy, USA - CONUS west of 107°W - onshore, has ballpark transformation + unknown id, Transformation from NGVD29 height (ftUS) to NAVD88 height (ballpark vertical transformation) + NAD27 to NAD83 (1), unknown accuracy, USA - CONUS including EEZ, has ballpark transformation + unknown id, Transformation from NGVD29 height (ftUS) to NAVD88 height (ballpark vertical transformation) + NAD27 to NAD83 (3), unknown accuracy, Canada, has ballpark transformation + unknown id, Transformation from NGVD29 height (ftUS) to NAVD88 height (ballpark vertical transformation) + NAD27 to NAD83 (4), unknown accuracy, Canada - NAD27, has ballpark transformation + unknown id, Transformation from NGVD29 height (ftUS) to NAVD88 height (ballpark vertical transformation) + NAD27 to NAD83 (5), unknown accuracy, Canada - Quebec, has ballpark transformation, at least one grid missing + unknown id, Transformation from NGVD29 height (ftUS) to NAVD88 height (ballpark vertical transformation) + NAD27 to NAD83 (6), unknown accuracy, Canada - Quebec, has ballpark transformation, at least one grid missing + unknown id, Transformation from NGVD29 height (ftUS) to NAVD88 height (ballpark vertical transformation) + NAD27 to NAD83 (9), unknown accuracy, Canada - Saskatchewan, has ballpark transformation, at least one grid missing + unknown id, Transformation from NGVD29 height (ftUS) to NAVD88 height (ballpark vertical transformation) + Ballpark geographic offset from NAD27 to NAD83, unknown accuracy, World, has ballpark transformation + + +2. Otherwise, when there is no such transformation, we decompose into 3 steps: + + - transform from the source CRS to the geographic 3D CRS corresponding to it + - transform from the geographic 3D CRS corresponding to the source CRS to the + geographic 3D CRS corresponding to the target CRS + - transform from the geographic 3D CRS corresponding to the target CRS to the + target CRS. + + Example: + + .. code-block:: shell + + $ projinfo -s "WGS 84 + EGM96 height" -t "ETRS89 + Belfast height" --spatial-test intersects --summary + + Candidate operations found: 7 + unknown id, Inverse of WGS 84 to EGM96 height (1) + Inverse of ETRS89 to WGS 84 (1) + ETRS89 to Belfast height (2), 2.014 m, UK - Northern Ireland - onshore + unknown id, Inverse of WGS 84 to EGM96 height (1) + Inverse of ETRS89 to WGS 84 (1) + ETRS89 to Belfast height (1), 2.03 m, UK - Northern Ireland - onshore, at least one grid missing + unknown id, Inverse of WGS 84 to EGM96 height (1) + Null geographic offset from WGS 84 (geog3D) to WGS 84 (geog2D) + Inverse of OSGB 1936 to WGS 84 (4) + OSGB 1936 to ETRS89 (2) + Null geographic offset from ETRS89 (geog2D) to ETRS89 (geog3D) + ETRS89 to Belfast height (2), 19.044 m, unknown domain of validity + unknown id, Inverse of WGS 84 to EGM96 height (1) + Null geographic offset from WGS 84 (geog3D) to WGS 84 (geog2D) + Inverse of OSGB 1936 to WGS 84 (2) + OSGB 1936 to ETRS89 (2) + Null geographic offset from ETRS89 (geog2D) to ETRS89 (geog3D) + ETRS89 to Belfast height (2), 11.044 m, unknown domain of validity + unknown id, Inverse of WGS 84 to EGM96 height (1) + Null geographic offset from WGS 84 (geog3D) to WGS 84 (geog2D) + Inverse of TM75 to WGS 84 (2) + TM75 to ETRS89 (3) + Null geographic offset from ETRS89 (geog2D) to ETRS89 (geog3D) + ETRS89 to Belfast height (2), 2.424 m, UK - Northern Ireland - onshore, at least one grid missing + unknown id, Inverse of WGS 84 to EGM96 height (1) + Null geographic offset from WGS 84 (geog3D) to WGS 84 (geog2D) + Inverse of TM75 to WGS 84 (2) + TM75 to ETRS89 (3) + Null geographic offset from ETRS89 (geog2D) to ETRS89 (geog3D) + ETRS89 to Belfast height (1), 2.44 m, UK - Northern Ireland - onshore, at least one grid missing + unknown id, Inverse of WGS 84 to EGM96 height (1) + Null geographic offset from WGS 84 (geog3D) to WGS 84 (geog2D) + Inverse of OSGB 1936 to WGS 84 (4) + OSGB 1936 to ETRS89 (2) + Null geographic offset from ETRS89 (geog2D) to ETRS89 (geog3D) + ETRS89 to Belfast height (1), 19.06 m, unknown domain of validity, at least one grid missing - $ projinfo -s "NAD27 + NGVD29 height (ftUS)" -t "NAD83 + NAVD88 height" --spatial-test intersects --summary - - Candidate operations found: 20 - unknown id, NGVD29 height (ftUS) to NAVD88 height (3) + NAD27 to NAD83 (1), 0.17 m, USA - CONUS east of 89°W - onshore - unknown id, NGVD29 height (ftUS) to NAVD88 height (2) + NAD27 to NAD83 (1), 0.17 m, USA - CONUS 89°W-107°W - onshore - unknown id, NGVD29 height (ftUS) to NAVD88 height (1) + NAD27 to NAD83 (1), 0.17 m, USA - CONUS west of 107°W - onshore - unknown id, NGVD29 height (ftUS) to NAVD88 height (3) + NAD27 to NAD83 (3), 1.02 m, unknown domain of validity - unknown id, NGVD29 height (ftUS) to NAVD88 height (2) + NAD27 to NAD83 (3), 1.02 m, unknown domain of validity - unknown id, NGVD29 height (ftUS) to NAVD88 height (1) + NAD27 to NAD83 (3), 1.02 m, unknown domain of validity - unknown id, NGVD29 height (ftUS) to NAVD88 height (3) + NAD27 to NAD83 (5), 1.02 m, unknown domain of validity, at least one grid missing - unknown id, NGVD29 height (ftUS) to NAVD88 height (3) + NAD27 to NAD83 (6), 1.52 m, unknown domain of validity, at least one grid missing - unknown id, NGVD29 height (ftUS) to NAVD88 height (2) + NAD27 to NAD83 (9), 1.52 m, unknown domain of validity, at least one grid missing - unknown id, NGVD29 height (ftUS) to NAVD88 height (1) + NAD27 to NAD83 (9), 1.52 m, unknown domain of validity, at least one grid missing - unknown id, NGVD29 height (ftUS) to NAVD88 height (3) + Ballpark geographic offset from NAD27 to NAD83, unknown accuracy, USA - CONUS east of 89°W - onshore, has ballpark transformation - unknown id, NGVD29 height (ftUS) to NAVD88 height (2) + Ballpark geographic offset from NAD27 to NAD83, unknown accuracy, USA - CONUS 89°W-107°W - onshore, has ballpark transformation - unknown id, NGVD29 height (ftUS) to NAVD88 height (1) + Ballpark geographic offset from NAD27 to NAD83, unknown accuracy, USA - CONUS west of 107°W - onshore, has ballpark transformation - unknown id, Transformation from NGVD29 height (ftUS) to NAVD88 height (ballpark vertical transformation) + NAD27 to NAD83 (1), unknown accuracy, USA - CONUS including EEZ, has ballpark transformation - unknown id, Transformation from NGVD29 height (ftUS) to NAVD88 height (ballpark vertical transformation) + NAD27 to NAD83 (3), unknown accuracy, Canada, has ballpark transformation - unknown id, Transformation from NGVD29 height (ftUS) to NAVD88 height (ballpark vertical transformation) + NAD27 to NAD83 (4), unknown accuracy, Canada - NAD27, has ballpark transformation - unknown id, Transformation from NGVD29 height (ftUS) to NAVD88 height (ballpark vertical transformation) + NAD27 to NAD83 (5), unknown accuracy, Canada - Quebec, has ballpark transformation, at least one grid missing - unknown id, Transformation from NGVD29 height (ftUS) to NAVD88 height (ballpark vertical transformation) + NAD27 to NAD83 (6), unknown accuracy, Canada - Quebec, has ballpark transformation, at least one grid missing - unknown id, Transformation from NGVD29 height (ftUS) to NAVD88 height (ballpark vertical transformation) + NAD27 to NAD83 (9), unknown accuracy, Canada - Saskatchewan, has ballpark transformation, at least one grid missing - unknown id, Transformation from NGVD29 height (ftUS) to NAVD88 height (ballpark vertical transformation) + Ballpark geographic offset from NAD27 to NAD83, unknown accuracy, World, has ballpark transformation +This is implemented by the ``createOperationsCompoundToCompound`` method When the source or target CRS is a BoundCRS --------------------------------------------------------------------------------- diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 267e9450..822d09ac 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -14754,84 +14754,144 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToCompound( const auto &componentsSrc = compoundSrc->componentReferenceSystems(); const auto &componentsDst = compoundDst->componentReferenceSystems(); - if (!componentsSrc.empty() && - componentsSrc.size() == componentsDst.size()) { - if (componentsSrc[0]->extractGeographicCRS() && - componentsDst[0]->extractGeographicCRS()) { - - std::vector<CoordinateOperationNNPtr> verticalTransforms; - if (componentsSrc.size() >= 2 && - componentsSrc[1]->extractVerticalCRS() && - componentsDst[1]->extractVerticalCRS()) { - if (!componentsSrc[1]->_isEquivalentTo( - componentsDst[1].get())) { - verticalTransforms = createOperations( - componentsSrc[1], componentsDst[1], context); - } - } + if (componentsSrc.empty() || componentsSrc.size() != componentsDst.size()) { + return; + } + const auto srcGeog = componentsSrc[0]->extractGeographicCRS(); + const auto dstGeog = componentsDst[0]->extractGeographicCRS(); + if (srcGeog == nullptr || dstGeog == nullptr) { + return; + } - for (const auto &verticalTransform : verticalTransforms) { - auto interpolationGeogCRS = - NN_NO_CHECK(componentsSrc[0]->extractGeographicCRS()); - auto transformationVerticalTransform = - dynamic_cast<const Transformation *>( - verticalTransform.get()); - if (transformationVerticalTransform) { - auto interpTransformCRS = - transformationVerticalTransform->interpolationCRS(); - if (interpTransformCRS) { - auto nn_interpTransformCRS = - NN_NO_CHECK(interpTransformCRS); - if (dynamic_cast<const crs::GeographicCRS *>( - nn_interpTransformCRS.get())) { - interpolationGeogCRS = NN_NO_CHECK( - util::nn_dynamic_pointer_cast< - crs::GeographicCRS>(nn_interpTransformCRS)); - } - } - } else { - auto compSrc0BoundCrs = - dynamic_cast<crs::BoundCRS *>(componentsSrc[0].get()); - auto compDst0BoundCrs = - dynamic_cast<crs::BoundCRS *>(componentsDst[0].get()); - if (compSrc0BoundCrs && compDst0BoundCrs && - dynamic_cast<crs::GeographicCRS *>( - compSrc0BoundCrs->hubCRS().get()) && - compSrc0BoundCrs->hubCRS()->_isEquivalentTo( - compDst0BoundCrs->hubCRS().get())) { - interpolationGeogCRS = NN_NO_CHECK( - util::nn_dynamic_pointer_cast<crs::GeographicCRS>( - compSrc0BoundCrs->hubCRS())); - } + std::vector<CoordinateOperationNNPtr> verticalTransforms; + if (componentsSrc.size() >= 2 && componentsSrc[1]->extractVerticalCRS() && + componentsDst[1]->extractVerticalCRS()) { + if (!componentsSrc[1]->_isEquivalentTo(componentsDst[1].get())) { + verticalTransforms = + createOperations(componentsSrc[1], componentsDst[1], context); + } + } + + // If we didn't find a non-ballbark transformation between + // the 2 vertical CRS, then try through intermediate geographic CRS + // For example + // WGS 84 + EGM96 --> ETRS89 + Belfast height where + // there is a geoid model for EGM96 referenced to WGS 84 + // and a geoid model for Belfast height referenced to ETRS89 + if (verticalTransforms.size() == 1 && + verticalTransforms.front()->hasBallparkTransformation()) { + auto dbContext = + context.context->getAuthorityFactory()->databaseContext(); + const auto intermGeogSrc = + srcGeog->promoteTo3D(std::string(), dbContext); + const bool intermGeogSrcIsSameAsIntermGeogDst = + srcGeog->_isEquivalentTo(dstGeog.get()); + const auto intermGeogDst = + intermGeogSrcIsSameAsIntermGeogDst + ? intermGeogSrc + : dstGeog->promoteTo3D(std::string(), dbContext); + const auto opsSrcToGeog = + createOperations(sourceCRS, intermGeogSrc, context); + const auto opsGeogToTarget = + createOperations(intermGeogDst, targetCRS, context); + const bool hasNonTrivalSrcTransf = + !opsSrcToGeog.empty() && + !opsSrcToGeog.front()->hasBallparkTransformation(); + const bool hasNonTrivialTargetTransf = + !opsGeogToTarget.empty() && + !opsGeogToTarget.front()->hasBallparkTransformation(); + if (hasNonTrivalSrcTransf && hasNonTrivialTargetTransf) { + const auto opsGeogSrcToGeogDst = + createOperations(intermGeogSrc, intermGeogDst, context); + for (const auto &op1 : opsSrcToGeog) { + if (op1->hasBallparkTransformation()) { + continue; } - auto opSrcCRSToGeogCRS = createOperations( - componentsSrc[0], interpolationGeogCRS, context); - auto opGeogCRStoDstCRS = createOperations( - interpolationGeogCRS, componentsDst[0], context); - for (const auto &opSrc : opSrcCRSToGeogCRS) { - for (const auto &opDst : opGeogCRStoDstCRS) { - + for (const auto &op2 : opsGeogSrcToGeogDst) { + for (const auto &op3 : opsGeogToTarget) { + if (op3->hasBallparkTransformation()) { + continue; + } try { - auto op = createHorizVerticalHorizPROJBased( - sourceCRS, targetCRS, opSrc, verticalTransform, - opDst, interpolationGeogCRS, true); - res.emplace_back(op); - } catch (const InvalidOperationEmptyIntersection &) { - } catch (const io::FormattingException &) { + res.emplace_back( + ConcatenatedOperation::createComputeMetadata( + intermGeogSrcIsSameAsIntermGeogDst + ? std::vector< + CoordinateOperationNNPtr>{op1, + op3} + : std::vector< + CoordinateOperationNNPtr>{op1, + op2, + op3}, + !allowEmptyIntersection)); + } catch (const std::exception &) { } } } } + } + if (!res.empty()) { + return; + } + } - if (verticalTransforms.empty()) { - auto resTmp = createOperations(componentsSrc[0], - componentsDst[0], context); - for (const auto &op : resTmp) { - auto opClone = op->shallowClone(); - setCRSs(opClone.get(), sourceCRS, targetCRS); - res.emplace_back(opClone); + for (const auto &verticalTransform : verticalTransforms) { + auto interpolationGeogCRS = NN_NO_CHECK(srcGeog); + auto transformationVerticalTransform = + dynamic_cast<const Transformation *>(verticalTransform.get()); + if (transformationVerticalTransform) { + auto interpTransformCRS = + transformationVerticalTransform->interpolationCRS(); + if (interpTransformCRS) { + auto nn_interpTransformCRS = NN_NO_CHECK(interpTransformCRS); + if (dynamic_cast<const crs::GeographicCRS *>( + nn_interpTransformCRS.get())) { + interpolationGeogCRS = NN_NO_CHECK( + util::nn_dynamic_pointer_cast<crs::GeographicCRS>( + nn_interpTransformCRS)); } } + } else { + auto compSrc0BoundCrs = + dynamic_cast<crs::BoundCRS *>(componentsSrc[0].get()); + auto compDst0BoundCrs = + dynamic_cast<crs::BoundCRS *>(componentsDst[0].get()); + if (compSrc0BoundCrs && compDst0BoundCrs && + dynamic_cast<crs::GeographicCRS *>( + compSrc0BoundCrs->hubCRS().get()) && + compSrc0BoundCrs->hubCRS()->_isEquivalentTo( + compDst0BoundCrs->hubCRS().get())) { + interpolationGeogCRS = NN_NO_CHECK( + util::nn_dynamic_pointer_cast<crs::GeographicCRS>( + compSrc0BoundCrs->hubCRS())); + } + } + auto opSrcCRSToGeogCRS = + createOperations(componentsSrc[0], interpolationGeogCRS, context); + auto opGeogCRStoDstCRS = + createOperations(interpolationGeogCRS, componentsDst[0], context); + for (const auto &opSrc : opSrcCRSToGeogCRS) { + for (const auto &opDst : opGeogCRStoDstCRS) { + + try { + auto op = createHorizVerticalHorizPROJBased( + sourceCRS, targetCRS, opSrc, verticalTransform, opDst, + interpolationGeogCRS, true); + res.emplace_back(op); + } catch (const InvalidOperationEmptyIntersection &) { + } catch (const io::FormattingException &) { + } + } + } + } + + if (verticalTransforms.empty()) { + auto resTmp = + createOperations(componentsSrc[0], componentsDst[0], context); + for (const auto &op : resTmp) { + auto opClone = op->shallowClone(); + setCRSs(opClone.get(), sourceCRS, targetCRS); + res.emplace_back(opClone); } } } diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp index ad218899..99e05dd1 100644 --- a/test/unit/test_operation.cpp +++ b/test/unit/test_operation.cpp @@ -7357,11 +7357,11 @@ TEST(operation, compoundCRS_to_compoundCRS_context_helmert_noop) { ctxt->setSpatialCriterion( CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); // WGS84 + EGM96 - auto objSrc = createFromUserInput("EPSG:4326+3855", dbContext); + auto objSrc = createFromUserInput("EPSG:4326+5773", dbContext); auto srcCrs = nn_dynamic_pointer_cast<CompoundCRS>(objSrc); ASSERT_TRUE(srcCrs != nullptr); // ETRS89 + EGM96 - auto objDest = createFromUserInput("EPSG:4258+3855", dbContext); + auto objDest = createFromUserInput("EPSG:4258+5773", dbContext); auto destCrs = nn_dynamic_pointer_cast<CompoundCRS>(objDest); ASSERT_TRUE(destCrs != nullptr); auto list = CoordinateOperationFactory::create()->createOperations( @@ -7373,6 +7373,81 @@ TEST(operation, compoundCRS_to_compoundCRS_context_helmert_noop) { // --------------------------------------------------------------------------- +// EGM96 has a geoid model referenced to WGS84, and Belfast height has a +// geoid model referenced to ETRS89 +TEST(operation, compoundCRS_to_compoundCRS_WGS84_EGM96_to_ETRS89_Belfast) { + auto dbContext = DatabaseContext::create(); + auto authFactory = AuthorityFactory::create(dbContext, "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + ctxt->setGridAvailabilityUse( + CoordinateOperationContext::GridAvailabilityUse:: + IGNORE_GRID_AVAILABILITY); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + // WGS84 + EGM96 + auto objSrc = createFromUserInput("EPSG:4326+5773", dbContext); + auto srcCrs = nn_dynamic_pointer_cast<CompoundCRS>(objSrc); + ASSERT_TRUE(srcCrs != nullptr); + // ETRS89 + Belfast height + auto objDest = createFromUserInput("EPSG:4258+5732", dbContext); + auto destCrs = nn_dynamic_pointer_cast<CompoundCRS>(objDest); + ASSERT_TRUE(destCrs != nullptr); + auto list = CoordinateOperationFactory::create()->createOperations( + NN_NO_CHECK(srcCrs), NN_NO_CHECK(destCrs), ctxt); + ASSERT_GE(list.size(), 1U); + EXPECT_EQ(list[0]->nameStr(), "Inverse of WGS 84 to EGM96 height (1) + " + "Inverse of ETRS89 to WGS 84 (1) + " + "ETRS89 to Belfast height (2)"); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 " + "+step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +proj=vgridshift +grids=us_nga_egm96_15.tif +multiplier=1 " + "+step +inv +proj=vgridshift +grids=uk_os_OSGM15_Belfast.tif " + "+multiplier=1 +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg " + "+step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +// Variant of above where source intermediate geog3D CRS == target intermediate +// geog3D CRS +TEST(operation, compoundCRS_to_compoundCRS_WGS84_EGM96_to_WGS84_Belfast) { + auto dbContext = DatabaseContext::create(); + auto authFactory = AuthorityFactory::create(dbContext, "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + ctxt->setGridAvailabilityUse( + CoordinateOperationContext::GridAvailabilityUse:: + IGNORE_GRID_AVAILABILITY); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + // WGS84 + EGM96 + auto objSrc = createFromUserInput("EPSG:4326+5773", dbContext); + auto srcCrs = nn_dynamic_pointer_cast<CompoundCRS>(objSrc); + ASSERT_TRUE(srcCrs != nullptr); + // WGS84 + Belfast height + auto objDest = createFromUserInput("EPSG:4326+5732", dbContext); + auto destCrs = nn_dynamic_pointer_cast<CompoundCRS>(objDest); + ASSERT_TRUE(destCrs != nullptr); + auto list = CoordinateOperationFactory::create()->createOperations( + NN_NO_CHECK(srcCrs), NN_NO_CHECK(destCrs), ctxt); + ASSERT_GE(list.size(), 1U); + EXPECT_EQ(list[0]->nameStr(), "Inverse of WGS 84 to EGM96 height (1) + " + "Inverse of ETRS89 to WGS 84 (1) + " + "ETRS89 to Belfast height (2) + " + "ETRS89 to WGS 84 (1)"); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 " + "+step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +proj=vgridshift +grids=us_nga_egm96_15.tif +multiplier=1 " + "+step +inv +proj=vgridshift +grids=uk_os_OSGM15_Belfast.tif " + "+multiplier=1 +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg " + "+step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + TEST(operation, vertCRS_to_vertCRS) { auto vertcrs_m_obj = PROJStringParser().createFromPROJString("+vunits=m"); |
