Skip to content

reproject(bounds_policy="auto") crops ordinary geographic-to-projected reprojections #2582

@brendancol

Description

@brendancol

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:

  1. Non-finite fraction of raw (unclamped) edge samples in the target CRS.
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions