diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2019-11-25 01:20:38 +0100 |
|---|---|---|
| committer | Even Rouault <even.rouault@spatialys.com> | 2019-11-25 01:20:38 +0100 |
| commit | fb73bc9bf71b4fe83907420366cc217ee86f6ef3 (patch) | |
| tree | 42fe02031744753a0ec1079807611262444d99fc | |
| parent | 875aba7c9d894f84f2c6a5efdfdeba9cf15245f5 (diff) | |
| download | PROJ-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.cpp | 161 | ||||
| -rw-r--r-- | test/unit/test_io.cpp | 8 | ||||
| -rw-r--r-- | test/unit/test_operation.cpp | 61 |
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 &) { + } } // --------------------------------------------------------------------------- |
