aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/iso19111/coordinateoperation.cpp39
-rw-r--r--test/unit/test_operation.cpp82
2 files changed, 108 insertions, 13 deletions
diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp
index f429c328..fef13e41 100644
--- a/src/iso19111/coordinateoperation.cpp
+++ b/src/iso19111/coordinateoperation.cpp
@@ -585,6 +585,7 @@ struct CoordinateOperation::Private {
util::optional<common::DataEpoch> sourceCoordinateEpoch_{};
util::optional<common::DataEpoch> targetCoordinateEpoch_{};
bool hasBallparkTransformation_ = false;
+ bool use3DHelmert_ = false;
// do not set this for a ProjectedCRS.definingConversion
struct CRSStrongRef {
@@ -9057,16 +9058,16 @@ void Transformation::_exportToPROJString(
double z =
parameterValueNumericAsSI(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION);
- bool addPushPopV3 =
- (methodEPSGCode ==
- EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_2D ||
- methodEPSGCode ==
- EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOGRAPHIC_2D ||
- methodEPSGCode == EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D ||
- methodEPSGCode ==
- EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOGRAPHIC_2D ||
- methodEPSGCode ==
- EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_2D);
+ auto sourceCRSGeog =
+ dynamic_cast<const crs::GeographicCRS *>(sourceCRS().get());
+ auto targetCRSGeog =
+ dynamic_cast<const crs::GeographicCRS *>(targetCRS().get());
+ const bool addPushPopV3 =
+ !CoordinateOperation::getPrivate()->use3DHelmert_ &&
+ ((sourceCRSGeog &&
+ sourceCRSGeog->coordinateSystem()->axisList().size() == 2) ||
+ (targetCRSGeog &&
+ targetCRSGeog->coordinateSystem()->axisList().size() == 2));
setupPROJGeodeticSourceCRS(formatter, sourceCRS(), addPushPopV3,
"Helmert");
@@ -12722,6 +12723,10 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot(
const auto candidatesDstGeod(findCandidateGeodCRSForDatum(
authFactory, geodDst, geodDst->datum().get()));
+ const bool sourceAndTargetAre3D =
+ geodSrc->coordinateSystem()->axisList().size() == 3 &&
+ geodDst->coordinateSystem()->axisList().size() == 3;
+
auto createTransformations = [&](const crs::CRSNNPtr &candidateSrcGeod,
const crs::CRSNNPtr &candidateDstGeod,
const CoordinateOperationNNPtr &opFirst,
@@ -12749,8 +12754,9 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot(
const bool isNullThird =
isNullTransformation(opsThird[0]->nameStr());
CoordinateOperationNNPtr opSecondCloned(
- (isNullFirst || isNullThird) ? opSecond->shallowClone()
- : opSecond);
+ (isNullFirst || isNullThird || sourceAndTargetAre3D)
+ ? opSecond->shallowClone()
+ : opSecond);
CoordinateOperation *invCOForward = nullptr;
if (isNullFirst || isNullThird) {
if (opSecondCloned->identifiers().size() == 1 &&
@@ -12778,6 +12784,15 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot(
}
}
}
+ if (sourceAndTargetAre3D) {
+ opSecondCloned->getPrivate()->use3DHelmert_ = true;
+ auto invCO = dynamic_cast<InverseCoordinateOperation *>(
+ opSecondCloned.get());
+ if (invCO) {
+ invCOForward = invCO->forwardOperation().get();
+ invCOForward->getPrivate()->use3DHelmert_ = true;
+ }
+ }
if (isNullFirst) {
auto oldTarget(NN_CHECK_ASSERT(opSecondCloned->targetCRS()));
setCRSs(opSecondCloned.get(), sourceCRS, oldTarget);
diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp
index 30d52806..63ed97e7 100644
--- a/test/unit/test_operation.cpp
+++ b/test/unit/test_operation.cpp
@@ -4666,6 +4666,85 @@ TEST(operation, geogCRS_to_geogCRS_context_EPSG_4240_Indian1975_to_EPSG_4326) {
// ---------------------------------------------------------------------------
+TEST(operation, geogCRS_to_geogCRS_context_helmert_geog3D_crs) {
+ auto authFactory =
+ AuthorityFactory::create(DatabaseContext::create(), "EPSG");
+ auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0);
+
+ auto list = CoordinateOperationFactory::create()->createOperations(
+ authFactory->createCoordinateReferenceSystem("4939"), // GDA94 3D
+ authFactory->createCoordinateReferenceSystem("7843"), // GDA2020 3D
+ ctxt);
+ ASSERT_EQ(list.size(), 1U);
+
+ // Check there is no push / pop of v_3
+ EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()),
+ "+proj=pipeline "
+ "+step +proj=axisswap +order=2,1 "
+ "+step +proj=unitconvert +xy_in=deg +z_in=m +xy_out=rad +z_out=m "
+ "+step +proj=cart +ellps=GRS80 "
+ "+step +proj=helmert +x=0.06155 +y=-0.01087 +z=-0.04019 "
+ "+rx=-0.0394924 +ry=-0.0327221 +rz=-0.0328979 +s=-0.009994 "
+ "+convention=coordinate_frame "
+ "+step +inv +proj=cart +ellps=GRS80 "
+ "+step +proj=unitconvert +xy_in=rad +z_in=m +xy_out=deg +z_out=m "
+ "+step +proj=axisswap +order=2,1");
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(operation, geogCRS_to_geogCRS_context_helmert_geocentric_3D) {
+ auto authFactory =
+ AuthorityFactory::create(DatabaseContext::create(), "EPSG");
+ auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0);
+
+ auto list = CoordinateOperationFactory::create()->createOperations(
+ // GDA94 geocentric
+ authFactory->createCoordinateReferenceSystem("4348"),
+ // GDA2020 geocentric
+ authFactory->createCoordinateReferenceSystem("7842"), ctxt);
+ ASSERT_EQ(list.size(), 1U);
+
+ // Check there is no push / pop of v_3
+ EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()),
+ "+proj=helmert +x=0.06155 +y=-0.01087 +z=-0.04019 "
+ "+rx=-0.0394924 +ry=-0.0327221 +rz=-0.0328979 +s=-0.009994 "
+ "+convention=coordinate_frame");
+ EXPECT_EQ(list[0]->inverse()->exportToPROJString(
+ PROJStringFormatter::create().get()),
+ "+proj=pipeline "
+ "+step +inv +proj=helmert +x=0.06155 +y=-0.01087 +z=-0.04019 "
+ "+rx=-0.0394924 +ry=-0.0327221 +rz=-0.0328979 +s=-0.009994 "
+ "+convention=coordinate_frame");
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(operation, geogCRS_to_geogCRS_context_helmert_geog3D_to_geocentirc) {
+ auto authFactory =
+ AuthorityFactory::create(DatabaseContext::create(), "EPSG");
+ auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0);
+
+ auto list = CoordinateOperationFactory::create()->createOperations(
+ // GDA94 3D
+ authFactory->createCoordinateReferenceSystem("4939"),
+ // GDA2020 geocentric
+ authFactory->createCoordinateReferenceSystem("7842"), ctxt);
+ ASSERT_EQ(list.size(), 1U);
+
+ // Check there is no push / pop of v_3
+ EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()),
+ "+proj=pipeline "
+ "+step +proj=axisswap +order=2,1 "
+ "+step +proj=unitconvert +xy_in=deg +z_in=m +xy_out=rad +z_out=m "
+ "+step +proj=cart +ellps=GRS80 "
+ "+step +proj=helmert +x=0.06155 +y=-0.01087 +z=-0.04019 "
+ "+rx=-0.0394924 +ry=-0.0327221 +rz=-0.0328979 +s=-0.009994 "
+ "+convention=coordinate_frame");
+}
+
+// ---------------------------------------------------------------------------
+
TEST(operation, vertCRS_to_geogCRS_context) {
auto authFactory =
AuthorityFactory::create(DatabaseContext::create(), "EPSG");
@@ -5784,7 +5863,8 @@ TEST(operation, geogCRS_3D_to_projCRS_with_2D_geocentric_translation) {
"+proj=pipeline "
"+step +proj=axisswap +order=2,1 "
"+step +proj=unitconvert +xy_in=deg +z_in=m +xy_out=rad +z_out=m "
- "+step +proj=push +v_3 " // this is what we check
+ "+step +proj=push +v_3 " // this is what we check. Due to the
+ // target system being 2D only
"+step +proj=cart +ellps=WGS84 "
"+step +proj=helmert +x=104 +y=-167 +z=38 "
"+step +inv +proj=cart +ellps=intl "