aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2019-11-25 01:20:38 +0100
committerEven Rouault <even.rouault@spatialys.com>2019-11-25 01:20:38 +0100
commitfb73bc9bf71b4fe83907420366cc217ee86f6ef3 (patch)
tree42fe02031744753a0ec1079807611262444d99fc
parent875aba7c9d894f84f2c6a5efdfdeba9cf15245f5 (diff)
downloadPROJ-fb73bc9bf71b4fe83907420366cc217ee86f6ef3.tar.gz
PROJ-fb73bc9bf71b4fe83907420366cc217ee86f6ef3.zip
createOperations(): optimize compoundCRS to geogCRS, when the geogCRS of the compoundCRS is the same as the target geogCRS
-rw-r--r--src/iso19111/coordinateoperation.cpp161
-rw-r--r--test/unit/test_io.cpp8
-rw-r--r--test/unit/test_operation.cpp61
3 files changed, 173 insertions, 57 deletions
diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp
index 12966bdd..b03683dc 100644
--- a/src/iso19111/coordinateoperation.cpp
+++ b/src/iso19111/coordinateoperation.cpp
@@ -5892,8 +5892,17 @@ void Conversion::_exportToPROJString(
auto l_sourceCRS = sourceCRS();
if (!formatter->getCRSExport() && l_sourceCRS && applySourceCRSModifiers) {
- auto geogCRS =
- dynamic_cast<const crs::GeographicCRS *>(l_sourceCRS.get());
+
+ crs::CRS *horiz = l_sourceCRS.get();
+ const auto compound = dynamic_cast<const crs::CompoundCRS *>(horiz);
+ if (compound) {
+ const auto &components = compound->componentReferenceSystems();
+ if (!components.empty()) {
+ horiz = components.front().get();
+ }
+ }
+
+ auto geogCRS = dynamic_cast<const crs::GeographicCRS *>(horiz);
if (geogCRS) {
formatter->setOmitProjLongLatIfPossible(true);
formatter->startInversion();
@@ -5902,11 +5911,12 @@ void Conversion::_exportToPROJString(
formatter->setOmitProjLongLatIfPossible(false);
}
- auto projCRS =
- dynamic_cast<const crs::ProjectedCRS *>(l_sourceCRS.get());
+ auto projCRS = dynamic_cast<const crs::ProjectedCRS *>(horiz);
if (projCRS) {
formatter->startInversion();
+ formatter->pushOmitZUnitConversion();
projCRS->addUnitConvertAndAxisSwap(formatter, false);
+ formatter->popOmitZUnitConversion();
formatter->stopInversion();
}
}
@@ -6171,8 +6181,17 @@ void Conversion::_exportToPROJString(
auto l_targetCRS = targetCRS();
if (l_targetCRS && applyTargetCRSModifiers) {
+ crs::CRS *horiz = l_targetCRS.get();
+ const auto compound = dynamic_cast<const crs::CompoundCRS *>(horiz);
+ if (compound) {
+ const auto &components = compound->componentReferenceSystems();
+ if (!components.empty()) {
+ horiz = components.front().get();
+ }
+ }
+
if (!bEllipsoidParametersDone) {
- auto targetGeogCRS = l_targetCRS->extractGeographicCRS();
+ auto targetGeogCRS = horiz->extractGeographicCRS();
if (targetGeogCRS) {
if (formatter->getCRSExport()) {
targetGeogCRS->addDatumInfoToPROJString(formatter);
@@ -6184,14 +6203,15 @@ void Conversion::_exportToPROJString(
}
}
- auto projCRS =
- dynamic_cast<const crs::ProjectedCRS *>(l_targetCRS.get());
+ auto projCRS = dynamic_cast<const crs::ProjectedCRS *>(horiz);
if (projCRS) {
+ formatter->pushOmitZUnitConversion();
projCRS->addUnitConvertAndAxisSwap(formatter, bAxisSpecFound);
+ formatter->popOmitZUnitConversion();
}
auto derivedGeographicCRS =
- dynamic_cast<const crs::DerivedGeographicCRS *>(l_targetCRS.get());
+ dynamic_cast<const crs::DerivedGeographicCRS *>(horiz);
if (derivedGeographicCRS) {
auto baseGeodCRS = derivedGeographicCRS->baseCRS();
formatter->setOmitProjLongLatIfPossible(true);
@@ -13912,6 +13932,37 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog(
const auto &authFactory = context.context->getAuthorityFactory();
const auto &componentsSrc = compoundSrc->componentReferenceSystems();
if (!componentsSrc.empty()) {
+
+ if (componentsSrc.size() == 2) {
+ auto derivedHSrc =
+ dynamic_cast<const crs::DerivedCRS *>(componentsSrc[0].get());
+ if (derivedHSrc) {
+ std::vector<crs::CRSNNPtr> intermComponents{
+ derivedHSrc->baseCRS(), componentsSrc[1]};
+ auto properties = util::PropertyMap().set(
+ common::IdentifiedObject::NAME_KEY,
+ intermComponents[0]->nameStr() + " + " +
+ intermComponents[1]->nameStr());
+ auto intermCompound =
+ crs::CompoundCRS::create(properties, intermComponents);
+ auto opsFirst =
+ createOperations(sourceCRS, intermCompound, context);
+ assert(!opsFirst.empty());
+ auto opsLast =
+ createOperations(intermCompound, targetCRS, context);
+ for (const auto &opLast : opsLast) {
+ try {
+ res.emplace_back(
+ ConcatenatedOperation::createComputeMetadata(
+ {opsFirst.front(), opLast},
+ !allowEmptyIntersection));
+ } catch (const std::exception &) {
+ }
+ }
+ return;
+ }
+ }
+
std::vector<CoordinateOperationNNPtr> horizTransforms;
auto srcGeogCRS = componentsSrc[0]->extractGeographicCRS();
if (srcGeogCRS) {
@@ -14040,6 +14091,26 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog(
key =
(*ids.front()->codeSpace()) + ':' + ids.front()->code();
}
+
+ const auto computeOpsToInterp =
+ [&srcToInterpOps, &interpToTargetOps, &componentsSrc,
+ &interpolationGeogCRS, &targetCRS, &dbContext,
+ &context]() {
+ srcToInterpOps = createOperations(
+ componentsSrc[0], NN_NO_CHECK(interpolationGeogCRS),
+ context);
+ auto target2D =
+ targetCRS->demoteTo2D(std::string(), dbContext);
+ if (!componentsSrc[0]->isEquivalentTo(
+ target2D.get(),
+ util::IComparable::Criterion::EQUIVALENT)) {
+ interpToTargetOps = createOperations(
+ NN_NO_CHECK(interpolationGeogCRS),
+ targetCRS->demoteTo2D(std::string(), dbContext),
+ context);
+ }
+ };
+
if (!key.empty()) {
auto iter = cacheHorizToInterpAndInterpToTarget.find(key);
if (iter == cacheHorizToInterpAndInterpToTarget.end()) {
@@ -14048,13 +14119,7 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog(
"from source to interpCRS and interpCRS to "
"target");
#endif
- srcToInterpOps = createOperations(
- componentsSrc[0], NN_NO_CHECK(interpolationGeogCRS),
- context);
- interpToTargetOps = createOperations(
- NN_NO_CHECK(interpolationGeogCRS),
- targetCRS->demoteTo2D(std::string(), dbContext),
- context);
+ computeOpsToInterp();
cacheHorizToInterpAndInterpToTarget[key] =
PairOfTransforms(srcToInterpOps, interpToTargetOps);
} else {
@@ -14067,34 +14132,39 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog(
"from source to interpCRS and interpCRS to "
"target");
#endif
- srcToInterpOps = createOperations(
- componentsSrc[0], NN_NO_CHECK(interpolationGeogCRS),
- context);
- interpToTargetOps = createOperations(
- NN_NO_CHECK(interpolationGeogCRS),
- targetCRS->demoteTo2D(std::string(), dbContext),
- context);
+ computeOpsToInterp();
}
#ifdef TRACE_CREATE_OPERATIONS
ENTER_BLOCK("creating HorizVerticalHorizPROJBased operations");
#endif
for (const auto &srcToInterp : srcToInterpOps) {
- for (const auto &interpToTarget : interpToTargetOps) {
-
- if (useDifferentTransformationsForSameSourceTarget(
- srcToInterp, interpToTarget)) {
- continue;
- }
-
+ if (interpToTargetOps.empty()) {
try {
auto op = createHorizVerticalHorizPROJBased(
sourceCRS, targetCRS, srcToInterp,
- verticalTransform, interpToTarget,
+ verticalTransform, srcToInterp->inverse(),
interpolationGeogCRS, true);
res.emplace_back(op);
} catch (const std::exception &) {
}
+ } else {
+ for (const auto &interpToTarget : interpToTargetOps) {
+
+ if (useDifferentTransformationsForSameSourceTarget(
+ srcToInterp, interpToTarget)) {
+ continue;
+ }
+
+ try {
+ auto op = createHorizVerticalHorizPROJBased(
+ sourceCRS, targetCRS, srcToInterp,
+ verticalTransform, interpToTarget,
+ interpolationGeogCRS, true);
+ res.emplace_back(op);
+ } catch (const std::exception &) {
+ }
+ }
}
}
} else {
@@ -14163,15 +14233,15 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToCompound(
if (componentsSrc[0]->extractGeographicCRS() &&
componentsDst[0]->extractGeographicCRS()) {
- bool verticalTransfIsNoOp = false;
std::vector<CoordinateOperationNNPtr> verticalTransforms;
if (componentsSrc.size() >= 2 &&
componentsSrc[1]->extractVerticalCRS() &&
componentsDst[1]->extractVerticalCRS()) {
- verticalTransfIsNoOp =
- componentsSrc[1]->_isEquivalentTo(componentsDst[1].get());
- verticalTransforms = createOperations(
- componentsSrc[1], componentsDst[1], context);
+ if (!componentsSrc[1]->_isEquivalentTo(
+ componentsDst[1].get())) {
+ verticalTransforms = createOperations(
+ componentsSrc[1], componentsDst[1], context);
+ }
}
for (const auto &verticalTransform : verticalTransforms) {
@@ -14216,15 +14286,9 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToCompound(
for (const auto &opDst : opGeogCRStoDstCRS) {
try {
- auto op = verticalTransfIsNoOp
- ? ConcatenatedOperation::
- createComputeMetadata(
- {opSrc, opDst},
- !allowEmptyIntersection)
- : createHorizVerticalHorizPROJBased(
- sourceCRS, targetCRS, opSrc,
- verticalTransform, opDst,
- interpolationGeogCRS, true);
+ auto op = createHorizVerticalHorizPROJBased(
+ sourceCRS, targetCRS, opSrc, verticalTransform,
+ opDst, interpolationGeogCRS, true);
res.emplace_back(op);
} catch (const InvalidOperationEmptyIntersection &) {
}
@@ -14233,8 +14297,13 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToCompound(
}
if (verticalTransforms.empty()) {
- res = createOperations(componentsSrc[0], componentsDst[0],
- context);
+ auto resTmp = createOperations(componentsSrc[0],
+ componentsDst[0], context);
+ for (const auto &op : resTmp) {
+ auto opClone = op->shallowClone();
+ setCRSs(opClone.get(), sourceCRS, targetCRS);
+ res.emplace_back(opClone);
+ }
}
}
}
diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp
index 07c4c6f1..fff86d27 100644
--- a/test/unit/test_io.cpp
+++ b/test/unit/test_io.cpp
@@ -8389,7 +8389,7 @@ TEST(io, projparse_axisswap_unitconvert_proj_unitconvert) {
"+type=crs +proj=pipeline +step +proj=axisswap +order=2,1 +step "
"+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=igh "
"+lon_0=0 +x_0=0 +y_0=0 +ellps=GRS80 +step +proj=unitconvert +xy_in=m "
- "+z_in=m +xy_out=ft +z_out=ft";
+ "+xy_out=ft";
auto obj = PROJStringParser().createFromPROJString(input);
auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
ASSERT_TRUE(crs != nullptr);
@@ -8400,7 +8400,7 @@ TEST(io, projparse_axisswap_unitconvert_proj_unitconvert) {
"+proj=pipeline +step +proj=axisswap +order=2,1 +step "
"+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=igh "
"+lon_0=0 +x_0=0 +y_0=0 +ellps=GRS80 +step +proj=unitconvert "
- "+xy_in=m +z_in=m +xy_out=ft +z_out=ft");
+ "+xy_in=m +xy_out=ft");
}
// ---------------------------------------------------------------------------
@@ -8410,7 +8410,7 @@ TEST(io, projparse_axisswap_unitconvert_proj_unitconvert_numeric_axisswap) {
"+type=crs +proj=pipeline +step +proj=axisswap +order=2,1 +step "
"+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=igh "
"+lon_0=0 +x_0=0 +y_0=0 +ellps=GRS80 +step +proj=unitconvert +xy_in=m "
- "+z_in=m +xy_out=2.5 +z_out=2.5 +step +proj=axisswap +order=-2,-1";
+ "+xy_out=2.5 +step +proj=axisswap +order=-2,-1";
auto obj = PROJStringParser().createFromPROJString(input);
auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
ASSERT_TRUE(crs != nullptr);
@@ -8421,7 +8421,7 @@ TEST(io, projparse_axisswap_unitconvert_proj_unitconvert_numeric_axisswap) {
"+proj=pipeline +step +proj=axisswap +order=2,1 +step "
"+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=igh "
"+lon_0=0 +x_0=0 +y_0=0 +ellps=GRS80 +step +proj=unitconvert "
- "+xy_in=m +z_in=m +xy_out=2.5 +z_out=2.5 +step +proj=axisswap "
+ "+xy_in=m +xy_out=2.5 +step +proj=axisswap "
"+order=-2,-1");
}
diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp
index 2bf46601..2c5792de 100644
--- a/test/unit/test_operation.cpp
+++ b/test/unit/test_operation.cpp
@@ -5343,8 +5343,8 @@ TEST(operation, esri_projectedCRS_to_geogCRS_with_ITRF_intermediate_context) {
EXPECT_PRED_FORMAT2(
ComparePROJString,
list[0]->exportToPROJString(PROJStringFormatter::create().get()),
- "+proj=pipeline +step +proj=unitconvert +xy_in=us-ft +z_in=us-ft "
- "+xy_out=m +z_out=m +step +inv +proj=lcc +lat_0=33.75 +lon_0=-79 "
+ "+proj=pipeline +step +proj=unitconvert +xy_in=us-ft "
+ "+xy_out=m +step +inv +proj=lcc +lat_0=33.75 +lon_0=-79 "
"+lat_1=34.3333333333333 +lat_2=36.1666666666667 "
"+x_0=609601.219202438 +y_0=0 +ellps=GRS80 +step +proj=cart "
"+ellps=GRS80 +step +inv +proj=helmert +x=0.9956 +y=-1.9013 "
@@ -7291,19 +7291,63 @@ TEST(operation, compoundCRS_to_geogCRS_2D_promote_to_3D_context) {
ctxt);
// The checked value is not that important, but in case this changes,
// likely due to a EPSG upgrade, worth checking
- ASSERT_EQ(listCompoundToGeog2D.size(), 467U);
+ EXPECT_EQ(listCompoundToGeog2D.size(), 141U);
auto listGeog2DToCompound =
CoordinateOperationFactory::create()->createOperations(dst, nnSrc,
ctxt);
- ASSERT_EQ(listGeog2DToCompound.size(), listCompoundToGeog2D.size());
+ EXPECT_EQ(listGeog2DToCompound.size(), listCompoundToGeog2D.size());
auto listCompoundToGeog3D =
CoordinateOperationFactory::create()->createOperations(
nnSrc,
dst->promoteTo3D(std::string(), authFactory->databaseContext()),
ctxt);
- ASSERT_EQ(listCompoundToGeog3D.size(), listCompoundToGeog2D.size());
+ EXPECT_EQ(listCompoundToGeog3D.size(), listCompoundToGeog2D.size());
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(operation, compoundCRS_of_projCRS_to_geogCRS_2D_context) {
+ auto authFactory =
+ AuthorityFactory::create(DatabaseContext::create(), "EPSG");
+ auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0);
+ ctxt->setSpatialCriterion(
+ CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION);
+ ctxt->setGridAvailabilityUse(
+ CoordinateOperationContext::GridAvailabilityUse::
+ IGNORE_GRID_AVAILABILITY);
+ // SPCS83 California zone 1 (US Survey feet) + NAVD88 height (ftUS)
+ auto srcObj = createFromUserInput("EPSG:2225+6360",
+ authFactory->databaseContext(), false);
+ auto src = nn_dynamic_pointer_cast<CRS>(srcObj);
+ ASSERT_TRUE(src != nullptr);
+ auto nnSrc = NN_NO_CHECK(src);
+ auto dst = authFactory->createCoordinateReferenceSystem("4269"); // NAD83
+
+ auto list = CoordinateOperationFactory::create()->createOperations(
+ nnSrc, dst, ctxt);
+ // The checked value is not that important, but in case this changes,
+ // likely due to a EPSG upgrade, worth checking
+ // We want to make sure that the horizontal adjustments before and after
+ // the vertical transformation are the reverse of each other, and there are
+ // not mixes with different alternative operations (like California grid
+ // forward and Nevada grid reverse)
+ ASSERT_EQ(list.size(), 14U);
+
+ // Check that unit conversion is OK
+ auto op_proj =
+ list[0]->exportToPROJString(PROJStringFormatter::create().get());
+ EXPECT_EQ(op_proj,
+ "+proj=pipeline "
+ "+step +proj=unitconvert +xy_in=us-ft +xy_out=m "
+ "+step +inv +proj=lcc +lat_0=39.3333333333333 +lon_0=-122 "
+ "+lat_1=41.6666666666667 +lat_2=40 +x_0=2000000.0001016 "
+ "+y_0=500000.0001016 +ellps=GRS80 "
+ "+step +proj=unitconvert +z_in=us-ft +z_out=m "
+ "+step +proj=vgridshift +grids=geoid09_conus.gtx +multiplier=1 "
+ "+step +proj=unitconvert +xy_in=rad +xy_out=deg "
+ "+step +proj=axisswap +order=2,1");
}
// ---------------------------------------------------------------------------
@@ -8284,8 +8328,11 @@ TEST(operation, createOperation_ossfuzz_18587) {
ASSERT_TRUE(dst != nullptr);
// Just check that we don't go into an infinite recursion
- CoordinateOperationFactory::create()->createOperation(NN_CHECK_ASSERT(src),
- NN_CHECK_ASSERT(dst));
+ try {
+ CoordinateOperationFactory::create()->createOperation(
+ NN_CHECK_ASSERT(src), NN_CHECK_ASSERT(dst));
+ } catch (const std::exception &) {
+ }
}
// ---------------------------------------------------------------------------