Skip to content

Commit

Permalink
Merge pull request #60767 from qgis/backport-60759-to-release-3_42
Browse files Browse the repository at this point in the history
[Backport release-3_42] Always fallback to default coordinate operations when bounding box transfrom fails
  • Loading branch information
alexbruy authored Feb 26, 2025
2 parents 9d6b3b4 + 60fd522 commit 01369ae
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 3 deletions.
24 changes: 21 additions & 3 deletions src/core/proj/qgscoordinatetransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -676,9 +676,27 @@ QgsRectangle QgsCoordinateTransform::transformBoundingBox( const QgsRectangle &r
proj_errno_reset( projData );
// proj documentation recommends 21 points for densification
constexpr int DENSIFY_POINTS = 21;
const int projResult = proj_trans_bounds( projContext, projData, ( direction == Qgis::TransformDirection::Forward && !d->mIsReversed ) || ( direction == Qgis::TransformDirection::Reverse && d->mIsReversed ) ? PJ_FWD : PJ_INV,
xMin, yMin, xMax, yMax,
&transXMin, &transYMin, &transXMax, &transYMax, DENSIFY_POINTS );
int projResult = proj_trans_bounds( projContext, projData, ( direction == Qgis::TransformDirection::Forward && !d->mIsReversed ) || ( direction == Qgis::TransformDirection::Reverse && d->mIsReversed ) ? PJ_FWD : PJ_INV,
xMin, yMin, xMax, yMax,
&transXMin, &transYMin, &transXMax, &transYMax, DENSIFY_POINTS );

if ( ( projResult != 1
|| !std::isfinite( transXMin )
|| !std::isfinite( transXMax )
|| !std::isfinite( transYMin )
|| !std::isfinite( transYMax ) )
&& ( d->mAvailableOpCount > 1 || d->mAvailableOpCount == -1 ) // only use fallbacks if more than one operation is possible -- otherwise we've already tried it and it failed
)
{
// fail #1 -- try with getting proj to auto-pick an appropriate coordinate operation for the points
if ( PJ *transform = d->threadLocalFallbackProjData() )
{
projResult = proj_trans_bounds( projContext, transform, ( direction == Qgis::TransformDirection::Forward && !d->mIsReversed ) || ( direction == Qgis::TransformDirection::Reverse && d->mIsReversed ) ? PJ_FWD : PJ_INV,
xMin, yMin, xMax, yMax,
&transXMin, &transYMin, &transXMax, &transYMax, DENSIFY_POINTS );
}
}

if ( projResult != 1
|| !std::isfinite( transXMin )
|| !std::isfinite( transXMax )
Expand Down
45 changes: 45 additions & 0 deletions tests/src/python/test_qgscoordinatetransform.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,51 @@ def testTransformQgsRectangle_Regression17600(self):
myTransformedExtentReverse.yMinimum(), myExtent.yMinimum()
)

def test_transform_bounding_box_grid(self):
"""
This test assumes the ca_nrc_NA83SCRS.tif grid is available on the system!
"""
transform = QgsCoordinateTransform(
QgsCoordinateReferenceSystem("EPSG:4269"),
QgsCoordinateReferenceSystem("EPSG:3857"),
QgsCoordinateTransformContext(),
)
res = transform.transformBoundingBox(
QgsRectangle(
-123.65020876249999,
45.987175336410544,
-101.22289073749998,
62.961980263589439,
)
)
self.assertAlmostEqual(res.xMinimum(), -13764678, -2)
self.assertAlmostEqual(res.yMinimum(), 5778294, -2)
self.assertAlmostEqual(res.xMaximum(), -11268080, -2)
self.assertAlmostEqual(res.yMaximum(), 9090934, -2)

transform = QgsCoordinateTransform(
QgsCoordinateReferenceSystem("EPSG:4269"),
QgsCoordinateReferenceSystem("EPSG:3857"),
QgsCoordinateTransformContext(),
)
# force use of grid shift operation. This will fail as the source rect is outside of the grid bounds, but we should silently
# fall back to the non-grid operation
transform.setCoordinateOperation(
"+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift +grids=ca_nrc_NA83SCRS.tif +step +proj=webmerc +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84"
)
res = transform.transformBoundingBox(
QgsRectangle(
-123.65020876249999,
45.987175336410544,
-101.22289073749998,
62.961980263589439,
)
)
self.assertAlmostEqual(res.xMinimum(), -13764678, -2)
self.assertAlmostEqual(res.yMinimum(), 5778294, -2)
self.assertAlmostEqual(res.xMaximum(), -11268080, -2)
self.assertAlmostEqual(res.yMaximum(), 9090934, -2)

def testContextProj6(self):
"""
Various tests to ensure that datum transforms are correctly set respecting context
Expand Down

0 comments on commit 01369ae

Please sign in to comment.