diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2020-12-14 21:12:43 +0100 |
|---|---|---|
| committer | Even Rouault <even.rouault@spatialys.com> | 2020-12-14 21:12:43 +0100 |
| commit | 95340fb1d1011d6864d82b60194eb77df9e9e38f (patch) | |
| tree | 30e2d3909d33c04f7966f996f90c971cd7023ade | |
| parent | bfc54bbac8550e18d352468032ad0b3d5ca2d328 (diff) | |
| download | PROJ-95340fb1d1011d6864d82b60194eb77df9e9e38f.tar.gz PROJ-95340fb1d1011d6864d82b60194eb77df9e9e38f.zip | |
createOperations(): fix inconsistent chaining exception when transforming from BoundCRS of projected CRS based on NTF Paris to BoundCRS of geog CRS NTF Paris. Fixes https://github.com/OSGeo/gdal/issues/3273
| -rw-r--r-- | src/iso19111/crs.cpp | 3 | ||||
| -rw-r--r-- | src/iso19111/io.cpp | 3 | ||||
| -rw-r--r-- | src/iso19111/operation/concatenatedoperation.cpp | 35 | ||||
| -rw-r--r-- | src/iso19111/operation/coordinateoperationfactory.cpp | 53 | ||||
| -rw-r--r-- | test/unit/test_operationfactory.cpp | 39 |
5 files changed, 94 insertions, 39 deletions
diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index 573dd6db..9d58f733 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -5188,7 +5188,8 @@ BoundCRSNNPtr BoundCRS::createFromNadgrids(const CRSNNPtr &baseCRSIn, " (with Greenwich prime meridian)"), sourceGeographicCRS->datumNonNull(nullptr)->ellipsoid(), util::optional<std::string>(), datum::PrimeMeridian::GREENWICH), - sourceGeographicCRS->coordinateSystem()); + cs::EllipsoidalCS::createLatitudeLongitude( + common::UnitOfMeasure::DEGREE)); } std::string transformationName = transformationSourceCRS->nameStr(); transformationName += " to WGS84"; diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index 86ece49d..dc51c5d9 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -4238,7 +4238,8 @@ createBoundCRSSourceTransformationCRS(const crs::CRSPtr &sourceCRS, sourceGeographicCRS->datum()->ellipsoid(), util::optional<std::string>(), datum::PrimeMeridian::GREENWICH), - sourceGeographicCRS->coordinateSystem()) + cs::EllipsoidalCS::createLatitudeLongitude( + common::UnitOfMeasure::DEGREE)) .as_nullable(); } } else { diff --git a/src/iso19111/operation/concatenatedoperation.cpp b/src/iso19111/operation/concatenatedoperation.cpp index 1c65a24b..ce4b015a 100644 --- a/src/iso19111/operation/concatenatedoperation.cpp +++ b/src/iso19111/operation/concatenatedoperation.cpp @@ -446,6 +446,41 @@ CoordinateOperationNNPtr ConcatenatedOperation::createComputeMetadata( flattenOps.emplace_back(subOp); } } + + // Remove consecutive inverse operations + if (flattenOps.size() > 2) { + std::vector<size_t> indices; + for (size_t i = 0; i < flattenOps.size(); ++i) + indices.push_back(i); + while (true) { + bool bHasChanged = false; + for (size_t i = 0; i + 1 < indices.size(); ++i) { + if (flattenOps[indices[i]]->_isEquivalentTo( + flattenOps[indices[i + 1]]->inverse().get(), + util::IComparable::Criterion::EQUIVALENT) && + flattenOps[indices[i]]->sourceCRS()->_isEquivalentTo( + flattenOps[indices[i + 1]]->targetCRS().get(), + util::IComparable::Criterion::EQUIVALENT)) { + indices.erase(indices.begin() + i, indices.begin() + i + 2); + bHasChanged = true; + break; + } + } + // We bail out if indices.size() == 2, because potentially + // the last 2 remaining ones could auto-cancel, and we would have + // to have a special case for that (and this happens in practice). + if (!bHasChanged || indices.size() <= 2) + break; + } + if (indices.size() < flattenOps.size()) { + std::vector<CoordinateOperationNNPtr> flattenOpsNew; + for (size_t i = 0; i < indices.size(); ++i) { + flattenOpsNew.emplace_back(flattenOps[indices[i]]); + } + flattenOps = std::move(flattenOpsNew); + } + } + if (flattenOps.size() == 1) { return flattenOps[0]; } diff --git a/src/iso19111/operation/coordinateoperationfactory.cpp b/src/iso19111/operation/coordinateoperationfactory.cpp index 1e919416..b8a9bcdf 100644 --- a/src/iso19111/operation/coordinateoperationfactory.cpp +++ b/src/iso19111/operation/coordinateoperationfactory.cpp @@ -4255,47 +4255,26 @@ void CoordinateOperationFactory::Private::createOperationsBoundToBound( auto hubSrcGeog = dynamic_cast<const crs::GeographicCRS *>(hubSrc.get()); const auto &hubDst = boundDst->hubCRS(); auto hubDstGeog = dynamic_cast<const crs::GeographicCRS *>(hubDst.get()); - auto geogCRSOfBaseOfBoundSrc = boundSrc->baseCRS()->extractGeographicCRS(); - auto geogCRSOfBaseOfBoundDst = boundDst->baseCRS()->extractGeographicCRS(); if (hubSrcGeog && hubDstGeog && hubSrcGeog->_isEquivalentTo(hubDstGeog, - util::IComparable::Criterion::EQUIVALENT) && - geogCRSOfBaseOfBoundSrc && geogCRSOfBaseOfBoundDst) { - const bool firstIsNoOp = geogCRSOfBaseOfBoundSrc->_isEquivalentTo( - boundSrc->baseCRS().get(), - util::IComparable::Criterion::EQUIVALENT); - const bool lastIsNoOp = geogCRSOfBaseOfBoundDst->_isEquivalentTo( - boundDst->baseCRS().get(), - util::IComparable::Criterion::EQUIVALENT); - auto opsFirst = createOperations( - boundSrc->baseCRS(), NN_NO_CHECK(geogCRSOfBaseOfBoundSrc), context); - auto opsLast = createOperations(NN_NO_CHECK(geogCRSOfBaseOfBoundDst), - boundDst->baseCRS(), context); - if (!opsFirst.empty() && !opsLast.empty()) { - const auto &opSecond = boundSrc->transformation(); - auto opThird = boundDst->transformation()->inverse(); - for (const auto &opFirst : opsFirst) { - for (const auto &opLast : opsLast) { - try { - std::vector<CoordinateOperationNNPtr> ops; - if (!firstIsNoOp) { - ops.push_back(opFirst); - } - ops.push_back(opSecond); - ops.push_back(opThird); - if (!lastIsNoOp) { - ops.push_back(opLast); - } - res.emplace_back( - ConcatenatedOperation::createComputeMetadata( - ops, disallowEmptyIntersection)); - } catch (const InvalidOperationEmptyIntersection &) { - } + util::IComparable::Criterion::EQUIVALENT)) { + auto opsFirst = createOperations(sourceCRS, hubSrc, context); + auto opsLast = createOperations(hubSrc, targetCRS, context); + for (const auto &opFirst : opsFirst) { + for (const auto &opLast : opsLast) { + try { + std::vector<CoordinateOperationNNPtr> ops; + ops.push_back(opFirst); + ops.push_back(opLast); + res.emplace_back( + ConcatenatedOperation::createComputeMetadata( + ops, disallowEmptyIntersection)); + } catch (const InvalidOperationEmptyIntersection &) { } } - if (!res.empty()) { - return; - } + } + if (!res.empty()) { + return; } } diff --git a/test/unit/test_operationfactory.cpp b/test/unit/test_operationfactory.cpp index b3f6f5cb..b71c3d66 100644 --- a/test/unit/test_operationfactory.cpp +++ b/test/unit/test_operationfactory.cpp @@ -2501,6 +2501,45 @@ TEST(operation, boundCRS_of_projCRS_towgs84_to_boundCRS_of_projCRS_nadgrids) { // --------------------------------------------------------------------------- +static CRSNNPtr buildCRSFromProjStrThroughWKT(const std::string &projStr) { + auto crsFromProj = nn_dynamic_pointer_cast<CRS>( + PROJStringParser().createFromPROJString(projStr)); + if (crsFromProj == nullptr) { + throw "crsFromProj == nullptr"; + } + auto crsFromWkt = nn_dynamic_pointer_cast<CRS>( + WKTParser().createFromWKT(crsFromProj->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2019).get()))); + if (crsFromWkt == nullptr) { + throw "crsFromWkt == nullptr"; + } + return NN_NO_CHECK(crsFromWkt); +} + +TEST(operation, + boundCRS_to_boundCRS_with_base_geog_crs_different_from_source_of_transf) { + + auto src = buildCRSFromProjStrThroughWKT( + "+proj=lcc +lat_1=49 +lat_0=49 +lon_0=0 +k_0=0.999877499 +x_0=600000 " + "+y_0=200000 +ellps=clrk80ign +pm=paris +towgs84=-168,-60,320,0,0,0,0 " + "+units=m +no_defs +type=crs"); + auto dst = buildCRSFromProjStrThroughWKT( + "+proj=longlat +ellps=clrk80ign +pm=paris " + "+towgs84=-168,-60,320,0,0,0,0 +no_defs +type=crs"); + + auto op = CoordinateOperationFactory::create()->createOperation(src, dst); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +inv +proj=lcc +lat_1=49 +lat_0=49 +lon_0=0 " + "+k_0=0.999877499 +x_0=600000 +y_0=200000 +ellps=clrk80ign " + "+pm=paris " + "+step +proj=longlat +ellps=clrk80ign +pm=paris " + "+step +proj=unitconvert +xy_in=rad +xy_out=deg"); +} + +// --------------------------------------------------------------------------- + TEST(operation, boundCRS_with_basecrs_with_extent_to_geogCRS) { auto wkt = |
