Skip to content

Releases: xarray-contrib/xarray-spatial

v0.10.9

15 Jun 19:23
ef65fa3

Choose a tag to compare

Bug fixes and improvements

  • cost_distance: route bounded large-radius dask path to iterative Dijkstra (#3345)
  • cost_distance: guard the numpy dask chunk path with _check_memory (#3346)
  • cost_distance: fix leaking dask task name as output .name (#3349)
  • cost_distance: replace mutable default target_values=[] with a None sentinel (#3348)
  • cost_distance: add tests for 1x1 raster, Inf friction, and metadata (#3347)
  • cost_distance: fix flake8/isort findings (#3350)
  • contour: batch dask chunk compute to bound client memory (#3334)
  • contour: remove redundant np.empty_like allocation in the coordinate transform (#3354)
  • contour: add test coverage for all-equal auto-levels and empty explicit levels (#3353)
  • geotiff: reject a zero or non-finite ModelPixelScale on read (#3332)
  • geotiff: fix isort import ordering in _writers/eager.py (#3330)
  • docs: fix file paths in the deep-sweep documentation (#3336)

Thanks

Thanks to @Melissari1997 for code and test contributions to this release.

v0.10.8

15 Jun 12:47
e2c258b

Choose a tag to compare

Bug fixes and improvements

  • geotiff: gate dict gdal_metadata behind the experimental rich-tag opt-in (#3327)
  • name dask tasks for remaining xrspatial tool modules (#3326)
  • geotiff: don't flip masked_nodata True on a caller dtype= cast of an unmasked buffer (#3325)
  • to_geotiff: type-check compression_level (#3324)
  • rasterize: burn all_touched lines with the supercover walk (#3322)
  • focal: handle empty rasters consistently across backends (#3319)
  • reproject: promote large-output in-memory cupy inputs to dask (#3318)
  • reproject: serialize all parallel=True kernel launches behind one lock (#3317)
  • docs: add an examples-and-data getting-started page and landing index (#3316)

v0.10.7

14 Jun 04:51
9c1a81a

Choose a tag to compare

Version 0.10.7 - 2026-06-13

New features

  • to_geotiff: add pack=True, the inverse of mask_and_scale (#3064) (#3065)
  • open_geotiff: add coregister=True (mask_and_scale + reproject + match grid) (#3069) (#3070) (#3072)
  • open_geotiff: pick reproject resampling by raster type under auto_reproject=True (#3067) (#3068)
  • open_geotiff: rename mask_and_scale to unpack and add GPU / dask+GPU support (#3075)

Bug fixes and improvements

  • polygonize: reject invalid return_type before running the computation (#3312)
  • polygonize: document that column_name is also used for geojson output (#3309)
  • kriging: keep template backend and variance name in the singular-matrix fallback (#3294)
  • rasterize: drop non-finite-coordinate geometries and clamp row casts (#3310)
  • merge: add GPU tests for mixed-type first/last ordering (#3296) (#3302)
  • interpolate: add edge-case and parameter test coverage (#3300)
  • polygonize: honor _xrspatial_no_georef in CRS detection (#3293) (#3308)
  • polygonize: jit the float-chunk region ranges scan (#3303) (#3314)
  • rasterize: burn each geometry once per pixel under merge='sum'/'count' (#3313)
  • interpolate: cut redundant work in the backends (#3311)
  • rasterize: widen the dtype annotation to the accepted forms (#3291) (#3297)
  • kriging: name the singular-fallback variance '{name}_variance' (#3289)
  • polygonize: cover the dask mask-rechunk path (#3299) (#3305)
  • polygonize: raise on uint32 region overflow in the cupy integer backend (#3292) (#3301)
  • interpolate: drop unused math import and fix isort drift (#3286) (#3287)
  • docs: add a why-xarray-spatial page, retire raster_huh (#3249) (#3284)
  • geotiff: fill pack=True nodata holes at the integer dtype's native width (#3277)
  • ci: add a docs-build job (#3252)
  • projection: read ITRF Helmert scale as ppm to match PROJ's data files (#3283)
  • projection: reject non-WGS84 ellipsoids in the fast-path dispatch (#3282)
  • projection: fix LAEA inverse rq normalization and authalic inverse series (#3280)
  • reproject: upload shared coordinate arrays once per chunk in multi-band cupy (#3279)
  • reproject: promote to the dask path when the output is large (#3278)
  • geotiff: export VRTUnsupportedError, CloudSizeLimitError, PixelSafetyLimitError publicly (#3265) (#3273)
  • geotiff: refuse pack=True values the packed integer dtype cannot hold (#3272)
  • merge: keep the shared integer input dtype instead of promoting to float64 (#3271)
  • geotiff: add gpu and dask+gpu legs to the pack=True feature tests (#3270)
  • geotiff: correct the unpack docstring (masking still runs without scale/offset) (#3263) (#3269)
  • geotiff: rewrap _attrs import in _writers/gpu.py to isort line_length=100 (#3259) (#3261)
  • docs: replace the usage page with a runnable quickstart (#3249) (#3258)
  • docs: rewrite the installation page (environments, extras, GPU and cloud notes) (#3257)
  • dask: name compute tasks xrspatial. for readable task graphs (#3255)
  • docs: fix stale Datashader claim and hardcoded links in getting-started (#3249) (#3251)
  • geotiff: coregister=True reads COG overviews and fails fast on bad templates (#3254)
  • geotiff: document coregister=True and register it as an experimental feature (#3248)
  • geotiff: warn when coregister=True covers only a small fraction of the caller grid (#3247)
  • proximity: replace the numpy line-sweep with an exact cKDTree query (#3245)
  • projection: add dask end-to-end warning test, lock note on _crs_to_dict (#3242) (#3244)
  • projection: silence the pyproj lossy-PROJ-string warning in param extractors (#3242) (#3243)
  • geotiff: stream dask input through the GPU writer one tile-row band at a time (#3241)
  • geotiff: fix to_geotiff(pack=True) crash on cupy and dask+cupy input (#3240)
  • geotiff: keep float32 sources float32 through to_geotiff(pack=True) (#3080) (#3239)
  • hotspots: raise instead of returning all zeros when the raster contains Inf (#3238)
  • geotiff: defer the pack=True NaN guard into the dask graph so chunks compute once (#3237)
  • geotiff: forward caller chunking to the coregister/auto_reproject output (#3236)
  • visibility: add cupy backend, NaN, and Fresnel-blocked coverage (#3202)
  • visibility: fix the cupy backend crash in cumulative_viewshed/visibility_frequency (#3205)
  • zonal: add GPU backend coverage for regions and crosstab (#3203)
  • focal: make the kernel memory guard dtype-aware (#3223) (#3232)
  • focal: resolve apply() default func per backend (#3215) (#3224)
  • focal: add edge-case and parameter tests from the test-coverage sweep (#3230)
  • hotspots: fix the docstring backend list and kernel weight semantics (#3216) (#3227)
  • clip_polygon: align dask+cupy mask chunks before map_blocks (#3186) (#3204)
  • focal: make output dtype consistent across backends (#3226)
  • clip_polygon: use the largest chunk for the rasterize mask on dask (#3191) (#3201)
  • visibility: compute the dask source once in cumulative_viewshed (#3185) (#3199)
  • polygon_clip: remove unused imports and fix import order (#3198)
  • visibility: align output name and frequency_mhz hint with viewshed (#3183) (#3195)
  • geotiff: fix to_geotiff dask+cupy crash in the CPU streaming writer (gpu=False) (#3171)
  • clip_polygon: make nodata dtype consistent across backends (#3208)
  • mcda: fix the owa() dask path and batch wpm() validation into one scheduler pass (#3158)
  • hypsometric_integral: validate raster inputs (#3211)
  • regions: vectorize the relabel loop (#3207) (#3212)
  • mcda: fix cupy and dask backend failures in owa, standardize, and sensitivity (#3146) (#3157)
  • mcda: fix API consistency (owa weights param, boolean_overlay Dataset input, ahp_weights docs, ConsistencyResult export) (#3148) (#3155)
  • mcda: add cupy, dask, metadata, and edge-case test coverage (#3149) (#3156)
  • mcda: guard monte_carlo dask materialization against OOM (#3145) (#3153)
  • proximity: add coverage tests (int rasters, bounded GPU metrics, empty input) (#3139) (#3140)
  • geotiff: add reader.unpack / writer.pack to SUPPORTED_FEATURES at experimental tier (#3172)
  • focal: preserve input float dtype in mean() across all backends (#3214) (#3221)
  • proximity: fix stale proximity/allocation/direction docstrings (#3091) (#3138)
  • visibility: fix isort import-ordering (#3182) (#3189)
  • geotiff: thread the nodata kwarg into _pack's NaN fill so pack=True holes match GDAL_NODATA (#3168) (#3174)
  • mcda: remove unused warnings import, fix isort drift (#3143) (#3144)
  • geotiff: rewrite stale per-band SCALE/OFFSET in to_geotiff(pack=True) after band-subset reads (#3175)
  • geotiff: validate compression_level before GPU dispatch and pass it to fallback codecs (#3167) (#3176)
  • vrt: place non-georeferenced tiles by explicit pixel offsets (#3116) (#3135)
  • projection: document actual return types of vertical datum functions (#3097) (#3134)
  • mcda: keep standardize piecewise/categorical on the device for cupy inputs (#3159)
  • geotiff: fix the compression corpus fixture path so oracle tests actually run (#3164) (#3169)
  • geotiff: warn when the GPU writer materializes dask input; scope streaming docs to the CPU path (#3173)
  • focal: hoist the input cast out of the cupy focal_stats loop (#3231) (#3233)
  • focal: make the memory guard backend-aware for dask input (#3218) (#3228)
  • clip_polygon: add backend/edge-case/metadata test coverage (#3197) (#3206)
  • docs: fix the README to_geotiff example that used the gated JPEG codec (#3162) (#3170)
  • zonal: tighten crosstab zone_ids/cat_ids type hints and fix stale nodata docstring (#3196)
  • mcda: preserve input attrs in constrain() (#3147) (#3154)
  • reproject: keep integer dtype for empty chunks (#3096) (#3133)
  • mcda: make monte_carlo sensitivity chunk-bounded on dask and drop constrain() deep copy (#3160)
  • rasterize: drop two avoidable full-raster allocations in the backends (#3107) (#3131)
  • proximity: fix bounded dask GREAT_CIRCLE missing antimeridian targets (#3108) (#3130)
  • geotiff: mask 64-bit integer sentinels at native width in eager reads (#3098) (#3128)
  • reproject: accept cupy and dask+cupy inputs in merge() (#3095) (#3125)
  • geotiff: add xfail'd GPU and dask+GPU pack=True round-trip tests (#3114) (#3127)
  • proximity: sort dask KDTree targets row-major so the tie-break matches numpy (#3090) (#3124)
  • proximity: hoist the line-sweep kernel to module level to stop per-call recompiles (#3103) (#3126)
  • rasterize: add all_touched-line and non-iterable-input coverage tests (#3120)
  • rasterize: rename use_cuda to gpu with a deprecation shim (#3089) (#3122)
  • geotiff: reject zero and non-finite SCALE/OFFSET under unpack=True; guard pack=True against divide-by-zero (#3104) (#3129)
  • reproject: gate the CUDA fast path on WGS84-compatible datums (#3094) (#3119)
  • geotiff: group streaming-write rows into bands so source chunks compute once (#3117) (#3136)
  • reproject: skip the duplicate try_numba_transform probe in the numpy chunk worker (#3123)
  • reproject: remove the obsolete strict xfail on the streaming 3-D test (#3100) (#3181)
  • reproject: add tests for the streaming fallback path (#3101) (#3118)
  • reproject: match streaming output dtype to the other backends (#3093) (#3111)
  • geotiff: name unpack instead of the deprecated mask_and_scale alias in docs and errors (#3115)
  • geotiff: keep regression tests and sweep state after #3109 landed the guard (#3088) (#3110)
  • proximity: build dask coordinate grids with chunk-aligned broadcast (#3132) (#3137)
  • rasterize: propagate GeoDataFrame CRS to output when like has none (#3087) (#3113)
  • rasterize: reject non-finite burn values against integer and bool output dtypes (#3085) (#3109)
  • reproject: remove unused import and dead local, fix import order in init.py (#3083) (#3092)
  • geotiff: fix isort import-ordering drift (#3082) (#3084)
  • merge: make the output-size guard backend-aware (#3048) (#3081)
  • geotiff: restore the nodata sentinel for float rasters in to_geotiff(pack=True) (#3078) (#3079)
  • projection: silence the pyproj PROJ-string warning in _get_datum_params (#3076) (#3077)
  • resample: add ...
Read more

v0.10.6

09 Jun 01:13
b24e4d5

Choose a tag to compare

New features

  • emerging_hotspots: compute the Getis-Ord Gi* statistic (#3040)
  • re-export open_geotiff and to_geotiff from top-level xrspatial (#3005) (#3006)
  • add Cursor IDE support (.cursorrules and .cursor/rules/) (#3019)

Bug fixes and improvements

  • reproject: fix flake8/isort style issues (#3052)
  • reproject: parametrize dask+cupy parity over resampling modes (#3050) (#3051)
  • reproject: make the output-size guard backend-aware (#3046) (#3047)
  • contours: add a cross-backend NaN-input parity test (#3045)
  • sweep: drop merge=union for state CSVs (#2754) (#3043)
  • flow_path_dinf: fix a hang on cyclic D-inf flow directions (#2796) (#3042)
  • viewshed: fix the broken bounds-check guard in _calculate_event_row_col() (#2793) (#3035)
  • viewshed: validate observer_elev and target_elev before dispatch (#2794) (#3037)
  • warnings: name the calling function in the unit-mismatch warning (#2782) (#3036)
  • stream_link_mfd / stream_order_mfd: keep the dask assembly lazy (#2885) (#3039)
  • remove dead datum-shift machinery (#2659) (#3038)
  • polygonize: fix dask 8-connectivity float divergence at large rtol (#2677) (#3041)
  • viewshed: validate regular grid spacing (#2789) (#3034)
  • geotiff: reject to_geotiff(compression=None) with a clear TypeError (#2978) (#3029)
  • geotiff: fix UnboundLocalError on restore_sentinel in plain-ndarray VRT tiled write (#2969) (#3028)
  • contours: add tests for the all-NaN auto-levels RuntimeWarning regression (#2795) (#3009)
  • geotiff: thread sidecar_origin through the HTTP/fsspec sidecar georef path (#3027)
  • geotiff: remove stale external-overview skips on dask/fsspec/GPU golden-corpus backends (#3026)
  • geotiff: route remote sidecar parse failures through the shared policy (#3022) (#3025)
  • geotiff: size the streaming write budget from source chunks, not output tiles (#3008)
  • contours: fix the border collar on integer dask input (#3020) (#3021)
  • contours: fix return_type validation to fail fast on invalid values (#3010)

Contributors

v0.10.5

07 Jun 03:02
43ae72c

Choose a tag to compare

New features

  • add Kilo CLI command definitions (#3003)

Bug fixes and improvements

  • contours: add geometry deduplication (#2790) (#3001)
  • geotiff: cover compress()/decompress() dispatcher error branches (#2995) (#2996)
  • geotiff: fail closed on malformed GDAL_METADATA XML under mask_and_scale (#2999)
  • geotiff: enforce tile_size positivity in array-level writers (#2997) (#3000)
  • geotiff: reject mixed per-band SCALE/OFFSET under mask_and_scale (#2988) (#2993)
  • geotiff: promote int to float on masked read regardless of sentinel hit (#2990) (#2994)
  • geotiff: reject malformed SCALE/OFFSET under mask_and_scale (#2992)
  • vrt: surface chunked decode-time hole gap under missing_sources='warn' (#2989) (#2991)
  • geotiff: cover GPU writer degenerate shapes (1x1/1xN/Nx1) (#2986)
  • geotiff: accumulate GPU overview mean in float64 for CPU byte parity (#2983) (#2985)
  • accessor: surface standalone docstrings on .xrs methods; fix broken hydro delegations (#2982)
  • geotiff: align direct backend mask_nodata default with open_geotiff (#2976) (#2980)
  • vrt: privatize build_vrt; route to_geotiff's VRT path through it (#2979)
  • geotiff: reject non-string compression at the to_geotiff boundary (#2975) (#2977)
  • geotiff: cover degenerate-shape reads on dask and GPU backends (#2973)
  • geotiff: fix flake8/isort style drift in tests and two source imports (#2970) (#2972)
  • vrt: stop build_vrt fabricating a GeoTransform for non-georeferenced sources (#2966) (#2971)
  • geotiff: clean up VRT staging dir on early validation failures (#2964) (#2968)
  • geotiff: write VRT index atomically via temp-then-rename (#2965) (#2967)
  • geotiff: align open_geotiff parameters with rioxarray's open_rasterio (#2961) (#2963)
  • geotiff: consolidate public API on open_geotiff / to_geotiff (#2962)

Contributors

Thanks to @brendancol and @Melissari1997 for their contributions to this release.

v0.10.4

05 Jun 13:15
489c54f

Choose a tag to compare

Bug fixes and improvements

  • kriging: guard single-point input in the variogram (#2924)
  • kriging: zero the matrix diagonal so the nugget is not placed on it (#2922)
  • polygonize: cast the float mask to bool before combining it with nan_mask (#2623) (#2913)
  • spline: upload invariant point/weight data to the GPU once on the dask+cupy path (#2929)
  • kriging: skip the full-grid k0 memory term for dask templates (#2923) (#2926)
  • kriging: fix flake8 E128 and isort drift in interpolate/_kriging.py (#2916) (#2918)
  • geotiff: extract a shared GeoTIFF fixture builder (#2912)
  • Add GPU and edge-case test coverage for spline() (#2927)
  • Add kriging test coverage for dask+numpy variance, nlags, and edge cases (#2921) (#2928)
  • Add GPU backend test coverage for idw() (#2925)
  • Remove a flaky wall-clock visvalingam-whyatt regression test (#2914)
  • docs: add a 1.0.0 stability policy and LTS commitment page (#2958)

New contributors

  • Thanks to @SAY-5 (Sai Asish Y) for their first contribution (#2913)

Full changelog: v0.10.3...v0.10.4

v0.10.3

03 Jun 17:39
60185dd

Choose a tag to compare

New features

  • focal: implement the Getis-Ord Gi* statistic in hotspots() (#2803)

Bug fixes and improvements

  • slope: validate planar cell size (#2897) (#2901)
  • contours: validate return_type before doing data work (#2891) (#2898)
  • viewshed: build the GPU rtx mesh with real x/y resolution (#2861) (#2887)
  • hydro: validate MFD fraction values in public consumers (#2873) (#2883)
  • viewshed: warn and document dask Tier C divergence from the exact sweep (#2880)
  • viewshed: validate max_distance, rejecting negative and non-finite values (#2874)
  • contours: validate the n_levels contract (#2895) (#2899)
  • proximity: fix bounded dask dropping in-range targets on irregular coords (#2908) (#2909)
  • contours: drop zero-length geometry at exact-level corners (#2902)
  • viewshed: stop mutating the caller's input raster (#2852) (#2871)
  • proximity: fix bounded-dask crash on skinny rasters (#2877)
  • contours: resolve output CRS the same way as polygonize (#2893) (#2900)
  • geotiff: gate remote sources in read_geotiff_gpu under stable_only (#2867) (#2878)
  • proximity: reject non-finite target_values (#2868)
  • focal: reject non-binary kernels in apply() and focal_stats() (#2862)
  • terrain: resolve geodesic coords from real lat/lon over numeric y/x (#2903)
  • slope: set output units to degrees instead of leaking input units (#2904)
  • proximity: reject non-monotonic 1D coords in proximity/allocation/direction (#2875)
  • focal: fix hotspots() silent all-zeros on degenerate dask inputs (#2860)
  • focal: raise a clear error for non-2D custom kernels (#2842) (#2853)
  • aspect: warn on degree/meter unit mismatch in the planar path (#2839) (#2844)
  • contours: fix docstring overstating the Dask peak-memory guarantee (#2906)
  • viewshed: fix crash on NaN cells, marking them INVISIBLE (#2857) (#2876)
  • proximity: stop the dask fallback mutating the caller's input chunks (#2847) (#2864)
  • terrain: list accepted unit names in the invalid z_unit error (#2870)
  • hydro: reject cyclic MFD fraction grids instead of returning wrong results or hanging (#2884)
  • polygonize: replace the flaky visvalingam timing test with deterministic checks (#2889)
  • proximity: make allocation/direction tie-breaking deterministic across backends (#2881)
  • hydro: validate the MFD companion raster shape against the grid (#2863) (#2879)
  • geotiff: gate stable_only before the bbox metadata read on remote/VRT sources (#2882)
  • hydro: keep MFD dask output assembly lazy (#2865) (#2886)
  • viewshed: call has_cuda_and_cupy() in has_rtx() so GPU dispatch gates correctly (#2849) (#2859)
  • aspect: guard dask geodesic against single-chunk OOM (#2840) (#2846)
  • aspect: fix .name leak on dask backends when name=None (#2845)
  • slope: cover planar boundary modes on cupy and dask+cupy backends (#2832)
  • slope: fix .name leak on dask backends when name=None (#2838)
  • proximity: pin the dim-order error path and all-NaN raster across backends (#2836)
  • aspect: cover the northness/eastness method='geodesic' path (#2829) (#2835)
  • focal: fix GPU std/var precision on large-offset rasters (#2834)
  • aspect: make the planar cupy backend match numpy in [0, 360) (#2827) (#2833)
  • geotiff: enforce stable_only for HTTP/fsspec sources on non-VRT read paths (#2826)
  • geotiff: synthesize x/y coords for no-georef VRT reads (#2824)
  • geotiff: validate the gpu dispatch flag as bool and fail closed (#2819) (#2823)
  • geotiff: correct the stable_only docstring to state HTTP/fsspec rejection (#2822)
  • polygonize: optimize visvalingam_whyatt with a min-heap (#2817)
  • aspect: fix the compass-direction table in the docstring (#2825) (#2828)
  • proximity: raise on invalid distance_metric (#2814)
  • focal: make hotspots() lazy for Dask input (#2802)
  • aspect: apply cell-size correction to the planar path (#2760) (#2780)
  • slope: warn on degree coords with meter elevation in the planar path (#2764) (#2777)
  • focal: fix GPU variety undercount for kernels larger than 5x5 (#2800)
  • aspect: add an independent rectangular-cell oracle for the planar path (#2781)
  • proximity: validate negative max_distance (#2813)
  • focal: preserve input float dtype in apply() and focal_stats() (#2769) (#2805)
  • focal: validate the hotspots() kernel like apply() and focal_stats() (#2771) (#2799)
  • terrain: warn when planar resolution averages irregular coordinates (#2766) (#2784)
  • slope: propagate the NaN center cell in the planar path (#2761) (#2773)
  • proximity: fix GREAT_CIRCLE on the CPU line-sweep backend (#2816)
  • proximity: fix dask halo depth on irregular coords and 1xN/Nx1 rasters (#2815)
  • contours: fix auto-levels silently dropping all contours with +/-inf (#2801)
  • proximity: fix Great Circle docstrings to say meters, not kilometers (#2811)
  • focal: validate focal_stats() stats_funcs before dispatch (#2798)
  • slope: keep dask+cupy geodesic lat/lon lazy to avoid GPU OOM (#2776)
  • aspect: skip the full-raster geodesic memory guard for dask (#2774)
  • repo: add Codex command mirrors (#2806)
  • terrain: make the geodesic memory guard backend-aware for dask (#2765) (#2779)

v0.10.2

01 Jun 15:52
59bb4d2

Choose a tag to compare

New contributors

Bug fixes and improvements

  • contours: handle infinite NaN coords (#2749)
  • contours: validate input raster, reject complex dtype (#2711)
  • contours: keep CRS on empty geopandas result (#2708)
  • focal: pin result .name across backends in focal_stats and hotspots (#2735)
  • focal: honour boundary on the cupy backend (#2736)
  • focal: keep hotspots dask+cupy classification on the GPU (#2739)
  • focal: align signatures across mean/apply/focal_stats/hotspots (#2699)
  • aspect: keep lat/lon chunked in dask+cupy geodesic path (#2707)
  • aspect: planar dask backends report float32 dtype matching computed data (#2741)
  • slope: planar dask backends declare float32 meta dtype (#2694)
  • slope: widen name type hint to Optional[str] for terrain-family parity (#2687)
  • viewshed: consistent output name and dtype across backends (#2747)
  • viewshed: guard degenerate-axis resolution in the CPU sweep (#2745)
  • viewshed: size max_distance window per axis on anisotropic rasters (#2702)
  • proximity: consistent output dtype and .name across allocation and direction backends (#2728)
  • proximity: fix bounded GREAT_CIRCLE crash on dask backends (#2722)
  • proximity: batch per-chunk-row compute in KDTree count pass (#2726)
  • hydro: cap flow_path downstream walk to break cycles (#2718)
  • hydro: stream_order_d8 accepts method alias; basins_d8 emits DeprecationWarning (#2716)
  • resample: reject out-of-range integer nodata sentinels (#2671)
  • resample: reject empty rasters with a clear ValueError (#2665)
  • resample: reject irregular/non-monotonic spatial coordinates (#2667)
  • resample: refresh nodata/nodatavals on identity fast path (#2670)
  • resample: fix dask nearest/bilinear chunk-seam values on downsampling (#2627)
  • reproject: make transform_precision=0 force exact pyproj transforms (#2654)
  • reproject: account for datum shifts in output bounds estimation (#2655)
  • reproject: disable numba fast path for non-WGS84 datums (#2657)
  • reproject: filter footprint coordinate pairs by joint finite mask (#2652)
  • reproject: fix per-band nodata masking for multi-band inputs (#2658)
  • reproject: fix cupy backend divergence from numpy on pyproj-fallback CRS pairs (#2629)
  • reproject: rename vertical CRS kwargs to source_/target_vertical_crs (#2626)
  • zonal: raise clear ValueError for empty Dataset in stats() (#2642)
  • zonal: count returns 0 for empty zones, other stats stay NaN (#2656)
  • zonal: validate backend compatibility on the crosstab 3D path (#2653)
  • zonal: validate backend compatibility for 3D apply() inputs (#2648)
  • zonal: make apply() output .name deterministic across backends (#2622)
  • zonal: fix int32 overflow in _strides() that corrupts stats/crosstab on huge rasters (#2617)
  • crop: validate zones/values shapes match instead of returning nonsense (#2645)
  • geotiff: forward allow_rotated/allow_invalid_nodata to VRT per-source reads (#2676)
  • geotiff: make open_geotiff bbox= work for .vrt sources (#2674)
  • geotiff: make VRT tiled writes atomic to avoid poisoned partial output (#2678)
  • polygonize: batch dask per-chunk compute instead of serial loop (#2673)
  • polygonize: batch dask compute per chunk row to recover parallelism (#2632)
  • polygonize: make dask float output chunk-invariant for rtol (#2675)
  • polygonize: keep diagonal-notch hole in dask 8-conn merge (#2633)
  • polygonize: derive transform from x/y coords so geopandas CRS matches geometry (#2630)

v0.10.1

29 May 14:15
57e7c57

Choose a tag to compare

New features

  • accessor: backend-aware .xrs.open_geotiff on DataArray and Dataset (#2598)
  • geotiff: add bbox= to open_geotiff for geographic-space windowed reads (#2556)

Bug fixes and improvements

  • rasterize: honor resolution= exactly when bounds don't divide evenly (#2597)
  • reproject: fix vertical-datum shift crash on integer DEMs (#2596)
  • resample: clamp dask interp overlap depth per axis (#2547) (#2599)
  • zonal: fix trim() with NaN sentinel on numpy and cupy backends (#2559) (#2594)
  • reproject: reject explicit out-of-range nodata for integer dtypes (#2591)
  • zonal: fix cupy backend dropping all-NaN zones and desyncing zone_ids (#2562) (#2589)
  • resample: refresh transform to match actual coord orientation (#2571) (#2587)
  • rasterize: validate resolution= shape and element type (#2576) (#2586)
  • resample: preserve integer precision in nodata mask comparison (#2570) (#2592)
  • zonal: keep hypsometric_integral dask path fully lazy (#2563) (#2593)
  • geotiff: accept projected GeoTIFFs that carry a base geographic CRS (#2603)
  • polygonize: reject non-finite simplify_tolerance (#2575) (#2584)
  • reproject: unit-aware bounds_policy="auto" blow-up detection (#2600)
  • zonal: validate return_type at entry to stats() (#2558) (#2578)
  • resample: validate target_resolution and scale_factor up front (#2574) (#2590)
  • rasterize: support descending-x in like= templates (#2568) (#2595)
  • rasterize: reject zig-zag duplicate-coord patterns in _check_uniform_axis (#2585)
  • polygonize: fix dask chunk-dependent merging and DN corruption (#2601)
  • zonal: make crop() return (0, 0) on all backends when no zone matches (#2580)
  • rasterize: reject partial width/height overrides instead of silent ignore (#2579)
  • zonal: fix crosstab cat_ids overcount when earlier categories are filtered out (#2560) (#2577)
  • resample: preserve scalar non-dim coords and refresh stale nodata attr (#2542) (#2549)
  • zonal: fix hypsometric_integral dask+cupy backend (#2525) (#2529)
  • geotiff: include GB allocation estimate in max_pixels error message (#2554)
  • zonal: raise on 'majority' in zonal_stats dask path instead of silent drop (#2528) (#2531)
  • reproject: fix half-pixel offset in geoid and datum-shift grid lookups (#2508) (#2516)
  • rasterize: reject NaN fill against integer dtype (#2504) (#2512)
  • geotiff: reject distinct per-band nodatavals on write (#2514) (#2519)
  • zonal: guard unbounded allocation in stats(return_type='xarray.DataArray') (#2523) (#2533)
  • zonal: rechunk apply 3D dask output along stacked axis (#2526) (#2532)
  • rasterize: hoist poly_props/poly_global cupy.asarray above all_touched (#2506) (#2510)
  • polygonize: auto-detect attrs['transform'] for output georeferencing (#2536) (#2541)
  • geotiff: reconcile COG write stability across registry, contract, and docstring (#2520)
  • zonal: rename crop(zones_ids=) to crop(zone_ids=) with deprecation shim (#2521) (#2527)
  • reproject: preserve integer dtype in dask+cupy fast path (#2505) (#2509)
  • geotiff: scope max_pixels to the chunk when chunks= is supplied (#2501) (#2502)

v0.10.0

27 May 15:27
6401e0d

Choose a tag to compare

GeoTIFF release contract

Tiers what the public GeoTIFF and COG surface promises for this release.
xrspatial.geotiff.SUPPORTED_FEATURES is the source of truth; the bullets
below mirror it. See the reference page at docs/source/reference/geotiff.rst
for the per-tier semantics and the audit trail.

Stable

  • Local-file GeoTIFF read (open_geotiff) on the eager numpy backend, including windowed reads via window=. Covered by the window-read suite in xrspatial/geotiff/tests/. (epic #2340)
  • Dask reads (read_geotiff_dask). Cross-backend parity against the eager numpy reader is gated by test_backend_parity_matrix.py and test_backend_full_parity_2211.py. (epic #2341)
  • Local-file GeoTIFF write (to_geotiff) on the CPU writer. (epic #2340)
  • Local COG read and write (reader.local_cog, writer.cog) for axis-aligned 2D / 3D rasters with the lossless codecs none, deflate, lzw, zstd, packbits, internal overviews only, and normal CRS / transform / dtype / nodata / band / pixel-is-area / pixel-is-point round-trip. Backed by the writer compliance suite (#2292), the cross-backend parity gate (#2293), and the per-tile byte-budget contract (#2294 / #2298). (epic #2286)
  • Lossless stable codecs: none, deflate, lzw, zstd, packbits. Integer and float byte-for-byte round-trip. (epic #2340)

Advanced

  • fsspec-routed reads (reader.fsspec) and HTTPS reads (reader.http). The transport layer works but the redirect / retry / cache surface is not contracted at the stable bar. (epic #2344)
  • HTTP COG reads (reader.http_cog). Range fetching, range coalescing, the SSRF / private-host filter, and the per-tile byte cap are pinned by the byte-budget contract (#2294 / #2298), but the broader transport surface is tracked separately for stable promotion. (epic #2344)
  • VRT reads (reader.vrt). Limited to simple GDAL VRT mosaics over GeoTIFF sources that agree on CRS, transform orientation, pixel size, dtype, and band count. See the VRT support matrix in docs/source/reference/geotiff.rst. (epic #2342, PR #2321)
  • External .tif.ovr sidecar reads (reader.sidecar_ovr). (epic #2340)
  • Writer overviews and BigTIFF (writer.overviews, writer.bigtiff). (epic #2340)
  • BigTIFF COG writes (writer.bigtiff_cog). The external-interop gate lives in test_bigtiff_cog_compliance_2286.py; promotion to stable follows the same release-cycle soak rule as the rest of the COG surface. (epic #2286)

Experimental

  • GPU reads and writes (reader.gpu, writer.gpu). Cross-backend numerical parity is not claimed at this tier. (epic #2341)
  • Experimental codecs: lerc, jpeg2000, j2k, lz4. Require allow_experimental_codecs=True on both read and write paths. Reader support across GDAL versions is uneven. (epic #2340)
  • Permissive read escape hatches: allow_rotated=True (reader.allow_rotated) and allow_unparseable_crs=True (reader.allow_unparseable_crs). Both bypass a read-side check that the writer normally rejects. (epic #2340)
  • Rich-tag writes: writer.gdal_metadata_xml and writer.extra_tags. Free-form payloads are written verbatim; interop with rasterio, libtiff, and GDAL depends on the payload. Gated by allow_experimental_codecs=True on writes from a fresh DataArray; round-tripped attrs from a previous read are exempt. (epic #2340)

Internal-only

  • codec.jpeg (JPEG-in-TIFF). The encoder writes self-contained JFIF tiles without the TIFF JPEGTables tag (347), so the on-disk output is not interoperable with libtiff, GDAL, or rasterio. Reads and writes require the dedicated allow_internal_only_jpeg=True opt-in; allow_experimental_codecs=True does not unlock this path. (epic #2340)

Unsupported for this release (these combinations fail closed: the writer or reader raises rather than silently producing a wrong result)

  • Warped VRTs (<VRTDataset subClass="VRTWarpedDataset">). (PR #2321)
  • Nested VRTs (a <SourceFilename> pointing at another .vrt). (PR #2321)
  • VRT mosaics whose sources disagree on CRS, pixel size, dtype, or band count without an explicit opt-in. The default raises MixedBandMetadataError. (PR #2321)
  • to_geotiff(..., cog=True, tiled=False). COG output requires the tiled layout. (epic #2286)
  • Rotated transforms on the write path. The reader has an allow_rotated=True escape hatch in the experimental tier; the writer has no equivalent. (epic #2340)
  • 1xN / Nx1 writes without an explicit attrs['transform']. The writer used to borrow the non-degenerate axis's spacing and silently invent the wrong pixel size; the writer now raises. (epic #2340)

Bug fixes and improvements

  • Move shapely from a required dependency to an optional vector extra. shapely (and GEOS) used to load on every import xrspatial because rasterize.py imported it at module top level. It is now imported lazily, so a plain pip install xarray-spatial neither installs nor loads shapely. The only paths that need it are the vector-to-raster functions rasterize and polygonize; install pip install xarray-spatial[vector] to use them. Calling rasterize without shapely raises a clear ImportError pointing at the extra. Follows the same pattern as the matplotlib change (#2494). (#2496)
  • Move matplotlib from a required dependency to an optional plot extra. A plain pip install xarray-spatial no longer pulls in matplotlib (and its pillow / fonttools / kiwisolver / contourpy / cycler / pyparsing chain), since no compute path imports it -- every matplotlib use is a lazy import inside the .xrs.plot accessor helpers. Install pip install xarray-spatial[plot] to use plotting; calling .xrs.plot() without it now raises a clear ImportError pointing at the extra instead of a bare ModuleNotFoundError. pandas stays required because xarray depends on it and several core modules import it directly. (#2494)
  • Reconcile xrspatial.geotiff.SUPPORTED_FEATURES with the GeoTIFF release-contract tiering proposed in epic #2340. Adds reader.windowed at stable (covered by the existing window-read suite) and reader.dask at stable (covered by the cross-backend parity matrix in test_backend_parity_matrix.py and test_backend_full_parity_2211.py). Demotes reader.allow_rotated and reader.allow_unparseable_crs from advanced to experimental to match the epic's placement of permissive read-side escape hatches in the Experimental tier. A new shape test (test_supported_features_shape_2348.py) pins the structural invariants of the mapping (every entry carries a tier label; the tier set is closed at {stable, advanced, experimental, internal_only}; the dict literal contains no duplicate keys) so future drift fails CI. Runtime behaviour of every read and write path is unchanged; this is metadata-only. Callers gating on the exact string value of these two demoted entries will need to update their checks. (#2348)
  • Promote the local COG read and write paths to the stable tier in xrspatial.geotiff.SUPPORTED_FEATURES. SUPPORTED_FEATURES['writer.cog'] and SUPPORTED_FEATURES['reader.local_cog'] now report stable; reader.http_cog stays advanced while the HTTP transport surface is contracted separately. The stable COG contract covers axis-aligned 2D / 3D rasters, the CPU writer and CPU reader, the lossless codecs (none, deflate, lzw, zstd, packbits), internal overviews, and normal CRS / transform / dtype / nodata / band / pixel-is-area / pixel-is-point round-trip. GPU COG paths, experimental codecs, rotated transforms, external .tif.ovr sidecars, file-like destinations with cog=True, BigTIFF COG, and HTTP COG remain outside the contract. Backed by the writer compliance suite (#2292), the cross-backend parity gate (#2293), and the per-tile byte-budget contract (#2294 / #2298). The reference docs (docs/source/reference/geotiff.rst) and the COG overview notebook spell out the full contract. (#2300)
  • Resolve the GeoTIFF writer's GeographicTypeGeoKey / ProjectedCSTypeGeoKey decision via pyproj instead of an EPSG number range. The legacy heuristic (4326 + 4000-4999 -> geographic, else projected) silently mis-tagged geographic CRSes registered outside the 4000-4999 block (NAD83(2011) = 6318, GDA2020 = 7844, WGS 84 (G2139) = 9057, etc.) as projected and projected codes inside the block (4087 / 4088 / 4499) as geographic, corrupting the CRS at write time. The writer now calls pyproj.CRS.from_epsg(...).is_geographic. When pyproj can't classify a code (uninstalled, or installed but the local PROJ database lacks the entry), the writer raises the new UnknownCRSModelTypeError rather than guessing -- a small vetted allowlist (4326, 4269, 4267, 4258, 4283, 4322, 4230, 4019, 4047) is still honoured for the pyproj-missing case. pyproj is now listed under the geotiff extra. (#2277)
  • Shut down the per-tile compression ThreadPoolExecutor on every exit path of the streaming tiled-write code in to_geotiff. The old code only called shutdown(wait=True) after the tile-row loop completed, so any mid-stream raise (compression failure, dask compute failure, file write failure) bypassed shutdown and leaked worker threads. The loop now runs inside try/finally and the finally calls shutdown(wait=True, cancel_futures=True) so queued tiles get dropped on the error path instead of blocking the unwind. The pool's workers carry an xrspatial-geotiff-tile-compress thread_name_prefix so leak-detection tests can tell them apart from dask's own offload/scheduler pools. (#2276)
  • Remove read-side emission of the 13 deprecated GeoTIFF attrs (crs_name, geog_citation, datum_code, angular_units, semi_major_axis, inv_flattening, linear_units, projection_code, vertical_crs, vertical_citation, vertical_units, colormap_rgba, cmap) and bump attrs['_xrspatial_geotiff_contract'] from 1 to 2. Downstream code that read these via attrs[key] now sees KeyError; migrate to attrs.get(key) or derive the value from attrs['crs'] / attrs['crs_wkt'] ...
Read more