diff options
| -rw-r--r-- | include/proj/coordinateoperation.hpp | 15 | ||||
| -rw-r--r-- | include/proj/internal/coordinateoperation_internal.hpp | 18 | ||||
| -rw-r--r-- | src/iso19111/coordinateoperation.cpp | 326 | ||||
| -rw-r--r-- | test/unit/test_factory.cpp | 12 | ||||
| -rw-r--r-- | test/unit/test_operation.cpp | 61 |
5 files changed, 385 insertions, 47 deletions
diff --git a/include/proj/coordinateoperation.hpp b/include/proj/coordinateoperation.hpp index 9e7812c7..994cbab2 100644 --- a/include/proj/coordinateoperation.hpp +++ b/include/proj/coordinateoperation.hpp @@ -154,6 +154,12 @@ class PROJ_GCC_DLL CoordinateOperation : public common::ObjectUsage, PROJ_DLL CoordinateOperationNNPtr normalizeForVisualization() const; + PROJ_PRIVATE : + //! @cond Doxygen_Suppress + PROJ_FOR_TEST CoordinateOperationNNPtr + shallowClone() const; + //! @endcond + protected: PROJ_INTERNAL CoordinateOperation(); PROJ_INTERNAL CoordinateOperation(const CoordinateOperation &other); @@ -179,6 +185,8 @@ class PROJ_GCC_DLL CoordinateOperation : public common::ObjectUsage, setProperties(const util::PropertyMap &properties); // throw(InvalidValueTypeException) + PROJ_INTERNAL virtual CoordinateOperationNNPtr _shallowClone() const = 0; + private: PROJ_OPAQUE_PRIVATE_DATA CoordinateOperation &operator=(const CoordinateOperation &other) = delete; @@ -1328,6 +1336,8 @@ class PROJ_GCC_DLL Conversion : public SingleOperation { PROJ_FRIEND(crs::ProjectedCRS); PROJ_INTERNAL bool addWKTExtensionNode(io::WKTFormatter *formatter) const; + PROJ_INTERNAL CoordinateOperationNNPtr _shallowClone() const override; + private: PROJ_OPAQUE_PRIVATE_DATA Conversion &operator=(const Conversion &other) = delete; @@ -1543,6 +1553,8 @@ class PROJ_GCC_DLL Transformation : public SingleOperation { PROJ_INTERNAL TransformationNNPtr inverseAsTransformation() const; + PROJ_INTERNAL CoordinateOperationNNPtr _shallowClone() const override; + private: PROJ_OPAQUE_PRIVATE_DATA }; @@ -1634,12 +1646,15 @@ class PROJ_GCC_DLL ConcatenatedOperation final : public CoordinateOperation { //! @endcond protected: + PROJ_INTERNAL ConcatenatedOperation(const ConcatenatedOperation &other); PROJ_INTERNAL explicit ConcatenatedOperation( const std::vector<CoordinateOperationNNPtr> &operationsIn); PROJ_INTERNAL void _exportToPROJString(io::PROJStringFormatter *formatter) const override; // throw(FormattingException) + PROJ_INTERNAL CoordinateOperationNNPtr _shallowClone() const override; + INLINED_MAKE_SHARED private: diff --git a/include/proj/internal/coordinateoperation_internal.hpp b/include/proj/internal/coordinateoperation_internal.hpp index 680e805e..207c20b4 100644 --- a/include/proj/internal/coordinateoperation_internal.hpp +++ b/include/proj/internal/coordinateoperation_internal.hpp @@ -109,8 +109,9 @@ using InverseCoordinateOperationNNPtr = util::nn<InverseCoordinateOperationPtr>; */ class InverseCoordinateOperation : virtual public CoordinateOperation { public: - InverseCoordinateOperation(const CoordinateOperationNNPtr &forwardOperation, - bool wktSupportsInversion); + InverseCoordinateOperation( + const CoordinateOperationNNPtr &forwardOperationIn, + bool wktSupportsInversion); ~InverseCoordinateOperation() override; @@ -124,6 +125,10 @@ class InverseCoordinateOperation : virtual public CoordinateOperation { CoordinateOperationNNPtr inverse() const override; + const CoordinateOperationNNPtr &forwardOperation() const { + return forwardOperation_; + } + protected: CoordinateOperationNNPtr forwardOperation_; bool wktSupportsInversion_; @@ -174,6 +179,8 @@ class InverseConversion : public Conversion, public InverseCoordinateOperation { #endif static CoordinateOperationNNPtr create(const ConversionNNPtr &forward); + + CoordinateOperationNNPtr _shallowClone() const override; }; // --------------------------------------------------------------------------- @@ -204,6 +211,8 @@ class InverseTransformation : public Transformation, return InverseCoordinateOperation::inverse(); } + TransformationNNPtr inverseAsTransformation() const; + #ifdef _MSC_VER // To avoid a warning C4250: // 'osgeo::proj::operation::InverseTransformation': inherits @@ -216,6 +225,8 @@ class InverseTransformation : public Transformation, #endif static TransformationNNPtr create(const TransformationNNPtr &forward); + + CoordinateOperationNNPtr _shallowClone() const override; }; // --------------------------------------------------------------------------- @@ -253,11 +264,14 @@ class PROJBasedOperation : public SingleOperation { gridsNeeded(const io::DatabaseContextPtr &databaseContext) const override; protected: + PROJBasedOperation(const PROJBasedOperation &) = default; explicit PROJBasedOperation(const OperationMethodNNPtr &methodIn); void _exportToPROJString(io::PROJStringFormatter *formatter) const override; // throw(FormattingException) + CoordinateOperationNNPtr _shallowClone() const override; + INLINED_MAKE_SHARED private: diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 15532a89..7eab849c 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -56,6 +56,10 @@ #include <string> #include <vector> +#ifdef DEBUG +#include <iostream> +#endif + using namespace NS_PROJ::internal; #if 0 @@ -806,6 +810,14 @@ CoordinateOperation::normalizeForVisualization() const { // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress +CoordinateOperationNNPtr CoordinateOperation::shallowClone() const { + return _shallowClone(); +} +//! @endcond + +// --------------------------------------------------------------------------- + +//! @cond Doxygen_Suppress struct OperationMethod::Private { util::optional<std::string> formula_{}; util::optional<metadata::Citation> formulaCitation_{}; @@ -2342,6 +2354,10 @@ ConversionNNPtr Conversion::shallowClone() const { conv->setCRSs(this, false); return conv; } + +CoordinateOperationNNPtr Conversion::_shallowClone() const { + return util::nn_static_pointer_cast<CoordinateOperation>(shallowClone()); +} //! @endcond // --------------------------------------------------------------------------- @@ -4679,6 +4695,16 @@ InverseConversion::create(const ConversionNNPtr &forward) { return conv; } +// --------------------------------------------------------------------------- + +CoordinateOperationNNPtr InverseConversion::_shallowClone() const { + auto op = InverseConversion::nn_make_shared<InverseConversion>( + inverseAsConversion()->shallowClone()); + op->assignSelf(op); + op->setCRSs(this, false); + return util::nn_static_pointer_cast<CoordinateOperation>(op); +} + //! @endcond // --------------------------------------------------------------------------- @@ -5181,6 +5207,7 @@ const char *Conversion::getWKT1GDALMethodName() const { // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress + void Conversion::_exportToWKT(io::WKTFormatter *formatter) const { const auto &l_method = method(); const auto &methodName = l_method->nameStr(); @@ -5210,6 +5237,17 @@ void Conversion::_exportToWKT(io::WKTFormatter *formatter) const { formatter->pushOutputId(false); } +#ifdef DEBUG_CONVERSION_ID + if (sourceCRS() && targetCRS()) { + formatter->startNode("SOURCECRS_ID", false); + sourceCRS()->formatID(formatter); + formatter->endNode(); + formatter->startNode("TARGETCRS_ID", false); + targetCRS()->formatID(formatter); + formatter->endNode(); + } +#endif + bool bAlreadyWritten = false; if (!isWKT2 && formatter->useESRIDialect()) { const ESRIParamMapping *esriParams = nullptr; @@ -6036,10 +6074,14 @@ const crs::CRSNNPtr &Transformation::targetCRS() PROJ_PURE_DEFN { //! @cond Doxygen_Suppress TransformationNNPtr Transformation::shallowClone() const { - auto conv = Transformation::nn_make_shared<Transformation>(*this); - conv->assignSelf(conv); - conv->setCRSs(this, false); - return conv; + auto transf = Transformation::nn_make_shared<Transformation>(*this); + transf->assignSelf(transf); + transf->setCRSs(this, false); + return transf; +} + +CoordinateOperationNNPtr Transformation::_shallowClone() const { + return util::nn_static_pointer_cast<CoordinateOperation>(shallowClone()); } //! @endcond @@ -7656,6 +7698,13 @@ InverseTransformation::create(const TransformationNNPtr &forward) { // --------------------------------------------------------------------------- +TransformationNNPtr InverseTransformation::inverseAsTransformation() const { + return NN_NO_CHECK( + util::nn_dynamic_pointer_cast<Transformation>(forwardOperation_)); +} + +// --------------------------------------------------------------------------- + void InverseTransformation::_exportToWKT(io::WKTFormatter *formatter) const { auto approxInverse = createApproximateInverseIfPossible( @@ -7667,6 +7716,16 @@ void InverseTransformation::_exportToWKT(io::WKTFormatter *formatter) const { } } +// --------------------------------------------------------------------------- + +CoordinateOperationNNPtr InverseTransformation::_shallowClone() const { + auto op = InverseTransformation::nn_make_shared<InverseTransformation>( + inverseAsTransformation()->shallowClone()); + op->assignSelf(op); + op->setCRSs(this, false); + return util::nn_static_pointer_cast<CoordinateOperation>(op); +} + //! @endcond // --------------------------------------------------------------------------- @@ -9063,6 +9122,7 @@ struct ConcatenatedOperation::Private { explicit Private(const std::vector<CoordinateOperationNNPtr> &operationsIn) : operations_(operationsIn) {} + Private(const Private &) = default; }; //! @endcond @@ -9074,6 +9134,14 @@ ConcatenatedOperation::~ConcatenatedOperation() = default; // --------------------------------------------------------------------------- +//! @cond Doxygen_Suppress +ConcatenatedOperation::ConcatenatedOperation(const ConcatenatedOperation &other) + : CoordinateOperation(other), + d(internal::make_unique<Private>(*(other.d))) {} +//! @endcond + +// --------------------------------------------------------------------------- + ConcatenatedOperation::ConcatenatedOperation( const std::vector<CoordinateOperationNNPtr> &operationsIn) : CoordinateOperation(), d(internal::make_unique<Private>(operationsIn)) {} @@ -9136,6 +9204,22 @@ ConcatenatedOperationNNPtr ConcatenatedOperation::create( } if (i >= 1) { if (!compareStepCRS(l_sourceCRS.get(), lastTargetCRS.get())) { +#ifdef DEBUG_CONCATENATED_OPERATION + { + auto f(io::WKTFormatter::create( + io::WKTFormatter::Convention::WKT2_2018)); + std::cerr << "Source CRS of step " << i << ":" << std::endl; + std::cerr << l_sourceCRS->exportToWKT(f.get()) << std::endl; + } + { + auto f(io::WKTFormatter::create( + io::WKTFormatter::Convention::WKT2_2018)); + std::cerr << "Target CRS of step " << i - 1 << ":" + << std::endl; + std::cerr << lastTargetCRS->exportToWKT(f.get()) + << std::endl; + } +#endif throw InvalidOperation( "Inconsistent chaining of CRS in operations"); } @@ -9149,6 +9233,14 @@ ConcatenatedOperationNNPtr ConcatenatedOperation::create( op->setCRSs(NN_NO_CHECK(operationsIn[0]->sourceCRS()), NN_NO_CHECK(operationsIn.back()->targetCRS()), nullptr); op->setAccuracies(accuracies); +#ifdef DEBUG_CONCATENATED_OPERATION + { + auto f( + io::WKTFormatter::create(io::WKTFormatter::Convention::WKT2_2018)); + std::cerr << "ConcatenatedOperation::create()" << std::endl; + std::cerr << op->exportToWKT(f.get()) << std::endl; + } +#endif return op; } @@ -9459,6 +9551,18 @@ void ConcatenatedOperation::_exportToWKT(io::WKTFormatter *formatter) const { // --------------------------------------------------------------------------- +//! @cond Doxygen_Suppress +CoordinateOperationNNPtr ConcatenatedOperation::_shallowClone() const { + auto op = + ConcatenatedOperation::nn_make_shared<ConcatenatedOperation>(*this); + op->assignSelf(op); + op->setCRSs(this, false); + return util::nn_static_pointer_cast<CoordinateOperation>(op); +} +//! @endcond + +// --------------------------------------------------------------------------- + void ConcatenatedOperation::_exportToPROJString( io::PROJStringFormatter *formatter) const // throw(FormattingException) { @@ -11359,11 +11463,46 @@ static bool isNullTransformation(const std::string &name) { // --------------------------------------------------------------------------- +#ifdef DEBUG + +static int nCallLevel = 0; + +struct EnterDebugLevel { + EnterDebugLevel() { ++nCallLevel; } + ~EnterDebugLevel() { --nCallLevel; } +}; + +static void debugTrace(const std::string &str) { + for (int i = 1; i < nCallLevel; i++) + std::cerr << " "; + std::cerr << str << std::endl; +} + +static std::string objectAsStr(const common::IdentifiedObject *obj) { + std::string ret(obj->nameStr()); + const auto &ids = obj->identifiers(); + if (!ids.empty()) { + ret += " ("; + ret += (*ids[0]->codeSpace()) + ":" + ids[0]->code(); + ret += ")"; + } + return ret; +} +#endif + +// --------------------------------------------------------------------------- + void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( std::vector<CoordinateOperationNNPtr> &res, const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const crs::GeodeticCRS *geodSrc, const crs::GeodeticCRS *geodDst, Private::Context &context) { +#ifdef DEBUG + EnterDebugLevel enterFunction; + debugTrace("createOperationsWithDatumPivot(" + + objectAsStr(sourceCRS.get()) + "," + + objectAsStr(targetCRS.get()) + ")"); +#endif const bool allowEmptyIntersection = true; struct CreateOperationsWithDatumPivotAntiRecursion { @@ -11411,20 +11550,73 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( } std::vector<CoordinateOperationNNPtr> subOps; + const bool isNullThird = + isNullTransformation(opsThird[0]->nameStr()); + CoordinateOperationNNPtr opSecondCloned( + (isNullFirst || isNullThird) ? opSecond->shallowClone() + : opSecond); + CoordinateOperation *invCOForward = nullptr; + if (isNullFirst || isNullThird) { + if (opSecondCloned->identifiers().size() == 1 && + (*opSecondCloned->identifiers()[0]->codeSpace()) + .find("DERIVED_FROM") == std::string::npos) { + { + util::PropertyMap map; + addModifiedIdentifier(map, opSecondCloned.get(), false, + true); + opSecondCloned->setProperties(map); + } + auto invCO = dynamic_cast<InverseCoordinateOperation *>( + opSecondCloned.get()); + if (invCO) { + invCOForward = invCO->forwardOperation().get(); + if (invCOForward->identifiers().size() == 1 && + (*invCOForward->identifiers()[0]->codeSpace()) + .find("DERIVED_FROM") == + std::string::npos) { + util::PropertyMap map; + addModifiedIdentifier(map, invCOForward, false, + true); + invCOForward->setProperties(map); + } + } + } + } if (isNullFirst) { - opSecond->setCRSs( - sourceCRS, NN_CHECK_ASSERT(opSecond->targetCRS()), nullptr); + auto oldTarget(NN_CHECK_ASSERT(opSecondCloned->targetCRS())); + opSecondCloned->setCRSs(sourceCRS, oldTarget, nullptr); + if (invCOForward) { + invCOForward->setCRSs(oldTarget, sourceCRS, nullptr); + } } else { subOps.emplace_back(opFirst); } - if (isNullTransformation(opsThird[0]->nameStr())) { - opSecond->setCRSs(NN_CHECK_ASSERT(opSecond->sourceCRS()), - targetCRS, nullptr); - subOps.emplace_back(opSecond); + if (isNullThird) { + auto oldSource(NN_CHECK_ASSERT(opSecondCloned->sourceCRS())); + opSecondCloned->setCRSs(oldSource, targetCRS, nullptr); + if (invCOForward) { + invCOForward->setCRSs(targetCRS, oldSource, nullptr); + } + subOps.emplace_back(opSecondCloned); } else { - subOps.emplace_back(opSecond); + subOps.emplace_back(opSecondCloned); subOps.emplace_back(opsThird[0]); } +#ifdef DEBUG + std::string debugStr; + for (const auto &op : subOps) { + if (!debugStr.empty()) { + debugStr += " + "; + } + debugStr += objectAsStr(op.get()); + debugStr += " ("; + debugStr += objectAsStr(op->sourceCRS().get()); + debugStr += "->"; + debugStr += objectAsStr(op->targetCRS().get()); + debugStr += ")"; + } + debugTrace("transformation " + debugStr); +#endif res.emplace_back(ConcatenatedOperation::createComputeMetadata( subOps, !allowEmptyIntersection)); } @@ -11436,6 +11628,14 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( if (candidateSrcGeod->nameStr() == sourceCRS->nameStr()) { for (const auto &candidateDstGeod : candidatesDstGeod) { if (candidateDstGeod->nameStr() == targetCRS->nameStr()) { +#ifdef DEBUG + EnterDebugLevel loopLevel; + debugTrace("try " + objectAsStr(sourceCRS.get()) + "->" + + objectAsStr(candidateSrcGeod.get()) + "->" + + objectAsStr(candidateDstGeod.get()) + "->" + + objectAsStr(targetCRS.get()) + ")"); + EnterDebugLevel loopLevel2; +#endif const auto opsFirst = createOperations(sourceCRS, candidateSrcGeod, context); assert(!opsFirst.empty()); @@ -11454,17 +11654,28 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( } for (const auto &candidateSrcGeod : candidatesSrcGeod) { +#ifdef DEBUG + EnterDebugLevel loopLevel; +#endif const auto opsFirst = createOperations(sourceCRS, candidateSrcGeod, context); assert(!opsFirst.empty()); const bool isNullFirst = isNullTransformation(opsFirst[0]->nameStr()); for (const auto &candidateDstGeod : candidatesDstGeod) { +#ifdef DEBUG + EnterDebugLevel loopLevel2; + debugTrace("try " + objectAsStr(sourceCRS.get()) + "->" + + objectAsStr(candidateSrcGeod.get()) + "->" + + objectAsStr(candidateDstGeod.get()) + "->" + + objectAsStr(targetCRS.get()) + ")"); + EnterDebugLevel loopLevel3; +#endif createTransformations(candidateSrcGeod, candidateDstGeod, opsFirst[0], isNullFirst); - } - if (!res.empty()) { - return; + if (!res.empty()) { + return; + } } } } @@ -11519,6 +11730,13 @@ CoordinateOperationFactory::Private::createOperations( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context) { +#ifdef DEBUG + EnterDebugLevel enterFunction; + auto debugStr("createOperations(" + objectAsStr(sourceCRS.get()) + "," + + objectAsStr(targetCRS.get()) + ")"); + debugTrace(debugStr); +#endif + std::vector<CoordinateOperationNNPtr> res; const bool allowEmptyIntersection = true; @@ -11598,15 +11816,48 @@ CoordinateOperationFactory::Private::createOperations( doFilterAndCheckPerfectOp = false; + bool sameGeodeticDatum = false; + if (geodSrc && geodDst) { + const auto &srcDatum = geodSrc->datum(); + const auto &dstDatum = geodDst->datum(); + sameGeodeticDatum = + srcDatum != nullptr && dstDatum != nullptr && + srcDatum->_isEquivalentTo( + dstDatum.get(), + util::IComparable::Criterion::EQUIVALENT); + } + + if (res.empty() && !sameGeodeticDatum && + !context.inCreateOperationsWithDatumPivotAntiRecursion && + geodSrc && geodDst) { + // If we still didn't find a transformation, and that the source + // and target are GeodeticCRS, then go through their underlying + // datum to find potential transformations between other + // GeodeticRSs + // that are made of those datum + // The typical example is if transforming between two + // GeographicCRS, + // but transformations are only available between their + // corresponding geocentric CRS. + const auto &srcDatum = geodSrc->datum(); + const auto &dstDatum = geodDst->datum(); + if (srcDatum != nullptr && dstDatum != nullptr) { + createOperationsWithDatumPivot(res, sourceCRS, targetCRS, + geodSrc, geodDst, context); + doFilterAndCheckPerfectOp = !res.empty(); + } + } + // NAD27 to NAD83 has tens of results already. No need to look // for a pivot - if ((res.empty() && + if (!sameGeodeticDatum && + ((res.empty() && + context.context->getAllowUseIntermediateCRS() == + CoordinateOperationContext::IntermediateCRSUse:: + IF_NO_DIRECT_TRANSFORMATION) || context.context->getAllowUseIntermediateCRS() == - CoordinateOperationContext::IntermediateCRSUse:: - IF_NO_DIRECT_TRANSFORMATION) || - context.context->getAllowUseIntermediateCRS() == - CoordinateOperationContext::IntermediateCRSUse::ALWAYS || - getenv("PROJ_FORCE_SEARCH_PIVOT")) { + CoordinateOperationContext::IntermediateCRSUse::ALWAYS || + getenv("PROJ_FORCE_SEARCH_PIVOT"))) { auto resWithIntermediate = findsOpsInRegistryWithIntermediate( sourceCRS, targetCRS, context.context); res.insert(res.end(), resWithIntermediate.begin(), @@ -11615,27 +11866,6 @@ CoordinateOperationFactory::Private::createOperations( } } - if (res.empty() && - !context.inCreateOperationsWithDatumPivotAntiRecursion && geodSrc && - geodDst) { - // If we still didn't find a transformation, and that the source - // and target are GeodeticCRS, then go through their underlying - // datum to find potential transformations between other GeodeticRSs - // that are made of those datum - // The typical example is if transforming between two GeographicCRS, - // but transformations are only available between their - // corresponding geocentric CRS. - const auto &srcDatum = geodSrc->datum(); - const auto &dstDatum = geodDst->datum(); - if (srcDatum != nullptr && dstDatum != nullptr && - !srcDatum->_isEquivalentTo( - dstDatum.get(), util::IComparable::Criterion::EQUIVALENT)) { - createOperationsWithDatumPivot(res, sourceCRS, targetCRS, - geodSrc, geodDst, context); - doFilterAndCheckPerfectOp = !res.empty(); - } - } - if (doFilterAndCheckPerfectOp) { // If we get at least a result with perfect accuracy, do not bother // generating synthetic transforms. @@ -12459,8 +12689,9 @@ InverseCoordinateOperation::~InverseCoordinateOperation() = default; // --------------------------------------------------------------------------- InverseCoordinateOperation::InverseCoordinateOperation( - const CoordinateOperationNNPtr &forwardOperation, bool wktSupportsInversion) - : forwardOperation_(forwardOperation), + const CoordinateOperationNNPtr &forwardOperationIn, + bool wktSupportsInversion) + : forwardOperation_(forwardOperationIn), wktSupportsInversion_(wktSupportsInversion) {} // --------------------------------------------------------------------------- @@ -12651,6 +12882,15 @@ void PROJBasedOperation::_exportToPROJString( // --------------------------------------------------------------------------- +CoordinateOperationNNPtr PROJBasedOperation::_shallowClone() const { + auto op = PROJBasedOperation::nn_make_shared<PROJBasedOperation>(*this); + op->assignSelf(op); + op->setCRSs(this, false); + return util::nn_static_pointer_cast<CoordinateOperation>(op); +} + +// --------------------------------------------------------------------------- + std::set<GridDescription> PROJBasedOperation::gridsNeeded( const io::DatabaseContextPtr &databaseContext) const { std::set<GridDescription> res; diff --git a/test/unit/test_factory.cpp b/test/unit/test_factory.cpp index 1ceef475..91024d62 100644 --- a/test/unit/test_factory.cpp +++ b/test/unit/test_factory.cpp @@ -1518,12 +1518,20 @@ class FactoryWithTmpDatabase : public ::testing::Test { const auto vals = std::vector<std::string>{"SOURCE", "TARGET", "PIVOT"}; for (const auto &val : vals) { + ASSERT_TRUE( + execute("INSERT INTO geodetic_datum " + "VALUES('FOO','" + + val + "','" + val + + "','',NULL," + "'EPSG','7030','EPSG','8901','EPSG','1262',0);")) + << last_error(); ASSERT_TRUE(execute("INSERT INTO geodetic_crs " "VALUES('NS_" + val + "','" + val + "','" + val + "',NULL,NULL,'geographic 2D','EPSG','6422'," - "'EPSG','6326'," - "'EPSG','1262',NULL,0);")) + "'FOO','" + + val + "'," + "'EPSG','1262',NULL,0);")) << last_error(); } } diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp index 08a3f6ad..b7b87d76 100644 --- a/test/unit/test_operation.cpp +++ b/test/unit/test_operation.cpp @@ -218,6 +218,11 @@ TEST(operation, SingleOperation) { EXPECT_TRUE(sop1->isEquivalentTo(sop1.get())); EXPECT_FALSE(sop1->isEquivalentTo(createUnrelatedObject().get())); + EXPECT_TRUE( + sop1->isEquivalentTo(sop1->CoordinateOperation::shallowClone().get())); + EXPECT_TRUE(sop1->inverse()->isEquivalentTo( + sop1->inverse()->CoordinateOperation::shallowClone().get())); + auto sop2 = Transformation::create( PropertyMap(), nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4326), nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4807), @@ -510,6 +515,8 @@ TEST(operation, concatenated_operation) { EXPECT_TRUE(concat->isEquivalentTo(concat.get())); EXPECT_FALSE(concat->isEquivalentTo(createUnrelatedObject().get())); + EXPECT_TRUE(concat->isEquivalentTo( + concat->CoordinateOperation::shallowClone().get())); EXPECT_FALSE( ConcatenatedOperation::create(PropertyMap(), std::vector<CoordinateOperationNNPtr>{ @@ -4050,6 +4057,11 @@ TEST(operation, conversion_inverse) { EXPECT_TRUE(inv->isEquivalentTo(inv.get())); EXPECT_FALSE(inv->isEquivalentTo(createUnrelatedObject().get())); + + EXPECT_TRUE( + conv->isEquivalentTo(conv->CoordinateOperation::shallowClone().get())); + EXPECT_TRUE( + inv->isEquivalentTo(inv->CoordinateOperation::shallowClone().get())); } // --------------------------------------------------------------------------- @@ -4943,6 +4955,24 @@ TEST(operation, geogCRS_geocentricCRS_same_datum_to_context) { // --------------------------------------------------------------------------- TEST(operation, + geog2D_to_geog3D_same_datum_but_with_potential_other_pivot_context) { + // Check that when going from geog2D to geog3D of same datum, we don't + // try to go through a WGS84 pivot... + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("5365"), // CR 05 2D + authFactory->createCoordinateReferenceSystem("5364"), // CR 05 3D + ctxt); + ASSERT_EQ(list.size(), 1U); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=noop"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_geogCRS_different_datum_though_geocentric_transform_context) { auto authFactory = AuthorityFactory::create(DatabaseContext::create(), "EPSG"); @@ -5402,6 +5432,37 @@ TEST(operation, projCRS_to_projCRS_south_pole_inverted_axis) { // --------------------------------------------------------------------------- +TEST(operation, projCRS_to_projCRS_through_geog3D) { + // Check that when going from projCRS to projCRS, using + // geog2D-->geog3D-->geog3D-->geog2D we do not have issues with + // inconsistent CRS chaining, due to how we 'hack' a bit some intermediate + // steps + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("5367"), // CR05 / CRTM05 + authFactory->createCoordinateReferenceSystem( + "8908"), // CR-SIRGAS / CRTM05 + ctxt); + ASSERT_EQ(list.size(), 1U); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 " + "+step +inv +proj=tmerc +lat_0=0 +lon_0=-84 +k=0.9999 " + "+x_0=500000 +y_0=0 +ellps=WGS84 " + "+step +proj=push +v_3 " + "+step +proj=cart +ellps=WGS84 " + "+step +proj=helmert +x=-0.16959 +y=0.35312 +z=0.51846 " + "+rx=-0.03385 +ry=0.16325 +rz=-0.03446 +s=0.03693 " + "+convention=position_vector " + "+step +inv +proj=cart +ellps=GRS80 " + "+step +proj=pop +v_3 " + "+step +proj=tmerc +lat_0=0 +lon_0=-84 +k=0.9999 +x_0=500000 " + "+y_0=0 +ellps=GRS80"); +} + +// --------------------------------------------------------------------------- + TEST(operation, boundCRS_of_geogCRS_to_geogCRS) { auto boundCRS = BoundCRS::createFromTOWGS84( GeographicCRS::EPSG_4807, std::vector<double>{1, 2, 3, 4, 5, 6, 7}); |
