Describe the bug
reproject(..., bounds_policy="auto") crops valid edge data on ordinary geographic-to-projected reprojections. The blow-up heuristic in xrspatial/reproject/_grid.py (around line 255) compares the source span in source CRS units (e.g. degrees for EPSG:4326) against the output span in target CRS units (e.g. metres for EPSG:3857). When the units differ, the ratio is meaningless. The > 50x threshold trips on almost any geographic-to-projected case, forcing the 2/98 percentile fallback even when the reprojection is well-behaved.
Reproduction
import numpy as np
import xarray as xr
from xrspatial.reproject import reproject
data = np.random.RandomState(0).rand(64, 64).astype(np.float32)
r = xr.DataArray(
data, dims=['y', 'x'],
coords={'y': np.linspace(10, -10, 64), 'x': np.linspace(-10, 10, 64)},
attrs={'crs': 'EPSG:4326'},
)
auto = reproject(r, 'EPSG:3857', bounds_policy='auto')
raw = reproject(r, 'EPSG:3857', bounds_policy='raw')
# auto x span 2,120,376 m vs raw 2,226,395 m, 106 km cropped
# auto y span 2,155,716 m vs raw 2,226,395 m, 70 km cropped
The source bbox is (-10, -10, 10, 10) in EPSG:4326, nowhere near a singularity. Under auto, roughly 70-106 km per side gets trimmed compared to raw.
Expected behavior
bounds_policy="auto" should leave benign reprojections alone and only trip the percentile fallback when there is an actual projection singularity (antimeridian wrap, pole near a non-polar projection, etc.).
Suggested fix
Replace the unit-mismatched ratio comparison with a unit-agnostic signal. Two options:
- Non-finite fraction of raw (unclamped) edge samples in the target CRS.
- Ratio of max absolute coordinate to median absolute coordinate among projected samples. Benign cases stay near 1-2; real singularities produce ratios above 10.
Option 2 handles both inf-producing singularities (Mercator at the poles) and finite-but-astronomically-large singularities (polar stereographic at the opposite pole).
Test plan
- Regression test: EPSG:4326 -> EPSG:3857 on (-10,-10,10,10) under
bounds_policy="auto" matches the raw output bounds to a small tolerance.
- Pathological case still trips: EPSG:4326 global -> polar stereographic still falls back to percentile bounds.
- Existing tests (
test_raw_skips_clamp_and_percentile, test_warns_when_percentile_fires_under_auto, test_no_warning_on_benign_input) keep passing.
Describe the bug
reproject(..., bounds_policy="auto")crops valid edge data on ordinary geographic-to-projected reprojections. The blow-up heuristic inxrspatial/reproject/_grid.py(around line 255) compares the source span in source CRS units (e.g. degrees for EPSG:4326) against the output span in target CRS units (e.g. metres for EPSG:3857). When the units differ, the ratio is meaningless. The > 50x threshold trips on almost any geographic-to-projected case, forcing the 2/98 percentile fallback even when the reprojection is well-behaved.Reproduction
The source bbox is (-10, -10, 10, 10) in EPSG:4326, nowhere near a singularity. Under
auto, roughly 70-106 km per side gets trimmed compared toraw.Expected behavior
bounds_policy="auto"should leave benign reprojections alone and only trip the percentile fallback when there is an actual projection singularity (antimeridian wrap, pole near a non-polar projection, etc.).Suggested fix
Replace the unit-mismatched ratio comparison with a unit-agnostic signal. Two options:
Option 2 handles both inf-producing singularities (Mercator at the poles) and finite-but-astronomically-large singularities (polar stereographic at the opposite pole).
Test plan
bounds_policy="auto"matches therawoutput bounds to a small tolerance.test_raw_skips_clamp_and_percentile,test_warns_when_percentile_fires_under_auto,test_no_warning_on_benign_input) keep passing.