aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2020-05-19 16:53:27 +0200
committerGitHub <noreply@github.com>2020-05-19 16:53:27 +0200
commit3ee7c9495b7340fbc189f43d9bb4bf5753307991 (patch)
treea9f6c1e3c7a1125dfb69c361f9d52b5504d99433 /src
parent2e5470387df8c713af18e601c0e6a4b352294556 (diff)
parentb0b33b8447972ac6e60d68213d6c24b0a4989421 (diff)
downloadPROJ-3ee7c9495b7340fbc189f43d9bb4bf5753307991.tar.gz
PROJ-3ee7c9495b7340fbc189f43d9bb4bf5753307991.zip
Merge pull request #2234 from rouault/fix_2232
Many fixes regarding BoundCRS, CompoundCRS, Geographic3D CRS with non-metre units
Diffstat (limited to 'src')
-rw-r--r--src/iso19111/coordinateoperation.cpp134
-rw-r--r--src/iso19111/crs.cpp100
-rw-r--r--src/iso19111/io.cpp32
3 files changed, 195 insertions, 71 deletions
diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp
index 9815a22a..bdb2ad2e 100644
--- a/src/iso19111/coordinateoperation.cpp
+++ b/src/iso19111/coordinateoperation.cpp
@@ -6587,6 +6587,10 @@ TransformationNNPtr Transformation::shallowClone() const {
auto transf = Transformation::nn_make_shared<Transformation>(*this);
transf->assignSelf(transf);
transf->setCRSs(this, false);
+ if (transf->d->forwardOperation_) {
+ transf->d->forwardOperation_ =
+ transf->d->forwardOperation_->shallowClone().as_nullable();
+ }
return transf;
}
@@ -12942,7 +12946,6 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot(
(isNullFirst || isNullThird || sourceAndTargetAre3D)
? opSecond->shallowClone()
: opSecond);
- CoordinateOperation *invCOForward = nullptr;
if (isNullFirst || isNullThird) {
if (opSecondCloned->identifiers().size() == 1 &&
(*opSecondCloned->identifiers()[0]->codeSpace())
@@ -12956,7 +12959,7 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot(
auto invCO = dynamic_cast<InverseCoordinateOperation *>(
opSecondCloned.get());
if (invCO) {
- invCOForward = invCO->forwardOperation().get();
+ auto invCOForward = invCO->forwardOperation().get();
if (invCOForward->identifiers().size() == 1 &&
(*invCOForward->identifiers()[0]->codeSpace())
.find("DERIVED_FROM") ==
@@ -12974,25 +12977,19 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot(
auto invCO = dynamic_cast<InverseCoordinateOperation *>(
opSecondCloned.get());
if (invCO) {
- invCOForward = invCO->forwardOperation().get();
+ auto invCOForward = invCO->forwardOperation().get();
invCOForward->getPrivate()->use3DHelmert_ = true;
}
}
if (isNullFirst) {
auto oldTarget(NN_CHECK_ASSERT(opSecondCloned->targetCRS()));
setCRSs(opSecondCloned.get(), sourceCRS, oldTarget);
- if (invCOForward) {
- setCRSs(invCOForward, oldTarget, sourceCRS);
- }
} else {
subOps.emplace_back(opFirst);
}
if (isNullThird) {
auto oldSource(NN_CHECK_ASSERT(opSecondCloned->sourceCRS()));
setCRSs(opSecondCloned.get(), oldSource, targetCRS);
- if (invCOForward) {
- setCRSs(invCOForward, targetCRS, oldSource);
- }
subOps.emplace_back(opSecondCloned);
} else {
subOps.emplace_back(opSecondCloned);
@@ -14000,6 +13997,20 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog(
const auto &hubSrc = boundSrc->hubCRS();
auto hubSrcGeog = dynamic_cast<const crs::GeographicCRS *>(hubSrc.get());
auto geogCRSOfBaseOfBoundSrc = boundSrc->baseCRS()->extractGeographicCRS();
+ auto geogDstDatum = geogDst->datum();
+
+ // If the underlying datum of the source is the same as the target, do
+ // not consider the boundCRS at all, but just its base
+ if (geogCRSOfBaseOfBoundSrc && geogDstDatum) {
+ auto geogCRSOfBaseOfBoundSrcDatum = geogCRSOfBaseOfBoundSrc->datum();
+ if (geogCRSOfBaseOfBoundSrcDatum &&
+ geogCRSOfBaseOfBoundSrcDatum->_isEquivalentTo(
+ geogDstDatum.get(), util::IComparable::Criterion::EQUIVALENT)) {
+ res = createOperations(boundSrc->baseCRS(), targetCRS, context);
+ return;
+ }
+ }
+
bool triedBoundCrsToGeogCRSSameAsHubCRS = false;
// Is it: boundCRS to a geogCRS that is the same as the hubCRS ?
if (hubSrcGeog && geogCRSOfBaseOfBoundSrc &&
@@ -14007,25 +14018,38 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog(
geogDst, util::IComparable::Criterion::EQUIVALENT) ||
hubSrcGeog->is2DPartOf3D(NN_NO_CHECK(geogDst)))) {
triedBoundCrsToGeogCRSSameAsHubCRS = true;
+
+ CoordinateOperationPtr opIntermediate;
+ if (!geogCRSOfBaseOfBoundSrc->_isEquivalentTo(
+ boundSrc->transformation()->sourceCRS().get(),
+ util::IComparable::Criterion::EQUIVALENT)) {
+ auto opsIntermediate = createOperations(
+ NN_NO_CHECK(geogCRSOfBaseOfBoundSrc),
+ boundSrc->transformation()->sourceCRS(), context);
+ assert(!opsIntermediate.empty());
+ opIntermediate = opsIntermediate.front();
+ }
+
if (boundSrc->baseCRS() == geogCRSOfBaseOfBoundSrc) {
- // Optimization to avoid creating a useless concatenated
- // operation
- res.emplace_back(boundSrc->transformation());
+ if (opIntermediate) {
+ try {
+ res.emplace_back(
+ ConcatenatedOperation::createComputeMetadata(
+ {NN_NO_CHECK(opIntermediate),
+ boundSrc->transformation()},
+ !allowEmptyIntersection));
+ } catch (const InvalidOperationEmptyIntersection &) {
+ }
+ } else {
+ // Optimization to avoid creating a useless concatenated
+ // operation
+ res.emplace_back(boundSrc->transformation());
+ }
return;
}
auto opsFirst = createOperations(
boundSrc->baseCRS(), NN_NO_CHECK(geogCRSOfBaseOfBoundSrc), context);
if (!opsFirst.empty()) {
- CoordinateOperationPtr opIntermediate;
- if (!geogCRSOfBaseOfBoundSrc->_isEquivalentTo(
- boundSrc->transformation()->sourceCRS().get(),
- util::IComparable::Criterion::EQUIVALENT)) {
- auto opsIntermediate = createOperations(
- NN_NO_CHECK(geogCRSOfBaseOfBoundSrc),
- boundSrc->transformation()->sourceCRS(), context);
- assert(!opsIntermediate.empty());
- opIntermediate = opsIntermediate.front();
- }
for (const auto &opFirst : opsFirst) {
try {
std::vector<CoordinateOperationNNPtr> subops;
@@ -14046,9 +14070,9 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog(
}
// If the datum are equivalent, this is also fine
} else if (geogCRSOfBaseOfBoundSrc && hubSrcGeog && hubSrcGeog->datum() &&
- geogDst->datum() &&
+ geogDstDatum &&
hubSrcGeog->datum()->_isEquivalentTo(
- geogDst->datum().get(),
+ geogDstDatum.get(),
util::IComparable::Criterion::EQUIVALENT)) {
auto opsFirst = createOperations(
boundSrc->baseCRS(), NN_NO_CHECK(geogCRSOfBaseOfBoundSrc), context);
@@ -14091,14 +14115,14 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog(
// +nadgrids=ntv1_can.dat,conus"
// to "+proj=latlong +datum=NAD83"
} else if (geogCRSOfBaseOfBoundSrc && hubSrcGeog && hubSrcGeog->datum() &&
- geogDst->datum() &&
+ geogDstDatum &&
geogCRSOfBaseOfBoundSrc->ellipsoid()->_isEquivalentTo(
datum::Ellipsoid::CLARKE_1866.get(),
util::IComparable::Criterion::EQUIVALENT) &&
hubSrcGeog->datum()->_isEquivalentTo(
datum::GeodeticReferenceFrame::EPSG_6326.get(),
util::IComparable::Criterion::EQUIVALENT) &&
- geogDst->datum()->_isEquivalentTo(
+ geogDstDatum->_isEquivalentTo(
datum::GeodeticReferenceFrame::EPSG_6269.get(),
util::IComparable::Criterion::EQUIVALENT)) {
auto nnGeogCRSOfBaseOfBoundSrc = NN_NO_CHECK(geogCRSOfBaseOfBoundSrc);
@@ -14197,8 +14221,57 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog(
if (vertCRSOfBaseOfBoundSrc && hubSrcGeog) {
auto opsFirst = createOperations(sourceCRS, hubSrc, context);
if (context.skipHorizontalTransformation) {
- if (!opsFirst.empty())
- res = opsFirst;
+ if (!opsFirst.empty()) {
+ const auto &hubAxisList =
+ hubSrcGeog->coordinateSystem()->axisList();
+ const auto &targetAxisList =
+ geogDst->coordinateSystem()->axisList();
+ if (hubAxisList.size() == 3 && hubAxisList.size() == 3 &&
+ !hubAxisList[2]->_isEquivalentTo(
+ targetAxisList[2].get(),
+ util::IComparable::Criterion::EQUIVALENT)) {
+
+ const auto &srcAxis = hubAxisList[2];
+ const double convSrc = srcAxis->unit().conversionToSI();
+ const auto &dstAxis = targetAxisList[2];
+ const double convDst = dstAxis->unit().conversionToSI();
+ const bool srcIsUp =
+ srcAxis->direction() == cs::AxisDirection::UP;
+ const bool srcIsDown =
+ srcAxis->direction() == cs::AxisDirection::DOWN;
+ const bool dstIsUp =
+ dstAxis->direction() == cs::AxisDirection::UP;
+ const bool dstIsDown =
+ dstAxis->direction() == cs::AxisDirection::DOWN;
+ const bool heightDepthReversal =
+ ((srcIsUp && dstIsDown) || (srcIsDown && dstIsUp));
+
+ const double factor = convSrc / convDst;
+ auto conv = Conversion::createChangeVerticalUnit(
+ util::PropertyMap().set(
+ common::IdentifiedObject::NAME_KEY,
+ "Change of vertical unit"),
+ common::Scale(heightDepthReversal ? -factor : factor));
+ auto dbContext = context.context->getAuthorityFactory()
+ ->databaseContext();
+ conv->setCRSs(
+ hubSrc,
+ hubSrc->demoteTo2D(std::string(), dbContext)
+ ->promoteTo3D(std::string(), dbContext, dstAxis),
+ nullptr);
+
+ for (const auto &op : opsFirst) {
+ try {
+ res.emplace_back(
+ ConcatenatedOperation::createComputeMetadata(
+ {op, conv}, !allowEmptyIntersection));
+ } catch (const InvalidOperationEmptyIntersection &) {
+ }
+ }
+ } else {
+ res = opsFirst;
+ }
+ }
return;
} else {
auto opsSecond = createOperations(hubSrc, targetCRS, context);
@@ -15145,10 +15218,15 @@ void CoordinateOperationFactory::Private::createOperationsBoundToCompound(
}
}
}
+ return;
}
}
}
}
+
+ // There might be better things to do, but for now just ignore the
+ // transformation of the bound CRS
+ res = createOperations(boundSrc->baseCRS(), targetCRS, context);
}
//! @endcond
diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp
index bad7deea..35e11d84 100644
--- a/src/iso19111/crs.cpp
+++ b/src/iso19111/crs.cpp
@@ -98,7 +98,7 @@ struct CRS::Private {
bool allowNonConformantWKT1Export_ = false;
// for what was initially a COMPD_CS with a VERT_CS with a datum type ==
// ellipsoidal height / 2002
- VerticalCRSPtr originalVertCRS_{};
+ CompoundCRSPtr originalCompoundCRS_{};
void setImplicitCS(const util::PropertyMap &properties) {
const auto pVal = properties.get("IMPLICIT_CS");
@@ -583,17 +583,18 @@ CRSNNPtr CRS::allowNonConformantWKT1Export() const {
//! @cond Doxygen_Suppress
-CRSNNPtr CRS::attachOriginalVertCRS(const VerticalCRSNNPtr &vertCRS) const {
+CRSNNPtr
+CRS::attachOriginalCompoundCRS(const CompoundCRSNNPtr &compoundCRS) const {
const auto boundCRS = dynamic_cast<const BoundCRS *>(this);
if (boundCRS) {
return BoundCRS::create(
- boundCRS->baseCRS()->attachOriginalVertCRS(vertCRS),
+ boundCRS->baseCRS()->attachOriginalCompoundCRS(compoundCRS),
boundCRS->hubCRS(), boundCRS->transformation());
}
auto crs(shallowClone());
- crs->d->originalVertCRS_ = vertCRS.as_nullable();
+ crs->d->originalCompoundCRS_ = compoundCRS.as_nullable();
return crs;
}
@@ -868,7 +869,22 @@ CRS::getNonDeprecated(const io::DatabaseContextNNPtr &dbContext) const {
*/
CRSNNPtr CRS::promoteTo3D(const std::string &newName,
const io::DatabaseContextPtr &dbContext) const {
+ auto upAxis = cs::CoordinateSystemAxis::create(
+ util::PropertyMap().set(IdentifiedObject::NAME_KEY,
+ cs::AxisName::Ellipsoidal_height),
+ cs::AxisAbbreviation::h, cs::AxisDirection::UP,
+ common::UnitOfMeasure::METRE);
+ return promoteTo3D(newName, dbContext, upAxis);
+}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+CRSNNPtr CRS::promoteTo3D(const std::string &newName,
+ const io::DatabaseContextPtr &dbContext,
+ const cs::CoordinateSystemAxisNNPtr
+ &verticalAxisIfNotAlreadyPresent) const {
const auto geogCRS = dynamic_cast<const GeographicCRS *>(this);
if (geogCRS) {
const auto &axisList = geogCRS->coordinateSystem()->axisList();
@@ -886,21 +902,23 @@ CRSNNPtr CRS::promoteTo3D(const std::string &newName,
false);
if (!res.empty()) {
const auto &firstRes = res.front();
- if (geogCRS->is2DPartOf3D(NN_NO_CHECK(
- dynamic_cast<GeographicCRS *>(firstRes.get())))) {
+ const auto firstResGeog =
+ dynamic_cast<GeographicCRS *>(firstRes.get());
+ const auto &firstResAxisList =
+ firstResGeog->coordinateSystem()->axisList();
+ if (firstResAxisList[2]->_isEquivalentTo(
+ verticalAxisIfNotAlreadyPresent.get(),
+ util::IComparable::Criterion::EQUIVALENT) &&
+ geogCRS->is2DPartOf3D(NN_NO_CHECK(firstResGeog))) {
return NN_NO_CHECK(
util::nn_dynamic_pointer_cast<CRS>(firstRes));
}
}
}
- auto upAxis = cs::CoordinateSystemAxis::create(
- util::PropertyMap().set(IdentifiedObject::NAME_KEY,
- cs::AxisName::Ellipsoidal_height),
- cs::AxisAbbreviation::h, cs::AxisDirection::UP,
- common::UnitOfMeasure::METRE);
auto cs = cs::EllipsoidalCS::create(
- util::PropertyMap(), axisList[0], axisList[1], upAxis);
+ util::PropertyMap(), axisList[0], axisList[1],
+ verticalAxisIfNotAlreadyPresent);
return util::nn_static_pointer_cast<CRS>(GeographicCRS::create(
util::PropertyMap().set(common::IdentifiedObject::NAME_KEY,
!newName.empty() ? newName : nameStr()),
@@ -914,13 +932,9 @@ CRSNNPtr CRS::promoteTo3D(const std::string &newName,
if (axisList.size() == 2) {
auto base3DCRS =
projCRS->baseCRS()->promoteTo3D(std::string(), dbContext);
- auto upAxis = cs::CoordinateSystemAxis::create(
- util::PropertyMap().set(IdentifiedObject::NAME_KEY,
- cs::AxisName::Ellipsoidal_height),
- cs::AxisAbbreviation::h, cs::AxisDirection::UP,
- common::UnitOfMeasure::METRE);
auto cs = cs::CartesianCS::create(util::PropertyMap(), axisList[0],
- axisList[1], upAxis);
+ axisList[1],
+ verticalAxisIfNotAlreadyPresent);
return util::nn_static_pointer_cast<CRS>(ProjectedCRS::create(
util::PropertyMap().set(common::IdentifiedObject::NAME_KEY,
!newName.empty() ? newName : nameStr()),
@@ -932,7 +946,8 @@ CRSNNPtr CRS::promoteTo3D(const std::string &newName,
const auto boundCRS = dynamic_cast<const BoundCRS *>(this);
if (boundCRS) {
- auto base3DCRS = boundCRS->baseCRS()->promoteTo3D(newName, dbContext);
+ auto base3DCRS = boundCRS->baseCRS()->promoteTo3D(
+ newName, dbContext, verticalAxisIfNotAlreadyPresent);
auto transf = boundCRS->transformation();
try {
transf->getTOWGS84Parameters();
@@ -948,6 +963,9 @@ CRSNNPtr CRS::promoteTo3D(const std::string &newName,
return NN_NO_CHECK(
std::static_pointer_cast<CRS>(shared_from_this().as_nullable()));
}
+
+//! @endcond
+
// ---------------------------------------------------------------------------
/** \brief Return a variant of this CRS "demoted" to a 2D one, if not already
@@ -1436,14 +1454,9 @@ void GeodeticCRS::_exportToWKT(io::WKTFormatter *formatter) const {
return;
}
- auto &originalVertCRS = CRS::getPrivate()->originalVertCRS_;
- if (originalVertCRS) {
- formatter->startNode(io::WKTConstants::COMPD_CS, false);
- formatter->addQuotedString(l_name + " + " +
- originalVertCRS->nameStr());
- geogCRS2D->_exportToWKT(formatter);
- originalVertCRS->_exportToWKT(formatter);
- formatter->endNode();
+ auto &originalCompoundCRS = CRS::getPrivate()->originalCompoundCRS_;
+ if (originalCompoundCRS) {
+ originalCompoundCRS->_exportToWKT(formatter);
return;
}
@@ -3292,14 +3305,9 @@ void ProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const {
return;
}
- auto &originalVertCRS = CRS::getPrivate()->originalVertCRS_;
- if (!formatter->useESRIDialect() && originalVertCRS) {
- formatter->startNode(io::WKTConstants::COMPD_CS, false);
- formatter->addQuotedString(l_name + " + " +
- originalVertCRS->nameStr());
- projCRS2D->_exportToWKT(formatter);
- originalVertCRS->_exportToWKT(formatter);
- formatter->endNode();
+ auto &originalCompoundCRS = CRS::getPrivate()->originalCompoundCRS_;
+ if (!formatter->useESRIDialect() && originalCompoundCRS) {
+ originalCompoundCRS->_exportToWKT(formatter);
return;
}
@@ -4254,8 +4262,8 @@ CRSNNPtr CompoundCRS::createLax(const util::PropertyMap &properties,
auto comp1 = components[1].get();
auto comp0Geog = dynamic_cast<const GeographicCRS *>(comp0);
auto comp0Proj = dynamic_cast<const ProjectedCRS *>(comp0);
+ auto comp0Bound = dynamic_cast<const BoundCRS *>(comp0);
if (comp0Geog == nullptr && comp0Proj == nullptr) {
- auto comp0Bound = dynamic_cast<const BoundCRS *>(comp0);
if (comp0Bound) {
const auto *baseCRS = comp0Bound->baseCRS().get();
comp0Geog = dynamic_cast<const GeographicCRS *>(baseCRS);
@@ -4286,14 +4294,20 @@ CRSNNPtr CompoundCRS::createLax(const util::PropertyMap &properties,
if (comp1Vert != nullptr && comp1Vert->datum() &&
comp1Vert->datum()->getWKT1DatumType() == "2002") {
const auto &axis = comp1Vert->coordinateSystem()->axisList()[0];
- if (axis->unit()._isEquivalentTo(
- common::UnitOfMeasure::METRE,
- util::IComparable::Criterion::EQUIVALENT) &&
- &(axis->direction()) == &(cs::AxisDirection::UP)) {
- return components[0]
- ->promoteTo3D(std::string(), dbContext)
- ->attachOriginalVertCRS(NN_NO_CHECK(comp1Vert));
+ std::string name(components[0]->nameStr());
+ if (!(axis->unit()._isEquivalentTo(
+ common::UnitOfMeasure::METRE,
+ util::IComparable::Criterion::EQUIVALENT) &&
+ &(axis->direction()) == &(cs::AxisDirection::UP))) {
+ name += " (" + comp1Vert->nameStr() + ')';
}
+ return components[0]
+ ->promoteTo3D(name, dbContext, axis)
+ ->attachOriginalCompoundCRS(create(
+ properties,
+ comp0Bound ? std::vector<CRSNNPtr>{comp0Bound->baseCRS(),
+ components[1]}
+ : components));
}
}
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index ebec053a..e7f8769c 100644
--- a/src/iso19111/io.cpp
+++ b/src/iso19111/io.cpp
@@ -6963,6 +6963,38 @@ const std::string &PROJStringFormatter::toString() const {
break;
}
+ // +step +proj=unitconvert +xy_in=X1 +z_in=Z1 +xy_out=X2 +z_out=Z2
+ // +step +proj=unitconvert +z_in=Z2 +z_out=Z3
+ // ==> step +proj=unitconvert +xy_in=X1 +z_in=Z1 +xy_out=X2
+ // +z_out=Z3
+ if (prevStep.name == "unitconvert" &&
+ curStep.name == "unitconvert" && !prevStep.inverted &&
+ !curStep.inverted && prevStep.paramValues.size() == 4 &&
+ curStep.paramValues.size() == 2 &&
+ prevStep.paramValues[0].keyEquals("xy_in") &&
+ prevStep.paramValues[1].keyEquals("z_in") &&
+ prevStep.paramValues[2].keyEquals("xy_out") &&
+ prevStep.paramValues[3].keyEquals("z_out") &&
+ curStep.paramValues[0].keyEquals("z_in") &&
+ curStep.paramValues[1].keyEquals("z_out") &&
+ prevStep.paramValues[3].value == curStep.paramValues[0].value) {
+ auto xy_in = prevStep.paramValues[0].value;
+ auto z_in = prevStep.paramValues[1].value;
+ auto xy_out = prevStep.paramValues[2].value;
+ auto z_out = curStep.paramValues[1].value;
+ d->steps_.erase(iterPrev, iterCur);
+ iterCur->paramValues.clear();
+ iterCur->paramValues.emplace_back(
+ Step::KeyValue("xy_in", xy_in));
+ iterCur->paramValues.emplace_back(Step::KeyValue("z_in", z_in));
+ iterCur->paramValues.emplace_back(
+ Step::KeyValue("xy_out", xy_out));
+ iterCur->paramValues.emplace_back(
+ Step::KeyValue("z_out", z_out));
+ changeDone = true;
+ break;
+ }
+
// unitconvert (1), axisswap order=2,1, unitconvert(2) ==>
// axisswap order=2,1, unitconvert (1), unitconvert(2) which
// will get further optimized by previous case