Skip to content

Commit 1baa125

Browse files
committed
fully respect crs_extent_use=none in pipeline composition
1 parent fc4c131 commit 1baa125

File tree

4 files changed

+85
-12
lines changed

4 files changed

+85
-12
lines changed

include/proj/io.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,7 +1259,8 @@ class PROJ_GCC_DLL AuthorityFactory {
12591259
const std::vector<std::string> &allowedAuthorities =
12601260
std::vector<std::string>(),
12611261
const metadata::ExtentPtr &intersectingExtent1 = nullptr,
1262-
const metadata::ExtentPtr &intersectingExtent2 = nullptr) const;
1262+
const metadata::ExtentPtr &intersectingExtent2 = nullptr,
1263+
bool skipIntermediateExtentIntersection = false) const;
12631264

12641265
PROJ_DLL std::string getOfficialNameFromAlias(
12651266
const std::string &aliasedName, const std::string &tableName,
@@ -1326,7 +1327,8 @@ class PROJ_GCC_DLL AuthorityFactory {
13261327
bool considerKnownGridsAsAvailable, bool discardSuperseded,
13271328
const std::vector<std::string> &allowedAuthorities,
13281329
const metadata::ExtentPtr &intersectingExtent1,
1329-
const metadata::ExtentPtr &intersectingExtent2) const;
1330+
const metadata::ExtentPtr &intersectingExtent2,
1331+
bool skipIntermediateExtentIntersection = false) const;
13301332

13311333
typedef std::pair<common::IdentifiedObjectNNPtr, std::string>
13321334
PairObjectName;

src/iso19111/factory.cpp

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7481,6 +7481,9 @@ static bool useIrrelevantPivot(const operation::CoordinateOperationNNPtr &op,
74817481
* must intersect.
74827482
* @param intersectingExtent2 Optional extent that the resulting operations
74837483
* must intersect.
7484+
* @param skipIntermediateExtentIntersection When true, skip the requirement
7485+
* that the extents of the two intermediate operations must intersect each
7486+
* other. This is useful when SourceTargetCRSExtentUse::NONE is set.
74847487
* @return list of coordinate operations
74857488
* @throw NoSuchAuthorityCodeException if there is no matching object.
74867489
* @throw FactoryException in case of other errors.
@@ -7497,7 +7500,8 @@ AuthorityFactory::createFromCRSCodesWithIntermediates(
74977500
ObjectType allowedIntermediateObjectType,
74987501
const std::vector<std::string> &allowedAuthorities,
74997502
const metadata::ExtentPtr &intersectingExtent1,
7500-
const metadata::ExtentPtr &intersectingExtent2) const {
7503+
const metadata::ExtentPtr &intersectingExtent2,
7504+
bool skipIntermediateExtentIntersection) const {
75017505

75027506
std::vector<operation::CoordinateOperationNNPtr> listTmp;
75037507

@@ -7669,9 +7673,12 @@ AuthorityFactory::createFromCRSCodesWithIntermediates(
76697673
auto params = ListOfParams{sourceCRSAuthName, sourceCRSCode,
76707674
targetCRSAuthName, targetCRSCode};
76717675
std::string additionalWhere(
7672-
"AND v1.deprecated = 0 AND v2.deprecated = 0 "
7673-
"AND intersects_bbox(south_lat1, west_lon1, north_lat1, east_lon1, "
7674-
"south_lat2, west_lon2, north_lat2, east_lon2) = 1 ");
7676+
skipIntermediateExtentIntersection
7677+
? "AND v1.deprecated = 0 AND v2.deprecated = 0 "
7678+
: "AND v1.deprecated = 0 AND v2.deprecated = 0 "
7679+
"AND intersects_bbox(south_lat1, west_lon1, north_lat1, "
7680+
"east_lon1, south_lat2, west_lon2, north_lat2, "
7681+
"east_lon2) = 1 ");
76757682
if (!allowedAuthorities.empty()) {
76767683
additionalWhere += "AND v1.auth_name IN (";
76777684
for (size_t i = 0; i < allowedAuthorities.size(); i++) {
@@ -8123,7 +8130,8 @@ AuthorityFactory::createBetweenGeodeticCRSWithDatumBasedIntermediates(
81238130
bool considerKnownGridsAsAvailable, bool discardSuperseded,
81248131
const std::vector<std::string> &allowedAuthorities,
81258132
const metadata::ExtentPtr &intersectingExtent1,
8126-
const metadata::ExtentPtr &intersectingExtent2) const {
8133+
const metadata::ExtentPtr &intersectingExtent2,
8134+
bool skipIntermediateExtentIntersection) const {
81278135

81288136
std::vector<operation::CoordinateOperationNNPtr> listTmp;
81298137

@@ -8370,7 +8378,8 @@ AuthorityFactory::createBetweenGeodeticCRSWithDatumBasedIntermediates(
83708378
auto bbox2 = metadata::GeographicBoundingBox::create(
83718379
trfmTarget.west, trfmTarget.south, trfmTarget.east,
83728380
trfmTarget.north);
8373-
if (!bbox1->intersects(bbox2))
8381+
if (!skipIntermediateExtentIntersection &&
8382+
!bbox1->intersects(bbox2))
83748383
continue;
83758384
bool okBbox2 = true;
83768385
for (const auto bbox : extraBbox)

src/iso19111/operation/coordinateoperationfactory.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2182,7 +2182,8 @@ CoordinateOperationFactory::Private::findsOpsInRegistryWithIntermediate(
21822182
authorities.size() > 1
21832183
? authorities
21842184
: std::vector<std::string>(),
2185-
context.extent1, context.extent2);
2185+
context.extent1, context.extent2,
2186+
!context.disallowEmptyIntersection());
21862187
} else {
21872188
io::AuthorityFactory::ObjectType intermediateObjectType =
21882189
io::AuthorityFactory::ObjectType::CRS;
@@ -2216,7 +2217,8 @@ CoordinateOperationFactory::Private::findsOpsInRegistryWithIntermediate(
22162217
authorities.size() > 1
22172218
? authorities
22182219
: std::vector<std::string>(),
2219-
context.extent1, context.extent2);
2220+
context.extent1, context.extent2,
2221+
!context.disallowEmptyIntersection());
22202222
}
22212223
if (!res.empty()) {
22222224

@@ -6581,7 +6583,7 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog(
65816583
auto op = createHorizVerticalHorizPROJBased(
65826584
sourceCRS, targetCRS, srcToInterp,
65836585
verticalTransform, interpToTarget,
6584-
interpolationGeogCRS, true);
6586+
interpolationGeogCRS, context.disallowEmptyIntersection());
65856587
res.emplace_back(op);
65866588
hasVerticalTransformWithInterpGeogCRS = true;
65876589
} catch (const std::exception &) {
@@ -7186,7 +7188,7 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToCompound(
71867188
try {
71877189
auto op = createHorizVerticalHorizPROJBased(
71887190
sourceCRS, targetCRS, opSrc, verticalTransform, opDst,
7189-
interpolationCRS, true);
7191+
interpolationCRS, context.disallowEmptyIntersection());
71907192
res.emplace_back(op);
71917193
} catch (const InvalidOperationEmptyIntersection &) {
71927194
} catch (const io::FormattingException &) {

test/unit/test_operationfactory.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12142,3 +12142,63 @@ TEST(operation, createOperation_defmodel_from_database) {
1214212142
"+step +proj=unitconvert +xy_in=rad +z_in=m +xy_out=deg +z_out=m "
1214312143
"+step +proj=axisswap +order=2,1");
1214412144
}
12145+
12146+
// ---------------------------------------------------------------------------
12147+
12148+
// Test that createHorizVerticalHorizPROJBased respects
12149+
// SourceTargetCRSExtentUse::NONE in compound CRS pipeline composition.
12150+
// Before the fix, the two call sites in createHorizVerticalHorizPROJBased
12151+
// hard-coded checkExtent=true, silently dropping valid horizontal+vertical
12152+
// operation combinations when their registered extents didn't overlap.
12153+
TEST(operation, compoundCRS_to_compoundCRS_context_extent_use_none) {
12154+
auto authFactory =
12155+
AuthorityFactory::create(DatabaseContext::create(), "EPSG");
12156+
12157+
// NAD27 + NGVD29 height (ftUS) --> NAD83(NSRS2007) + NAVD88 height
12158+
// This exercises the compound-to-compound code path through
12159+
// createHorizVerticalHorizPROJBased. The vertical transforms (VERTCON)
12160+
// have CONUS-only extents, while the horizontal transforms
12161+
// (NAD27 to WGS 84 via Alaska / Canada regional variants composed with
12162+
// WGS 84 to NAD83(NSRS2007)) include operations with extents outside
12163+
// CONUS. Combining an Alaska horizontal transform with a CONUS-only
12164+
// VERTCON grid produces an empty extent intersection, which was
12165+
// incorrectly rejected even when the user requested NONE.
12166+
12167+
size_t countDefault;
12168+
{
12169+
auto ctxt =
12170+
CoordinateOperationContext::create(authFactory, nullptr, 0.0);
12171+
ctxt->setGridAvailabilityUse(
12172+
CoordinateOperationContext::GridAvailabilityUse::
12173+
IGNORE_GRID_AVAILABILITY);
12174+
ctxt->setSpatialCriterion(
12175+
CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION);
12176+
auto list = CoordinateOperationFactory::create()->createOperations(
12177+
authFactory->createCoordinateReferenceSystem("7406"),
12178+
authFactory->createCoordinateReferenceSystem("5500"), ctxt);
12179+
countDefault = list.size();
12180+
ASSERT_GE(countDefault, 1U);
12181+
}
12182+
12183+
size_t countNone;
12184+
{
12185+
auto ctxt =
12186+
CoordinateOperationContext::create(authFactory, nullptr, 0.0);
12187+
ctxt->setGridAvailabilityUse(
12188+
CoordinateOperationContext::GridAvailabilityUse::
12189+
IGNORE_GRID_AVAILABILITY);
12190+
ctxt->setSpatialCriterion(
12191+
CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION);
12192+
ctxt->setSourceAndTargetCRSExtentUse(
12193+
CoordinateOperationContext::SourceTargetCRSExtentUse::NONE);
12194+
auto list = CoordinateOperationFactory::create()->createOperations(
12195+
authFactory->createCoordinateReferenceSystem("7406"),
12196+
authFactory->createCoordinateReferenceSystem("5500"), ctxt);
12197+
countNone = list.size();
12198+
}
12199+
12200+
// With NONE, extent-disjoint compound pipeline combinations (such as
12201+
// Alaska NAD27->WGS84 horizontal + CONUS VERTCON vertical) are no longer
12202+
// silently filtered, so we expect strictly more results.
12203+
EXPECT_GT(countNone, countDefault);
12204+
}

0 commit comments

Comments
 (0)