aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/proj/coordinateoperation.hpp15
-rw-r--r--include/proj/internal/coordinateoperation_internal.hpp18
-rw-r--r--src/iso19111/coordinateoperation.cpp326
-rw-r--r--test/unit/test_factory.cpp12
-rw-r--r--test/unit/test_operation.cpp61
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});