aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2019-03-16 23:11:25 +0100
committerEven Rouault <even.rouault@spatialys.com>2019-03-16 23:11:25 +0100
commitb5fb5f070793c29bb9b60faec9d221b25e7017d4 (patch)
tree506d1e29db44b068d20672a42642c6571121f0a1
parent70c9d0f75c9ecbb6f1ae9b5c7cefbe3a6fdbd5b9 (diff)
downloadPROJ-b5fb5f070793c29bb9b60faec9d221b25e7017d4.tar.gz
PROJ-b5fb5f070793c29bb9b60faec9d221b25e7017d4.zip
createOperations(): fix nadgrids -> nadgrids+geoidgrids
-rw-r--r--src/iso19111/coordinateoperation.cpp83
-rw-r--r--test/unit/test_operation.cpp30
2 files changed, 113 insertions, 0 deletions
diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp
index c1b3704c..7ee20758 100644
--- a/src/iso19111/coordinateoperation.cpp
+++ b/src/iso19111/coordinateoperation.cpp
@@ -12054,6 +12054,89 @@ CoordinateOperationFactory::Private::createOperations(
}
}
+ // '+proj=longlat +ellps=GRS67 +nadgrids=@foo.gsb +type=crs' to
+ // '+proj=longlat +ellps=GRS80 +nadgrids=@bar.gsb +geoidgrids=@bar.gtx
+ // +type=crs'
+ if (boundSrc && compoundDst) {
+ const auto &componentsDst = compoundDst->componentReferenceSystems();
+ if (!componentsDst.empty()) {
+ auto compDst0BoundCrs =
+ dynamic_cast<crs::BoundCRS *>(componentsDst[0].get());
+ if (compDst0BoundCrs) {
+ auto boundSrcHubAsGeogCRS = dynamic_cast<crs::GeographicCRS *>(
+ boundSrc->hubCRS().get());
+ auto compDst0BoundCrsHubAsGeogCRS =
+ dynamic_cast<crs::GeographicCRS *>(
+ compDst0BoundCrs->hubCRS().get());
+ if (boundSrcHubAsGeogCRS && compDst0BoundCrsHubAsGeogCRS) {
+ const auto &boundSrcHubAsGeogCRSDatum =
+ boundSrcHubAsGeogCRS->datum();
+ const auto &compDst0BoundCrsHubAsGeogCRSDatum =
+ compDst0BoundCrsHubAsGeogCRS->datum();
+ if (boundSrcHubAsGeogCRSDatum &&
+ compDst0BoundCrsHubAsGeogCRSDatum &&
+ boundSrcHubAsGeogCRSDatum->_isEquivalentTo(
+ compDst0BoundCrsHubAsGeogCRSDatum.get())) {
+ auto cs = cs::EllipsoidalCS::
+ createLatitudeLongitudeEllipsoidalHeight(
+ common::UnitOfMeasure::DEGREE,
+ common::UnitOfMeasure::METRE);
+ auto intermGeog3DCRS = util::nn_static_pointer_cast<
+ crs::CRS>(crs::GeographicCRS::create(
+ util::PropertyMap()
+ .set(common::IdentifiedObject::NAME_KEY,
+ boundSrcHubAsGeogCRS->nameStr())
+ .set(
+ common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY,
+ metadata::Extent::WORLD),
+ NN_NO_CHECK(boundSrcHubAsGeogCRSDatum), cs));
+ auto sourceToGeog3DOps = createOperations(
+ sourceCRS, intermGeog3DCRS, context);
+ auto geog3DToTargetOps = createOperations(
+ intermGeog3DCRS, targetCRS, context);
+ for (const auto &opSrc : sourceToGeog3DOps) {
+ for (const auto &opDst : geog3DToTargetOps) {
+ if (opSrc->targetCRS() && opDst->sourceCRS() &&
+ !opSrc->targetCRS()->_isEquivalentTo(
+ opDst->sourceCRS().get())) {
+ // Shouldn't happen normally, but typically
+ // one of them can be 2D and the other 3D
+ // due to above createOperations() not
+ // exactly setting the expected source and
+ // target CRS.
+ // So create an adapter operation...
+ auto intermOps = createOperations(
+ NN_NO_CHECK(opSrc->targetCRS()),
+ NN_NO_CHECK(opDst->sourceCRS()),
+ context);
+ if (!intermOps.empty()) {
+ res.emplace_back(
+ ConcatenatedOperation::
+ createComputeMetadata(
+ {opSrc, intermOps.front(),
+ opDst},
+ !allowEmptyIntersection));
+ }
+ } else {
+ res.emplace_back(
+ ConcatenatedOperation::
+ createComputeMetadata(
+ {opSrc, opDst},
+ !allowEmptyIntersection));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // reverse of previous case
+ if (boundDst && compoundSrc) {
+ return applyInverse(createOperations(targetCRS, sourceCRS, context));
+ }
+
return res;
}
//! @endcond
diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp
index c7811b02..9111b862 100644
--- a/test/unit/test_operation.cpp
+++ b/test/unit/test_operation.cpp
@@ -6406,6 +6406,36 @@ TEST(operation, compoundCRS_to_geogCRS_3D_context) {
// ---------------------------------------------------------------------------
+TEST(operation, boundCRS_to_compoundCRS) {
+ auto objSrc = PROJStringParser().createFromPROJString(
+ "+proj=longlat +ellps=GRS67 +nadgrids=@foo.gsb +type=crs");
+ auto src = nn_dynamic_pointer_cast<CRS>(objSrc);
+ ASSERT_TRUE(src != nullptr);
+ auto objDst = PROJStringParser().createFromPROJString(
+ "+proj=longlat +ellps=GRS80 +nadgrids=@bar.gsb +geoidgrids=@bar.gtx "
+ "+type=crs");
+ auto dst = nn_dynamic_pointer_cast<CRS>(objDst);
+ ASSERT_TRUE(dst != nullptr);
+
+ auto op = CoordinateOperationFactory::create()->createOperation(
+ NN_CHECK_ASSERT(src), NN_CHECK_ASSERT(dst));
+ ASSERT_TRUE(op != nullptr);
+ EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()),
+ "+proj=pipeline "
+ "+step +proj=unitconvert +xy_in=deg +xy_out=rad "
+ "+step +proj=hgridshift +grids=@foo.gsb "
+ "+step +inv +proj=vgridshift +grids=@bar.gtx +multiplier=1 "
+ "+step +inv +proj=hgridshift +grids=@bar.gsb "
+ "+step +proj=unitconvert +xy_in=rad +xy_out=deg");
+
+ auto opInverse = CoordinateOperationFactory::create()->createOperation(
+ NN_CHECK_ASSERT(dst), NN_CHECK_ASSERT(src));
+ ASSERT_TRUE(opInverse != nullptr);
+ EXPECT_TRUE(opInverse->inverse()->_isEquivalentTo(op.get()));
+}
+
+// ---------------------------------------------------------------------------
+
TEST(operation, IGNF_LAMB1_TO_EPSG_4326) {
auto authFactory =
AuthorityFactory::create(DatabaseContext::create(), std::string());