xarray-spatial 0.10.3__tar.gz → 0.10.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (581) hide show
  1. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/sweep-accuracy-state.csv +1 -0
  2. {xarray_spatial-0.10.3/.codex → xarray_spatial-0.10.5/.claude}/sweep-performance-state.csv +2 -0
  3. {xarray_spatial-0.10.3/.codex → xarray_spatial-0.10.5/.claude}/sweep-security-state.csv +1 -0
  4. {xarray_spatial-0.10.3/.codex → xarray_spatial-0.10.5/.claude}/sweep-style-state.csv +3 -2
  5. xarray_spatial-0.10.5/.claude/sweep-test-coverage-state.csv +17 -0
  6. xarray_spatial-0.10.5/.kilo/command/backend-parity.md +159 -0
  7. xarray_spatial-0.10.5/.kilo/command/bench.md +127 -0
  8. xarray_spatial-0.10.5/.kilo/command/dask-notebook.md +148 -0
  9. xarray_spatial-0.10.5/.kilo/command/deep-sweep.md +438 -0
  10. xarray_spatial-0.10.5/.kilo/command/efficiency-audit.md +274 -0
  11. xarray_spatial-0.10.5/.kilo/command/new-issues.md +113 -0
  12. xarray_spatial-0.10.5/.kilo/command/ready-to-merge.md +153 -0
  13. xarray_spatial-0.10.5/.kilo/command/release-major.md +146 -0
  14. xarray_spatial-0.10.5/.kilo/command/release-minor.md +146 -0
  15. xarray_spatial-0.10.5/.kilo/command/release-patch.md +146 -0
  16. xarray_spatial-0.10.5/.kilo/command/review-contributor-pr.md +332 -0
  17. xarray_spatial-0.10.5/.kilo/command/review-pr.md +249 -0
  18. xarray_spatial-0.10.5/.kilo/command/rockout.md +377 -0
  19. xarray_spatial-0.10.5/.kilo/command/sweep-accuracy.md +335 -0
  20. xarray_spatial-0.10.5/.kilo/command/sweep-api-consistency.md +291 -0
  21. xarray_spatial-0.10.5/.kilo/command/sweep-metadata.md +334 -0
  22. xarray_spatial-0.10.5/.kilo/command/sweep-performance.md +366 -0
  23. xarray_spatial-0.10.5/.kilo/command/sweep-security.md +334 -0
  24. xarray_spatial-0.10.5/.kilo/command/sweep-style.md +315 -0
  25. xarray_spatial-0.10.5/.kilo/command/sweep-test-coverage.md +293 -0
  26. xarray_spatial-0.10.5/.kilo/command/user-guide-notebook.md +203 -0
  27. xarray_spatial-0.10.5/.kilo/command/validate.md +216 -0
  28. xarray_spatial-0.10.5/.kilo/sweep-accuracy-state.csv +39 -0
  29. xarray_spatial-0.10.5/.kilo/sweep-api-consistency-state.csv +10 -0
  30. xarray_spatial-0.10.5/.kilo/sweep-metadata-state.csv +12 -0
  31. xarray_spatial-0.10.5/.kilo/sweep-performance-state.csv +49 -0
  32. xarray_spatial-0.10.5/.kilo/sweep-security-state.csv +49 -0
  33. xarray_spatial-0.10.5/.kilo/sweep-style-state.csv +14 -0
  34. xarray_spatial-0.10.5/.kilo/sweep-test-coverage-state.csv +17 -0
  35. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/CHANGELOG.md +52 -0
  36. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/PKG-INFO +6 -6
  37. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/README.md +5 -5
  38. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xarray_spatial.egg-info/PKG-INFO +6 -6
  39. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xarray_spatial.egg-info/SOURCES.txt +33 -0
  40. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/_version.py +3 -3
  41. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/accessor.py +92 -34
  42. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/contour.py +47 -1
  43. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/__init__.py +261 -124
  44. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_attrs.py +168 -22
  45. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_backends/_gpu_helpers.py +7 -7
  46. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_backends/dask.py +83 -48
  47. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_backends/gpu.py +57 -56
  48. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_backends/vrt.py +133 -71
  49. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_cog_http.py +3 -3
  50. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_coords.py +2 -2
  51. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_crs.py +3 -3
  52. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_decode.py +1 -1
  53. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_errors.py +23 -1
  54. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_geotags.py +43 -8
  55. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_gpu_decode.py +11 -2
  56. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_reader.py +6 -6
  57. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_runtime.py +17 -9
  58. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_validation.py +40 -25
  59. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_vrt.py +57 -6
  60. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_vrt_validation.py +4 -4
  61. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_writer.py +38 -24
  62. xarray_spatial-0.10.5/xrspatial/geotiff/_writers/__init__.py +7 -0
  63. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_writers/eager.py +171 -153
  64. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_writers/gpu.py +34 -34
  65. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_writers/vrt.py +49 -52
  66. xarray_spatial-0.10.5/xrspatial/geotiff/tests/_geotiff_fixtures.py +118 -0
  67. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/attrs/test_contract.py +3 -3
  68. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/test_dask_numpy.py +3 -5
  69. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/test_eager_numpy.py +2 -4
  70. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/test_fsspec.py +2 -4
  71. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/test_vrt.py +5 -5
  72. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/gpu/test_codec.py +52 -49
  73. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/gpu/test_kernels_and_kwargs.py +251 -240
  74. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/gpu/test_reader.py +100 -99
  75. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/gpu/test_writer.py +196 -68
  76. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/integration/test_dask_pipeline.py +36 -36
  77. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/integration/test_gpu_pipeline.py +4 -4
  78. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/integration/test_http_sources.py +23 -23
  79. xarray_spatial-0.10.5/xrspatial/geotiff/tests/parity/test_api_consolidation.py +130 -0
  80. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/parity/test_backend_matrix.py +14 -13
  81. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/parity/test_finalization.py +67 -67
  82. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/parity/test_pixel_equality.py +34 -34
  83. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/parity/test_reference.py +14 -14
  84. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/parity/test_signature_contract.py +22 -21
  85. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/read/test_basic.py +19 -18
  86. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/read/test_coords.py +2 -2
  87. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/read/test_crs.py +1 -1
  88. xarray_spatial-0.10.5/xrspatial/geotiff/tests/read/test_degenerate_shapes.py +150 -0
  89. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/read/test_dtypes.py +22 -22
  90. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/read/test_endianness.py +5 -5
  91. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/read/test_georef.py +11 -11
  92. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/read/test_nodata.py +177 -121
  93. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/read/test_overview.py +45 -45
  94. xarray_spatial-0.10.5/xrspatial/geotiff/tests/read/test_rioxarray_compat_2961.py +494 -0
  95. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/read/test_tiling.py +9 -8
  96. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/release_gates/test_features.py +49 -44
  97. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/release_gates/test_stable_features.py +23 -11
  98. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/test_edge_cases.py +2 -2
  99. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/test_polish.py +16 -16
  100. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/test_round_trip.py +6 -6
  101. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/test_security.py +2 -0
  102. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/test_stable_only_bbox_ordering_2869.py +2 -3
  103. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/test_stable_only_remote_2821.py +10 -10
  104. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/unit/test_codec_roundtrip.py +43 -9
  105. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/unit/test_geotags.py +2 -88
  106. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/unit/test_ifd.py +3 -3
  107. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/unit/test_input_validation.py +59 -59
  108. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/unit/test_metadata.py +21 -102
  109. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/unit/test_photometric.py +22 -22
  110. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/unit/test_predictor.py +4 -4
  111. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/unit/test_safe_xml.py +1 -1
  112. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/unit/test_signatures.py +218 -164
  113. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/vrt/test_dtype_conversion.py +53 -53
  114. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/vrt/test_metadata.py +187 -95
  115. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/vrt/test_missing_sources.py +194 -44
  116. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/vrt/test_parity.py +48 -47
  117. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/vrt/test_source_opt_ins_2672.py +8 -8
  118. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/vrt/test_validation.py +32 -32
  119. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/vrt/test_window.py +36 -36
  120. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/write/test_basic.py +280 -92
  121. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/write/test_bigtiff.py +3 -3
  122. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/write/test_cog.py +10 -10
  123. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/write/test_crs.py +2 -2
  124. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/write/test_nodata.py +58 -33
  125. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/write/test_overview.py +12 -12
  126. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/write/test_streaming.py +1 -1
  127. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/write/test_vrt_atomic.py +28 -0
  128. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/interpolate/_kriging.py +26 -11
  129. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/interpolate/_spline.py +29 -8
  130. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/polygonize.py +2 -2
  131. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_accessor.py +102 -0
  132. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_contour.py +120 -0
  133. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_interpolation.py +359 -0
  134. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_polygonize.py +0 -108
  135. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_polygonize_mask_dtype_coverage_2026_05_29.py +13 -15
  136. xarray_spatial-0.10.3/.claude/sweep-test-coverage-state.csv +0 -44
  137. xarray_spatial-0.10.3/xrspatial/geotiff/_writers/__init__.py +0 -7
  138. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/backend-parity.md +0 -0
  139. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/bench.md +0 -0
  140. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/dask-notebook.md +0 -0
  141. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/deep-sweep.md +0 -0
  142. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/efficiency-audit.md +0 -0
  143. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/new-issues.md +0 -0
  144. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/ready-to-merge.md +0 -0
  145. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/release-major.md +0 -0
  146. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/release-minor.md +0 -0
  147. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/release-patch.md +0 -0
  148. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/review-contributor-pr.md +0 -0
  149. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/review-pr.md +0 -0
  150. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/rockout.md +0 -0
  151. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/sweep-accuracy.md +0 -0
  152. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/sweep-api-consistency.md +0 -0
  153. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/sweep-metadata.md +0 -0
  154. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/sweep-performance.md +0 -0
  155. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/sweep-security.md +0 -0
  156. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/sweep-style.md +0 -0
  157. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/sweep-test-coverage.md +0 -0
  158. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/user-guide-notebook.md +0 -0
  159. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/commands/validate.md +0 -0
  160. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/sweep-api-consistency-state.csv +0 -0
  161. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.claude/sweep-metadata-state.csv +0 -0
  162. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/backend-parity.md +0 -0
  163. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/bench.md +0 -0
  164. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/dask-notebook.md +0 -0
  165. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/deep-sweep.md +0 -0
  166. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/efficiency-audit.md +0 -0
  167. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/new-issues.md +0 -0
  168. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/ready-to-merge.md +0 -0
  169. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/release-major.md +0 -0
  170. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/release-minor.md +0 -0
  171. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/release-patch.md +0 -0
  172. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/review-contributor-pr.md +0 -0
  173. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/review-pr.md +0 -0
  174. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/rockout.md +0 -0
  175. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/sweep-accuracy.md +0 -0
  176. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/sweep-api-consistency.md +0 -0
  177. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/sweep-metadata.md +0 -0
  178. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/sweep-performance.md +0 -0
  179. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/sweep-security.md +0 -0
  180. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/sweep-style.md +0 -0
  181. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/sweep-test-coverage.md +0 -0
  182. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/user-guide-notebook.md +0 -0
  183. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/commands/validate.md +0 -0
  184. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/sweep-accuracy-state.csv +0 -0
  185. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/sweep-api-consistency-state.csv +0 -0
  186. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/sweep-metadata-state.csv +0 -0
  187. {xarray_spatial-0.10.3/.claude → xarray_spatial-0.10.5/.codex}/sweep-performance-state.csv +0 -0
  188. {xarray_spatial-0.10.3/.claude → xarray_spatial-0.10.5/.codex}/sweep-security-state.csv +0 -0
  189. {xarray_spatial-0.10.3/.claude → xarray_spatial-0.10.5/.codex}/sweep-style-state.csv +0 -0
  190. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.codex/sweep-test-coverage-state.csv +0 -0
  191. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.efficiency-audit-baseline.json +0 -0
  192. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.efficiency-audit-baseline.prev.json +0 -0
  193. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.gitattributes +0 -0
  194. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  195. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.github/ISSUE_TEMPLATE/feature-proposal.md +0 -0
  196. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.github/ISSUE_TEMPLATE/new-contributor.md +0 -0
  197. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.github/labeler.yml +0 -0
  198. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.github/pull_request_template.md +0 -0
  199. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.github/workflows/benchmarks.yml +0 -0
  200. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.github/workflows/copilot-review.yml +0 -0
  201. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.github/workflows/labeler.yml +0 -0
  202. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.github/workflows/pypi-publish.yml +0 -0
  203. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.github/workflows/test-cog-validator.yml +0 -0
  204. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.github/workflows/test-geotiff-corpus.yml +0 -0
  205. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.github/workflows/test.yml +0 -0
  206. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.github/workflows/welcome-contributor.yml +0 -0
  207. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.gitignore +0 -0
  208. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/.readthedocs.yml +0 -0
  209. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/AI_POLICY.md +0 -0
  210. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/CLAUDE.md +0 -0
  211. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/CODE_OF_CONDUCT.md +0 -0
  212. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/CONTRIBUTING.md +0 -0
  213. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/Citation-styles.md +0 -0
  214. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/LICENSE.txt +0 -0
  215. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/MANIFEST.in +0 -0
  216. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/RELEASE.md +0 -0
  217. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/codecov.yml +0 -0
  218. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/pyproject.toml +0 -0
  219. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/setup.cfg +0 -0
  220. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/setup.py +0 -0
  221. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xarray_spatial.egg-info/dependency_links.txt +0 -0
  222. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xarray_spatial.egg-info/entry_points.txt +0 -0
  223. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xarray_spatial.egg-info/not-zip-safe +0 -0
  224. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xarray_spatial.egg-info/requires.txt +0 -0
  225. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xarray_spatial.egg-info/top_level.txt +0 -0
  226. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/__init__.py +0 -0
  227. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/__main__.py +0 -0
  228. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/analytics.py +0 -0
  229. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/aspect.py +0 -0
  230. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/balanced_allocation.py +0 -0
  231. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/bilateral.py +0 -0
  232. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/bump.py +0 -0
  233. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/classify.py +0 -0
  234. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/convolution.py +0 -0
  235. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/corridor.py +0 -0
  236. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/cost_distance.py +0 -0
  237. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/curvature.py +0 -0
  238. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/dasymetric.py +0 -0
  239. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/dataset_support.py +0 -0
  240. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/datasets/__init__.py +0 -0
  241. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/datasets/sentinel-2/blue_band.nc +0 -0
  242. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/datasets/sentinel-2/green_band.nc +0 -0
  243. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/datasets/sentinel-2/nir_band.nc +0 -0
  244. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/datasets/sentinel-2/red_band.nc +0 -0
  245. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/datasets/sentinel-2/swir1_band.nc +0 -0
  246. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/datasets/sentinel-2/swir2_band.nc +0 -0
  247. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/diagnostics.py +0 -0
  248. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/diffusion.py +0 -0
  249. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/edge_detection.py +0 -0
  250. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/emerging_hotspots.py +0 -0
  251. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/erosion.py +0 -0
  252. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/experimental/__init__.py +0 -0
  253. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/experimental/min_observable_height.py +0 -0
  254. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/fire.py +0 -0
  255. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/flood.py +0 -0
  256. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/focal.py +0 -0
  257. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geodesic.py +0 -0
  258. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_backends/__init__.py +0 -0
  259. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_compression.py +0 -0
  260. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_dtypes.py +0 -0
  261. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_encode.py +0 -0
  262. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_header.py +0 -0
  263. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_layout.py +0 -0
  264. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_nodata.py +0 -0
  265. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_overview.py +0 -0
  266. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_overview_kernels.py +0 -0
  267. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_safe_xml.py +0 -0
  268. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_sidecar.py +0 -0
  269. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_sources.py +0 -0
  270. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/_write_layout.py +0 -0
  271. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/__init__.py +0 -0
  272. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/_helpers/__init__.py +0 -0
  273. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/_helpers/markers.py +0 -0
  274. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/_helpers/tiff_builders.py +0 -0
  275. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/_helpers/tiff_surgery.py +0 -0
  276. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/attrs/__init__.py +0 -0
  277. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/bench_vs_rioxarray.py +0 -0
  278. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/conftest.py +0 -0
  279. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/README.md +0 -0
  280. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/__init__.py +0 -0
  281. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/_marks.py +0 -0
  282. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/_oracle.py +0 -0
  283. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/cog_internal_overview_uint16.tif +0 -0
  284. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/compression_deflate_predictor2_uint16.tif +0 -0
  285. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/compression_deflate_predictor3_float32.tif +0 -0
  286. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/compression_jpeg_uint8_ycbcr.tif +0 -0
  287. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/compression_lerc_float32.tif +0 -0
  288. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/compression_lzw_predictor2_int16.tif +0 -0
  289. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/compression_none_uint8.tif +0 -0
  290. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/crs_citation_only.tif +0 -0
  291. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/crs_epsg_3857.tif +0 -0
  292. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/crs_wkt_utm10n.tif +0 -0
  293. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/dtype_float32.tif +0 -0
  294. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/dtype_float64.tif +0 -0
  295. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/dtype_int16.tif +0 -0
  296. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/dtype_int32.tif +0 -0
  297. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/dtype_int8.tif +0 -0
  298. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/dtype_uint16.tif +0 -0
  299. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/dtype_uint32.tif +0 -0
  300. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/dtype_uint8.tif +0 -0
  301. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/extra_tags_uint16.tif +0 -0
  302. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/gdal_metadata_namespaced_uint16.tif +0 -0
  303. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/nodata_int_sentinel_uint16.tif +0 -0
  304. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/nodata_miniswhite_uint8.tif +0 -0
  305. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/nodata_nan_float32.tif +0 -0
  306. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/overview_external_ovr_uint16.tif +0 -0
  307. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/overview_external_ovr_uint16.tif.ovr +0 -0
  308. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/overview_internal_uint16.tif +0 -0
  309. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/planar_separate_uint8_rgb.tif +0 -0
  310. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/sparse_tiled_uint16.tif +0 -0
  311. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/stripped_be_uint16.tif +0 -0
  312. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/stripped_le_uint16.tif +0 -0
  313. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/tiled_be_uint16.tif +0 -0
  314. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/fixtures/tiled_le_uint16.tif +0 -0
  315. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/generate.py +0 -0
  316. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/manifest.yaml +0 -0
  317. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/test_compression.py +0 -0
  318. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/test_corpus_determinism.py +0 -0
  319. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/test_dask_gpu.py +0 -0
  320. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/test_dtype_variants.py +0 -0
  321. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/test_gpu.py +0 -0
  322. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/test_http.py +0 -0
  323. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/test_layout_endian.py +0 -0
  324. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/test_manifest.py +0 -0
  325. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/test_metadata_tags.py +0 -0
  326. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/test_nodata_sentinels.py +0 -0
  327. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/test_oracle.py +0 -0
  328. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/golden_corpus/test_overview_cog.py +0 -0
  329. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/gpu/__init__.py +0 -0
  330. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/integration/__init__.py +0 -0
  331. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/integration/test_sidecar.py +0 -0
  332. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/parity/__init__.py +0 -0
  333. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/read/__init__.py +0 -0
  334. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/read/test_bbox_2555.py +0 -0
  335. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/read/test_bbox_vrt_2668.py +0 -0
  336. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/read/test_compression.py +0 -0
  337. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/read/test_streaming.py +0 -0
  338. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/release_gates/__init__.py +0 -0
  339. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/test_fuzz_hypothesis.py +0 -0
  340. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/test_shutdown_cleanup_2486.py +0 -0
  341. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/unit/__init__.py +0 -0
  342. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/unit/test_compression.py +0 -0
  343. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/unit/test_header.py +0 -0
  344. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/vrt/__init__.py +0 -0
  345. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/geotiff/tests/write/__init__.py +0 -0
  346. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/glcm.py +0 -0
  347. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/gpu_rtx/__init__.py +0 -0
  348. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/gpu_rtx/_memory.py +0 -0
  349. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/gpu_rtx/cuda_utils.py +0 -0
  350. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/gpu_rtx/hillshade.py +0 -0
  351. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/gpu_rtx/mesh_utils.py +0 -0
  352. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/gpu_rtx/viewshed.py +0 -0
  353. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hillshade.py +0 -0
  354. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/__init__.py +0 -0
  355. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/_boundary_store.py +0 -0
  356. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/basin_d8.py +0 -0
  357. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/fill_d8.py +0 -0
  358. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/flow_accumulation_d8.py +0 -0
  359. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/flow_accumulation_dinf.py +0 -0
  360. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/flow_accumulation_mfd.py +0 -0
  361. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/flow_direction_d8.py +0 -0
  362. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/flow_direction_dinf.py +0 -0
  363. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/flow_direction_mfd.py +0 -0
  364. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/flow_length_d8.py +0 -0
  365. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/flow_length_dinf.py +0 -0
  366. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/flow_length_mfd.py +0 -0
  367. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/flow_path_d8.py +0 -0
  368. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/flow_path_dinf.py +0 -0
  369. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/flow_path_mfd.py +0 -0
  370. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/hand_d8.py +0 -0
  371. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/hand_dinf.py +0 -0
  372. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/hand_mfd.py +0 -0
  373. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/sink_d8.py +0 -0
  374. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/snap_pour_point_d8.py +0 -0
  375. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/stream_link_d8.py +0 -0
  376. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/stream_link_dinf.py +0 -0
  377. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/stream_link_mfd.py +0 -0
  378. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/stream_order_d8.py +0 -0
  379. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/stream_order_dinf.py +0 -0
  380. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/stream_order_mfd.py +0 -0
  381. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/__init__.py +0 -0
  382. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/conftest.py +0 -0
  383. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_basin_d8.py +0 -0
  384. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_fill_d8.py +0 -0
  385. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_flow_accumulation_d8.py +0 -0
  386. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_flow_accumulation_dinf.py +0 -0
  387. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_flow_accumulation_mfd.py +0 -0
  388. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_flow_direction_d8.py +0 -0
  389. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_flow_direction_dinf.py +0 -0
  390. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_flow_direction_mfd.py +0 -0
  391. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_flow_length_d8.py +0 -0
  392. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_flow_length_dinf.py +0 -0
  393. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_flow_length_mfd.py +0 -0
  394. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_flow_path_d8.py +0 -0
  395. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_flow_path_dinf.py +0 -0
  396. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_flow_path_mfd.py +0 -0
  397. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_hand_d8.py +0 -0
  398. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_hand_dinf.py +0 -0
  399. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_hand_mfd.py +0 -0
  400. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_sink_d8.py +0 -0
  401. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_snap_pour_point_d8.py +0 -0
  402. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_stream_link_d8.py +0 -0
  403. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_stream_link_dinf.py +0 -0
  404. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_stream_link_mfd.py +0 -0
  405. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_stream_order_d8.py +0 -0
  406. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_stream_order_dinf.py +0 -0
  407. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_stream_order_mfd.py +0 -0
  408. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_twi_d8.py +0 -0
  409. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_validate_cellsize.py +0 -0
  410. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_validate_mfd_companion_shape.py +0 -0
  411. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_validate_mfd_fractions.py +0 -0
  412. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_validate_scalar_params.py +0 -0
  413. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_validate_secondary_args.py +0 -0
  414. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_watershed_d8.py +0 -0
  415. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_watershed_dinf.py +0 -0
  416. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/tests/test_watershed_mfd.py +0 -0
  417. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/twi_d8.py +0 -0
  418. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/watershed_d8.py +0 -0
  419. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/watershed_dinf.py +0 -0
  420. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/hydro/watershed_mfd.py +0 -0
  421. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/interpolate/__init__.py +0 -0
  422. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/interpolate/_idw.py +0 -0
  423. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/interpolate/_validation.py +0 -0
  424. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/kde.py +0 -0
  425. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/mahalanobis.py +0 -0
  426. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/mcda/__init__.py +0 -0
  427. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/mcda/combine.py +0 -0
  428. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/mcda/constrain.py +0 -0
  429. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/mcda/sensitivity.py +0 -0
  430. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/mcda/standardize.py +0 -0
  431. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/mcda/weights.py +0 -0
  432. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/morphology.py +0 -0
  433. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/multispectral.py +0 -0
  434. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/normalize.py +0 -0
  435. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/pathfinding.py +0 -0
  436. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/perlin.py +0 -0
  437. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/polygon_clip.py +0 -0
  438. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/preview.py +0 -0
  439. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/proximity.py +0 -0
  440. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/rasterize.py +0 -0
  441. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/__init__.py +0 -0
  442. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/_crs_utils.py +0 -0
  443. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/_datum_grids.py +0 -0
  444. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/_grid.py +0 -0
  445. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/_interpolate.py +0 -0
  446. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/_itrf.py +0 -0
  447. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/_lite_crs.py +0 -0
  448. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/_merge.py +0 -0
  449. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/_projections.py +0 -0
  450. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/_projections_cuda.py +0 -0
  451. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/_transform.py +0 -0
  452. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/_vertical.py +0 -0
  453. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/grids/at_bev_AT_GIS_GRID.tif +0 -0
  454. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/grids/au_icsm_A66_National_13_09_01.tif +0 -0
  455. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/grids/be_ign_bd72lb72_etrs89lb08.tif +0 -0
  456. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/grids/ch_swisstopo_CHENyx06_ETRS.tif +0 -0
  457. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/grids/de_adv_BETA2007.tif +0 -0
  458. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/grids/es_ign_SPED2ETV2.tif +0 -0
  459. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/grids/nl_nsgi_rdcorr2018.tif +0 -0
  460. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/grids/pt_dgt_D73_ETRS89_geo.tif +0 -0
  461. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/grids/uk_os_OSTN15_NTv2_OSGBtoETRS.tif +0 -0
  462. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/grids/us_nga_egm96_15.tif +0 -0
  463. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/grids/us_noaa_alaska.tif +0 -0
  464. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/grids/us_noaa_conus.tif +0 -0
  465. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/grids/us_noaa_hawaii.tif +0 -0
  466. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/grids/us_noaa_nadcon5_nad27_nad83_1986_conus.tif +0 -0
  467. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/reproject/grids/us_noaa_prvi.tif +0 -0
  468. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/resample.py +0 -0
  469. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/sieve.py +0 -0
  470. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/sky_view_factor.py +0 -0
  471. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/slope.py +0 -0
  472. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/surface_distance.py +0 -0
  473. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/terrain.py +0 -0
  474. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/terrain_metrics.py +0 -0
  475. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/__init__.py +0 -0
  476. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/bench_reproject_vs_rioxarray.py +0 -0
  477. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/conftest.py +0 -0
  478. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/general_checks.py +0 -0
  479. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_analytics.py +0 -0
  480. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_aspect.py +0 -0
  481. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_balanced_allocation.py +0 -0
  482. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_bilateral.py +0 -0
  483. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_bump.py +0 -0
  484. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_classify.py +0 -0
  485. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_convolution.py +0 -0
  486. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_corridor.py +0 -0
  487. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_cost_distance.py +0 -0
  488. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_curvature.py +0 -0
  489. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_dask_cupy_gaps.py +0 -0
  490. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_dask_laziness.py +0 -0
  491. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_dasymetric.py +0 -0
  492. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_dataset_support.py +0 -0
  493. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_datasets.py +0 -0
  494. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_diagnostics.py +0 -0
  495. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_diffusion.py +0 -0
  496. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_edge_detection.py +0 -0
  497. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_emerging_hotspots.py +0 -0
  498. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_erosion.py +0 -0
  499. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_fire.py +0 -0
  500. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_flood.py +0 -0
  501. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_focal.py +0 -0
  502. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_fused_overlap.py +0 -0
  503. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_geodesic_aspect.py +0 -0
  504. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_geodesic_slope.py +0 -0
  505. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_glcm.py +0 -0
  506. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_glcm_metric_order.py +0 -0
  507. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_gpu_rtx_has_rtx.py +0 -0
  508. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_gpu_rtx_memory.py +0 -0
  509. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_gpu_rtx_mesh.py +0 -0
  510. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_hillshade.py +0 -0
  511. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_hypsometric_integral.py +0 -0
  512. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_kde.py +0 -0
  513. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_lite_crs.py +0 -0
  514. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_mahalanobis.py +0 -0
  515. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_mcda.py +0 -0
  516. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_min_observable_height.py +0 -0
  517. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_morphology.py +0 -0
  518. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_morphology_derived.py +0 -0
  519. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_multi_overlap.py +0 -0
  520. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_multispectral.py +0 -0
  521. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_normalize.py +0 -0
  522. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_northness_eastness.py +0 -0
  523. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_optional_shapely.py +0 -0
  524. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_pathfinding.py +0 -0
  525. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_perlin.py +0 -0
  526. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_polygon_clip.py +0 -0
  527. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_polygonize_atol_rtol_backend_coverage_2026_05_27.py +0 -0
  528. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_polygonize_coverage_2026_05_19.py +0 -0
  529. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_polygonize_dask_row_batch_2608.py +0 -0
  530. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_polygonize_issue_2172.py +0 -0
  531. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_polygonize_issue_2583.py +0 -0
  532. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_polygonize_issue_2606.py +0 -0
  533. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_polygonize_issue_2666.py +0 -0
  534. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_preview.py +0 -0
  535. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_proximity.py +0 -0
  536. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_rasterize.py +0 -0
  537. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_rasterize_accuracy.py +0 -0
  538. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_rasterize_all_touched_supercover_2169.py +0 -0
  539. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_rasterize_coverage_2026_05_17.py +0 -0
  540. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_rasterize_coverage_2026_05_21.py +0 -0
  541. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_rasterize_coverage_2026_05_27.py +0 -0
  542. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_rasterize_coverage_2026_05_29.py +0 -0
  543. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_rasterize_descending_x_2568.py +0 -0
  544. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_rasterize_gpu_race_2167.py +0 -0
  545. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_rasterize_nan_int_fill_2504.py +0 -0
  546. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_rasterize_nan_propagation_2255.py +0 -0
  547. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_rasterize_partial_dims_2569.py +0 -0
  548. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_rasterize_props_hoist_2506.py +0 -0
  549. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_rasterize_resolution_exact_2573.py +0 -0
  550. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_rasterize_resolution_validation_2576.py +0 -0
  551. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_rasterize_signature_annot_2250.py +0 -0
  552. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_rasterize_signed_step_2566.py +0 -0
  553. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_rasterize_tile_props_slice_2020.py +0 -0
  554. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_rechunk_no_shuffle.py +0 -0
  555. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_reproject.py +0 -0
  556. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_reproject_coverage_2026_05_27.py +0 -0
  557. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_reproject_cupy_gate_2564.py +0 -0
  558. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_resample.py +0 -0
  559. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_resample_coverage_2026_05_27.py +0 -0
  560. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_resample_cupy_agg_fallback_2615.py +0 -0
  561. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_resample_input_validation_2574.py +0 -0
  562. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_resample_irregular_coords_2663.py +0 -0
  563. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_resample_signature_annot_2544.py +0 -0
  564. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_sieve.py +0 -0
  565. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_sieve_gdal_parity.py +0 -0
  566. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_sky_view_factor.py +0 -0
  567. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_slope.py +0 -0
  568. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_surface_distance.py +0 -0
  569. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_terrain.py +0 -0
  570. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_terrain_metrics.py +0 -0
  571. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_utils.py +0 -0
  572. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_validation.py +0 -0
  573. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_viewshed.py +0 -0
  574. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_visibility.py +0 -0
  575. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_zonal.py +0 -0
  576. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/tests/test_zonal_backend_coverage_2026_05_27.py +0 -0
  577. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/utils.py +0 -0
  578. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/viewshed.py +0 -0
  579. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/visibility.py +0 -0
  580. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/worley.py +0 -0
  581. {xarray_spatial-0.10.3 → xarray_spatial-0.10.5}/xrspatial/zonal.py +0 -0
@@ -17,6 +17,7 @@ geotiff,2026-05-15,1975,HIGH,1;2;5,"Pass 25 (2026-05-15): HIGH fixed -- issue #1
17
17
  glcm,2026-05-01,1408,HIGH,2,"angle=None averaged NaN as 0, masking no-valid-pairs as zero texture; fixed via nanmean-style averaging"
18
18
  hillshade,2026-04-10T12:00:00Z,,,,"Horn's method correct. All backends consistent. NaN propagation correct. float32 adequate for [0,1] output."
19
19
  hydro,2026-04-30,,LOW,1,Only LOW: twi log(0)=-inf if fa=0 (out-of-contract); MFD weighted sum no Kahan (negligible). No CRIT/HIGH issues.
20
+ interpolate-kriging,2026-06-04,2915,MEDIUM,1,"Cat1 nugget-on-diagonal bug (MEDIUM): _build_kriging_matrix set K[:n,:n]=vario_func(D) where D has 0 diagonal, so vario_func(0)=nugget c0 landed on the matrix diagonal; semivariogram gamma(0)=0 by definition (nugget is the h->0+ limit). Forced exact interpolation of noisy data and biased kriging variance downward. Only bites when fitted nugget>0; existing trend-dominated test data fits ~0 nugget so tests passed. Fix #2915/PR #2922: np.fill_diagonal(G,0.0) in shared host code (all 4 backends consume same K_inv). Cats 2-5 clean: validate_points drops NaN/Inf rows; range floor 1e-12 prevents div blowup; dask map_blocks slices grid coords with correct half-open extents and returns matching block shape (kriging is global, no overlap needed); planar Euclidean distance is expected for kriging (Cat4 n/a); numpy/cupy/dask share one algorithm and parity tests pass rtol=1e-10. CUDA available; all 16 kriging tests pass incl cupy + dask+cupy. Singular-matrix path adds 1e-10*eye Tikhonov term (separate from nugget, unaffected, correct)."
20
21
  kde,2026-04-13T12:00:00Z,1198,,,kde/line_density return zeros for descending-y templates. Fix in PR #1199.
21
22
  mahalanobis,2026-05-01,,LOW,1,"LOW: np.linalg.inv (no pinv fallback) returns garbage for near-singular cov without raising. LOW: two-pass mean/cov instead of Welford could lose precision for inputs with very large mean/small variance. No CRIT/HIGH; all four backends use float64 throughout, NaN handled via isfinite, dist_sq clamped non-negative, singular case raises ValueError."
22
23
  morphology,2026-04-30,"1397,1399",HIGH,2;5,HIGH fixed in #1397/PR #1398: morph_erode/dilate seeded centre cell into running min/max even when kernel[centre]==0 (all 4 backends). HIGH fixed in #1399/PR #1400: dask backends raised on 1xN/Nx1 kernels because empty-slice writeback (0:-0).
@@ -22,6 +22,8 @@ geotiff,2026-05-20,SAFE,IO-bound,0,2212,"Pass 13 (2026-05-20): 1 MEDIUM found an
22
22
  glcm,2026-03-31T18:00:00Z,SAFE,compute-bound,0,,"Downgraded to MEDIUM. da.stack without rechunk is scheduling overhead, not OOM risk."
23
23
  hillshade,2026-04-16T12:00:00Z,SAFE,compute-bound,0,,"Re-audit after Horn's method rewrite (PR 1175): clean stencil, map_overlap depth=(1,1), no materialization. Zero findings."
24
24
  hydro,2026-05-01,RISKY,memory-bound,0,1416,"Fixed-in-tree 2026-05-01: hand_mfd._hand_mfd_dask now assembles via da.map_blocks instead of eager da.block of pre-computed tiles (matches hand_dinf pattern). Remaining MEDIUM: sink_d8 CCL fully materializes labels (inherently global), flow_accumulation_mfd frac_bdry held in driver dict instead of memmap-backed BoundaryStore. D8 iterative paths (flow_accum/fill/watershed/basin/stream_*) use serial-tile sweep with memmap-backed boundary store -- per-tile RAM bounded but driver iterates O(diameter) times. flow_direction_*, flow_path/snap_pour_point/twi/hand_d8/hand_dinf are SAFE."
25
+ interpolate_spline,2026-06-04,SAFE,compute-bound,0,,"scope=spline-only. Audited _spline.py + _validation.py only (not _idw/_kriging). 1 MEDIUM (Cat3 GPU transfer): _spline_dask_cupy/_spline_cupy re-uploaded invariant x_pts/y_pts/weights host->device once per chunk. Fixed in PR #2929: added _tps_evaluate_gpu taking on-device point/weight arrays + only per-chunk grid slices; dask+cupy uploads invariants once at graph build (verified 48->3 on 16 chunks, scales with chunk count). numpy/cupy/dask+cupy parity ~1e-14. Added cupy+dask+cupy parity tests and an upload-count regression test (red without fix: 48!=3). _tps_cuda_kernel 30 regs/thread, 6 scalar locals -- no register pressure. CPU/dask+numpy eval @ngjit, row-major, no materialization. Dask graph probe 2560x2560/256 chunks = 200 tasks (2/chunk), no fan-in. Memory guard _check_spline_memory bounds N^2 solve. No issue filed -- gh issue create denied by auto-mode classifier; finding surfaced directly by sweep. GitHub issue field left empty."
26
+ interpolate-kriging,2026-06-04,SAFE,graph-bound,0,2923,"MEDIUM: memory guard used full-grid k0 term on dask templates -> spurious MemoryError (issue #2923, fixed). LOW: _experimental_variogram nlags python loop vectorizable via bincount (~1.2x, pair-array materialization dominates) - doc only. Dask graph clean (2 tasks/chunk); cupy returns device arrays; no .values/.compute/.data.get materialization."
25
27
  kde,2026-04-14T12:00:00Z,SAFE,compute-bound,0,,Graph construction serialized per-tile. _filter_points_to_tile scans all points per tile. No HIGH findings.
26
28
  mahalanobis,2026-03-31T18:00:00Z,SAFE,compute-bound,0,,False positive. Numpy path materializes by design. Dask path uses lazy reductions + map_blocks.
27
29
  morphology,2026-03-31T18:00:00Z,SAFE,compute-bound,0,,
@@ -23,6 +23,7 @@ glcm,2026-04-24,1257,HIGH,1,,"HIGH (fixed #1257): glcm_texture() validated windo
23
23
  gpu_rtx,2026-04-29,1308,HIGH,1,,"HIGH (fixed #1308 / PR #1310): hillshade_rtx (gpu_rtx/hillshade.py:184) and viewshed_gpu (gpu_rtx/viewshed.py:269) allocated cupy device buffers sized by raster shape with no memory check. create_triangulation (mesh_utils.py:23-24) adds verts (12 B/px) + triangles (24 B/px) = 36 B/px; hillshade_rtx adds d_rays(32) + d_hits(16) + d_aux(12) + d_output(4) = 64 B/px (100 B/px total); viewshed_gpu adds d_rays(32) + d_hits(16) + d_visgrid(4) + d_vsrays(32) = 84 B/px (120 B/px total). A 30000x30000 raster asked for 90-108 GB of VRAM before cupy surfaced an opaque allocator error. Fixed by adding gpu_rtx/_memory.py with _available_gpu_memory_bytes() and _check_gpu_memory(func_name, h, w) helpers (cost_distance #1262 / sky_view_factor #1299 pattern, 120 B/px budget covers worst case, raises MemoryError when required > 50% of free VRAM, skips silently when memGetInfo() unavailable). Wired into both entry points after the cupy.ndarray type check and before create_triangulation. 9 new tests in test_gpu_rtx_memory.py (5 helper-unit + 4 end-to-end gated on has_rtx). All 81 existing hillshade/viewshed tests still pass. Cat 4 clean: all CUDA kernels (hillshade.py:25/62/106, viewshed.py:32/74/116, mesh_utils.py:50) have bounds guards; no shared memory, no syncthreads needed. MEDIUM not fixed (Cat 6): hillshade_rtx and viewshed_gpu do not call _validate_raster directly but parent hillshade() (hillshade.py:252) and viewshed() (viewshed.py:1707) already validate, so input validation runs before the gpu_rtx entry point - defense-in-depth, not exploitable. MEDIUM not fixed (Cat 2): mesh_utils.py:64-68 cast mesh_map_index to int32 in the triangle index buffer; overflows at H*W > 2.1B vertices (~46341x46341+) but the new memory guard rejects rasters that large first - documentation/clarity item rather than exploitable. MEDIUM not fixed (Cat 3): mesh_utils.py:19 scale = maxDim / maxH divides by zero on an all-zero raster, propagating inf/NaN into mesh vertex z-coords; separate follow-up. LOW not fixed (Cat 5): mesh_utils.write() opens user-supplied path without canonicalization but its only call site (mesh_utils.py:38-39) sits behind if False: in create_triangulation, not reachable in production."
24
24
  hillshade,2026-04-27,,,,,"Clean. Cat 1: only allocation is the output np.empty(data.shape) at line 32 (cupy at line 165) and a _pad_array with hardcoded depth=1 (line 62) -- bounded by caller, no user-controlled amplifier. Azimuth/altitude are scalars and don't drive size. Cat 2: numba kernel uses range(1, rows-1) with simple (y, x) indexing; numba range loops promote to int64. Cat 3: math.sqrt(1.0 + xx_plus_yy) is always >= 1.0 (no neg sqrt, no div-by-zero); NaN elevation propagates correctly through dz_dx/dz_dy -> shaded -> output (the shaded < 0.0 / shaded > 1.0 clamps don't fire on NaN). Azimuth validated to [0, 360], altitude to [0, 90]. Cat 4: _gpu_calc_numba (line 107) guards both grid bounds and 3x3 stencil reads via i > 0 and i < shape[0]-1 and j > 0 and j < shape[1]-1; no shared memory. Cat 5: no file I/O. Cat 6: hillshade() calls _validate_raster (line 252) and _validate_scalar for both azimuth (253) and angle_altitude (254); all four backend paths cast to float32; tests parametrize int32/int64/float32/float64."
25
25
  hydro,2026-05-03,1423;1425;1427;1429,HIGH,1;3;6,,"Re-audit 2026-05-03. ALL HIGH and MEDIUM findings fixed across 4 PRs. HIGH (Cat 1) fixed in PR #1424: flow_direction_mfd numpy/cupy memory guard ports _check_memory / _check_gpu_memory from flow_accumulation_mfd. MEDIUM Cat 6 fixed in PR #1426: secondary DataArray args validated across watershed_*/snap_pour_point_d8/flow_path_*/stream_link_*/stream_order_*. MEDIUM Cat 3 scalars fixed in PR #1428: flow_direction_mfd p (finite>0), snap_pour_point_d8 search_radius (positive int), hand_*/threshold (finite), fill_d8 z_limit (non-negative finite or None). MEDIUM Cat 3 cellsize fixed in PR #1430: twi_d8/flow_direction_d8/_dinf/_mfd/flow_length_d8/_dinf/_mfd validate cellsize finite-and-non-zero before division. No remaining findings."
26
+ interpolate-kriging,2026-06-04,2917,MEDIUM,3;6,,"Audited _kriging.py (515 LOC) + _validation.py + __init__.py + tests. Cat 1 (alloc): _check_kriging_memory() guards variogram pair arrays, (N+1)x(N+1) matrix, and (grid_pixels,N+1) k0 against 0.8*host RAM; well-tested. LOW gap: cupy path allocates k0 on GPU but guard reads host /proc/meminfo not GPU mem, so large cupy templates can hit cupy OutOfMemoryError (loud, not silent) -- not fixed. Cat 2 (int overflow): memory math uses Python ints (bigint), triu_indices int64; no int32 overflow. Cat 3 (NaN/Inf): singular matrix caught, regularised, then returns None -> all-NaN raster (explicit). variogram divisors bounded a>=1e-12. Cat 4: no CUDA kernels. Cat 5: no file I/O. Cat 6: validate_points coerces float64+drops NaN; _validate_raster on template. FOUND (MEDIUM, fixed): single-point input (n=1 or all-but-one NaN) crashed with opaque numpy 'zero-size array to reduction' ValueError in _experimental_variogram (dists.max() before max_dist guard). Fixed via issue #2917 / PR #2924. CUDA_AVAILABLE=true; cupy/dask+cupy parity tests pass."
26
27
  kde,2026-04-27,1287,HIGH,1,,"HIGH (fixed #1287): kde() and line_density() accepted user-controlled width/height with no upper bound. The eager numpy and cupy backends allocated np.zeros((height, width), dtype=float64) (or cupy.zeros) up front (kde.py: _run_kde_numpy line 308, _run_kde_cupy line 314, line_density inline at line 706). width=1_000_000, height=1_000_000 requested ~8 TB of float64 (or VRAM on the GPU path) before any check ran. Fixed by adding local _available_memory_bytes() helper (mirrors convolution/morphology/bump pattern) and _check_grid_memory(rows, cols) that raises MemoryError when rows*cols*8 exceeds 50% of available RAM. Wired into kde() (skipped for dask paths since _run_kde_dask_numpy/_run_kde_dask_cupy build per-tile via da.from_delayed and are bounded by chunk size) and line_density() (single numpy backend, always guarded). Error message names width/height so the caller knows which knob to turn. No other HIGH findings: Cat 2 (no int32 flat-index math, numba range loops are int64), Cat 3 (bandwidth <= 0 rejected, Silverman fallback returns 1.0 when sigma==0, NaN coords clamp to empty range via min/max), Cat 4 (_kde_cuda has 'if r >= rows or c >= cols: return' bounds guard at line 254, no shared memory, each thread writes own pixel), Cat 5 (no file I/O), Cat 6 (template only used for shape/coords, output dtype forced to float64). MEDIUM (unfixed, Cat 6): _validate_template only checks DataArray + ndim; does not call _validate_raster, but template dtype does not affect compute correctness here."
27
28
  mahalanobis,2026-04-27,1288,HIGH,1,,"HIGH (fixed #1288): mahalanobis() had no memory guard. Both _compute_stats_numpy/_compute_stats_cupy and _mahalanobis_pixel_numpy/_mahalanobis_pixel_cupy materialise float64 buffers of shape (n_bands, H*W) -- the np.stack at line 45/80, the reshape+transpose at line 184 (which forces a contiguous BLAS copy), the centered diff, and the diff @ inv_cov result are all live at peak. A 100kx100k 5-band raster projected to ~400 GB of host memory just for the stack. Fixed by adding _available_memory_bytes()/_available_gpu_memory_bytes() (mirroring cost_distance.py:261-292) plus _check_memory/_check_gpu_memory at 32 bytes/cell/band budget, and wiring them into the public mahalanobis() entry point before any np.stack runs. Eager paths (numpy, cupy) are guarded; dask paths skip the check because chunks are bounded by user-supplied chunksize. MEDIUM (unfixed, Cat 6): mahalanobis() does not call _validate_raster on each band -- validate_arrays only enforces matching shape and array-type, so boolean / non-numeric DataArrays silently coerce. Deferred to a separate PR per the security-sweep one-fix-per-PR policy. No other HIGH findings: Cat 2 (no int32 indexing, numpy default int64), Cat 3 (singular covariance raises a clean ValueError, dist_sq is clamped to 0 before sqrt to absorb numerical noise, NaN mask propagates correctly), Cat 4 (no CUDA kernels), Cat 5 (no file I/O beyond /proc/meminfo)."
28
29
  mcda,2026-04-29,1311,HIGH,3,,Cat 3: NaN/Inf weights silently pass _validate_weights (combine.py:35-39) and owa order_weights check (combine.py:154-158) because abs(NaN-1.0) > 0.01 is False; produces all-NaN raster. Same shape of bug in ahp_weights (weights.py:94) where val<=0 lets NaN slip past. Fixed in #1311 with explicit np.isfinite checks. MEDIUM Cat 1 noted: sensitivity._monte_carlo eagerly computes full dask Dataset; combine.owa stacks all criteria via xr.concat without size guard. MEDIUM Cat 3 noted: sensitivity n_samples=0 divides by zero; wpm permits zero-base/negative-weight without bounds check. No CUDA kernels (Cat 4 N/A); no file I/O (Cat 5 N/A); no int32 index math (Cat 2 N/A).
@@ -1,13 +1,14 @@
1
1
  module,last_inspected,issue,severity_max,categories_found,notes
2
- focal,2026-05-29,2731,HIGH,3;4;5,"F401 not_implemented_func (import line 36, unused, not re-exported). isort: stdlib reorder (import math before from-imports), dropped stray blank lines in import groups, alphabetised+rewrapped convolution/utils from-imports, moved dataset_support import into order. Cat 5: mutable default excludes=[np.nan] in mean() (line 238) -> None sentinel, resolved to [np.nan] in body; never mutated so behaviour preserved; regression test test_mean_default_excludes_does_not_leak added. Cat 1/2 clean. 115 focal tests pass. PR pending."
3
2
  aspect,2026-05-29,2683,MEDIUM,1,E402+E305 line 38: from xrspatial.geodesic import block sat below _geodesic_cuda_dims; moved up with top-of-file imports. E501 lines 219/263: wrapped two _run_gpu_geodesic_aspect kernel-launch calls (101/109 chars). Cat 4 isort reviewed but NOT applied: slope.py/curvature.py use one-import-per-line for xrspatial.utils so raw isort would make aspect inconsistent. Cat 2/3/5 grep clean. PR #2740. 82 aspect+geodesic tests pass.
4
3
  contour,2026-05-29,2698,HIGH,3,"F821 line 557: contours() return annotation ""gpd.GeoDataFrame"" referenced gpd not bound at module scope (only imported inside _to_geopandas). Fixed via TYPE_CHECKING-guarded import geopandas as gpd, matching polygonize.py. No runtime change; geopandas stays optional. isort clean. Cat 1/2/4/5 clean. 24 contour tests pass. PR open."
4
+ focal,2026-05-29,2731,HIGH,3;4;5,"F401 not_implemented_func (import line 36, unused, not re-exported). isort: stdlib reorder (import math before from-imports), dropped stray blank lines in import groups, alphabetised+rewrapped convolution/utils from-imports, moved dataset_support import into order. Cat 5: mutable default excludes=[np.nan] in mean() (line 238) -> None sentinel, resolved to [np.nan] in body; never mutated so behaviour preserved; regression test test_mean_default_excludes_does_not_leak added. Cat 1/2 clean. 115 focal tests pass. PR pending."
5
5
  geotiff,2026-05-27,2481,HIGH,1;3;4,"Bundled 387 flake8 + ~30 isort fixes since #2285/#2430. F401 x9, F811 x6, F841 x3. E501 x250 (mostly wrapped, 3 file-scope imports keep noqa: E402+E501). E252 x62, blank-line cluster, E128/E127 indents. importorskip imports use # noqa: E402. Cat 5 grep clean."
6
6
  hydro-d8,2026-05-29,2705,HIGH,1;3;4,"flake8+isort over the 13 D8 files only (dinf/mfd out of scope). Cat 3 HIGH: F401 x2 (flow_length_d8 function-local _compute_accum_seeds never called; snap_pour_point_d8 module-level cuda_args unused) - both confirmed dead, no re-export. Cat 1: E127/E128 continuation-indent x90 (mostly multi-line def signatures); E302/E303 blank-line cluster in watershed_d8; E501 x4 (flow_path_d8 + snap_pour_point_d8, wrapped ternaries). Cat 4: isort import-block reordering on all 13 files. No Cat 2 (W-codes), no Cat 5 (grep clean: no bare except, mutable defaults, ==None/==True, or shadowed builtins). flake8+isort clean after fix; 385 D8 tests pass. flow_direction_d8 needed manual blank-line placement to satisfy both isort and E302."
7
+ interpolate-kriging,2026-06-04,2916,MEDIUM,1;4,"flake8 E128 x2: continuation-line under-indent at the _chunk_var kriging-predict calls in _kriging_dask_numpy (L234) and _kriging_dask_cupy (L324); re-indented to visual-indent column. Cat 4 isort: 5-line from xrspatial.utils (...) block collapses to one 88-char line under line_length=100. Cat 2/3/5 grep clean (no W-codes, F-codes, bare except, mutable defaults, ==None/True, or shadowed builtins). flake8+isort clean after fix; 14 kriging tests pass. PR open."
7
8
  polygonize,2026-05-27,2534,HIGH,1;3;4,"F401 line 58 (is_cupy_array unused, not re-exported). E127 lines 83/88 (overload continuation indent in generated_jit). isort: 5-line .utils import block collapses to one line at 100-char limit. Cat 2 clean. Cat 5 grep clean."
8
9
  proximity,2026-05-29,2725,HIGH,1;3;4;5,"F841 line 1274 original_chunks dead local in unbounded dask+cupy branch (refactor leftover). Cat 5 mutable default target_values: list = [] in proximity/allocation/direction -> None sentinel, normalized to [] in body (never mutated, behaviour preserved). E128 line 291 np.where continuation under-indent in _vectorized_calc_direction. isort: re-sorted xrspatial import block + blank line after inline import cupy as cp. flake8+isort clean after fix; 69 proximity tests pass + new parametrized regression test. Pre-existing E127 (test_proximity.py 726/752) + test-file isort drift left untouched (out of module scope)."
9
10
  rasterize,2026-05-27,2503,HIGH,1;3,F401 line 15 + F811 line 1193 (paired: local import warnings shadowed unused module-level import); E306 line 1775 (nested @cuda.jit). isort clean. Cat 5 grep clean. Fix in PR #2507.
10
11
  resample,2026-05-27,2543,MEDIUM,4,isort drift only: 4 multi-line parenthesised imports collapsed to single/one-per-line under line_length=100 (top-of-file scipy.ndimage + xrspatial.utils; local cupyx imports in _nan_aware_interp_cupy and _interp_block_cupy); two blank-line nits after import math in _run_dask_numpy/_run_dask_cupy. flake8 clean. Cat 5 grep clean. 169 resample tests pass.
11
- viewshed,2026-05-29,2690,HIGH,1;4;5,"flake8 E127 x2 (L2013-2014 _viewshed_distance_sweep sig); isort .utils import reflow; shadowed builtin id->node_id (L1409,1474). Fixed via /rockout PR. No behavioural change."
12
12
  slope,2026-05-29,2685,HIGH,1;3;4,"F401 line 26 (VALID_BOUNDARY_MODES unused, not re-exported). E402+E305 line 48 (geodesic import block sat after _geodesic_cuda_dims; moved up to top-of-file imports). E501 line 260 (cupy kernel launch, 108 chars) wrapped. isort: consolidated/regrouped xrspatial imports (dataset_support, geodesic, utils). Cat 2 clean. Cat 5 grep clean. 41 slope + 21 geodesic_slope tests pass."
13
+ viewshed,2026-05-29,2690,HIGH,1;4;5,"flake8 E127 x2 (L2013-2014 _viewshed_distance_sweep sig); isort .utils import reflow; shadowed builtin id->node_id (L1409,1474). Fixed via /rockout PR. No behavioural change."
13
14
  zonal,2026-05-27,2522,HIGH,1;3;4,"F401 not_implemented_func (line 42, only present on import line). E501 line 455 (dd.concat one-liner, 117 chars) wrapped across 3 lines. isort: consolidated xrspatial.utils block (merged has_dask_array, dropped not_implemented_func, alphabetised, trimmed extra blank line). Cat 5 grep clean. 125 zonal tests pass."
@@ -0,0 +1,17 @@
1
+ module,last_inspected,issue,severity_max,categories_found,notes
2
+ aspect,2026-06-02,2742;2829,HIGH,3;4,"#2742: degenerate shapes (1x1/Nx1/1xN) + geodesic boundary modes; tests added all 4 backends, GPU-validated. #2829: northness/eastness method='geodesic' branch was untested (planar only); added correctness (diagonal surface where planar!=geodesic) + 4-backend parity, GPU-validated. all-NaN planar/geodesic returns all-NaN (correct). Inf input -> silent -1/flat on spike cell: possible source bug, out of scope for test-only sweep, not filed. Dedup: rectangular-cell oracle #2781 + cell-size #2780 already merged, not duplicated."
3
+ contour,2026-05-29,2704;2710,HIGH,2;5,"Pass 1 (2026-05-29): added TestInfHandling, TestCRSPropagation, TestNonDefaultDims to test_contour.py (5 passed + 2 strict-xfail on a CUDA host; full file 29 passed, 2 xfailed). All four backends (numpy / cupy / dask+numpy / dask+cupy) were already exercised with cross-backend segment-equality assertions (TestBackendEquivalence), and ran green locally on the CUDA host -- Cat 1 well covered, no new backend tests needed. Cat 2 HIGH (Inf): the marching-squares NaN-skip guard at contour.py:67 uses x!=x which does not catch infinity, so a finite level near a +/-inf corner leaks NaN coordinates into the output. Filed source bug #2704 and added two xfail(strict=True) tests pinning it (+inf and -inf) plus test_inf_far_level_no_crossing covering the safe path where the inf quad classifies as all-above (idx 15) and is skipped before any interpolation. Cat 5 MEDIUM: no test asserted gdf.crs propagation from agg.attrs['crs'] (contour.py:660) -- added test_geopandas_crs_from_attrs (to_epsg()==5070) + test_geopandas_no_crs_attr. Cat 5 MEDIUM: the index-to-coordinate transform (contour.py:644-654) reads agg.dims[0]/[1] coords but no test used non-y/x dims -- added test_lat_lon_dims_coordinate_transform + test_lat_lon_matches_yx_equivalent. PR #2710 (test-only, source untouched). LOW (documented, not fixed): non-square cellsize (cellsize_x != cellsize_y) never exercised -- all tests use res (0.5,0.5); levels=None early-return on all-NaN/all-equal works (probed) but only the explicit-levels all-NaN path is asserted. Cat 3 1x1/Nx1/1xN are rejected by the >=2x2 validation guard and that rejection is already tested (test_too_small, test_minimum_raster)."
4
+ focal,2026-05-29,2732,HIGH,1,"Pass (2026-05-29): added test_hotspots_dask_cupy to test_focal.py closing Cat 1 HIGH backend-coverage gap. hotspots() registers dask_cupy_func=_hotspots_dask_cupy (focal.py L1414) but no test invoked it, while mean/apply/focal_stats each have a dedicated dask+cupy test. New test compares dask+cupy vs numpy on chunk interior (matches test_apply_dask_cupy/test_focal_stats_dask_cupy style). RUN on CUDA host: passes; spy confirmed routing through _hotspots_dask_cupy; path matches numpy exactly so no source fix needed. LOW (documented not fixed): Inf/-Inf inputs untested across focal funcs; 1x1 raster not explicitly tested for mean/apply/hotspots (focal_stats 1x1 covered by test_variety_single_cell). Issue #2732."
5
+ geotiff,2026-06-06,2984,MEDIUM,1;3,"Pass 20 (2026-06-06, deep-sweep test-coverage): filed #2984 and added test_writer.py degenerate-shape GPU write coverage (Cat 1 backend + Cat 3 geometric edge). Read side already covers 1x1/1xN/Nx1 on all 4 backends (read/test_degenerate_shapes.py) and the dask streaming writer covers them (integration/test_dask_pipeline.py); the GPU write path was the gap (smallest shape in gpu/test_writer.py was 2x2). Added test_write_geotiff_gpu_degenerate_round_trip (1x1/1xN/Nx1 x none/deflate) + test_to_geotiff_dask_gpu_degenerate_round_trip (dask+cupy via gpu=True). 9 new tests RUN+passing on a CUDA host. Verified paths work first (not a source bug); transform supplied explicitly via attrs. Wider tree audit (~92k test LOC vs ~33k source): rioxarray-compat (#2961), bbox NaN/Inf/rotated, 8-backend parity matrix, codec round-trips already covered -- no other real gaps. | Pass (2026-06-05 test-coverage sweep): mature module (~31k src / ~124k test LOC, 9 test dirs). Exhaustive existing coverage -- parity/test_backend_matrix.py runs all 4 backends + VRT + HTTP + fsspec; golden_corpus full-manifest parity; read_rioxarray_compat_2961 covers masked/mask_and_scale/parse_coordinates/default_name on eager+dask. Cat1+Cat3 gap found (MEDIUM): degenerate-shape READS (1x1/1xN/Nx1) were tested only on the eager numpy reader (test_edge_cases.py) and the dask streaming WRITE path (integration/test_dask_pipeline.py); the windowed dask READ (chunks=) and GPU READ (gpu=True) on a single-pixel dimension were never exercised (smallest dask-read source in read/test_tiling is 8x8/2x32, parity fixtures 32x32/64x64). Probed: paths work today, no source bug -- pure coverage gap. Added read/test_degenerate_shapes.py (18 tests): dask read x{chunks 1,3,4} x{1x1,1xN,Nx1} + coord/transform/crs parity + GPU read + dask+gpu read. GPU cells RAN and PASSED on this CUDA host (grid-size-1 launch validated). Fixture supplies explicit attrs['transform'] (writer cannot infer pixel size from a 1-element coord axis). Branch deep-sweep-test-coverage-geotiff-degenerate-read-01. NOTE: pre-existing union-merge CRLF/duplicate-record corruption in this CSV left untouched -- appended one clean record; DictReader last-write-wins picks this one."
6
+ idw,2026-06-04,2919,HIGH,1;4,"cupy/dask+cupy backends untested (Cat1 HIGH); GPU k-reject error path untested (Cat4 MED). Added 6 GPU tests, validated on CUDA host. Inf-in-points (Cat2) and attrs-preservation (Cat5) are LOW, documented not fixed."
7
+ interpolate-kriging,2026-06-04,2920;2921,HIGH,1;2;3;4;5,"Single public fn kriging(); all 4 backends already had cross-backend parity tests (numpy/cupy/dask+numpy/dask+cupy) incl. cupy & dask+cupy variance -- ran green on CUDA host. Gaps closed (test-only, #2921): Cat1 dask+numpy return_variance branch (_chunk_var) was untested -> added test_dask_return_variance_matches_numpy (atol=1e-12, var ~1e-14). Cat4 nlags only default(15) tested -> added non-default nlags=5 + invalid paths (nlags=0/-1 ValueError, nlags=2.5 TypeError). Cat2/3 two-point <3-lag-bins UserWarning branch -> test_two_point_warns_few_lag_bins. Cat2 all-NaN kriging input -> test_kriging_all_nan_points (only idw covered before). Cat5 output metadata (coords/dims/attrs/name) untested -> added test_output_metadata. Single-point kriging CRASHES (zero-size array reduction in _experimental_variogram, N=1) -- real source bug filed #2920; added xfail(strict, raises=ValueError) test_single_point documenting expected graceful behavior; source fix left to #2920 (test-only PR). LOW/not filed: singular-matrix K_inv-is-None all-NaN branch is defensive and unreachable via public API. GPU-validated."
8
+ interpolate_spline,2026-06-04,,HIGH,1;3;5,scope=spline-only; cupy+dask_cupy spline backends untested (_tps_cuda_kernel) | n==2 affine branch + metadata untested | added 4 tests to TestSpline all pass on CUDA host | issue-create denied by classifier no GH issue
9
+ module,last_inspected,issue,severity_max,categories_found,notes
10
+ polygonize,2026-05-29,2623,MEDIUM,4,"Pass 3 (2026-05-29): added test_polygonize_mask_dtype_coverage_2026_05_29.py (41 passed, 8 xfailed on a CUDA host). Closes Cat 4 MEDIUM parameter-coverage gap: mask= is documented to accept bool/integer/float values but every prior test passed only a bool mask. Integer masks (int32/int64) now pinned against the same-backend bool-mask output on all four backends x both raster dtypes x connectivity 4/8; float-mask-on-integer-raster also pinned. Each backend is compared to its OWN bool reference to isolate mask-dtype from the unrelated numpy-vs-dask hole-vs-single-ring representation difference. Mutation (drop the not-mask[ij] exclusion in _calculate_regions) flips 11 tests red incl. the pixel-exclusion sanity anchor; clean md5 restore. Surfaced source bug #2623: a float-dtype mask on a float-dtype raster raises TypeError at polygonize.py:918 (mask & nan_mask; bitwise_and undefined for float&bool; cupy/dask route floats through _polygonize_numpy so they crash too; int masks coerce fine). 8 float-mask cases marked xfail(strict, raises=TypeError) referencing #2623. Test-only; source untouched. | Pass 2 (2026-05-27): added test_polygonize_atol_rtol_backend_coverage_2026_05_27.py with 15 tests, all passing on a CUDA host. Closes Cat 4 MEDIUM parameter-coverage gap on atol/rtol forwarding through the cupy and dask+cupy backends. atol/rtol were exposed by #2173 / #2194 and thread through _polygonize_cupy (polygonize.py:808) and _polygonize_dask (polygonize.py:1719); the dask path further plumbs them into dask.delayed(_polygonize_chunk)(...) at lines 1748-1754 and into _bucket_key_for_value for cross-chunk merge bucketing at lines 1757-1758. Pre-existing tests covered non-default atol/rtol only on numpy and dask+numpy. The cupy and dask+cupy dispatchers were untested -- a regression dropping the kwargs there would silently change the float polygon count and would not be caught. Same dispatcher-silently-drops-kwarg pattern fixed by #1561 / #1605 / #1685 / #1810 / #1974 on adjacent GeoTIFF surfaces. 15 tests: cupy strict-equality + default-tolerance pin on _REPRO_2173, dask+cupy strict-equality single-chunk + multi-chunk (engages cross-chunk merge bucket) + default-tolerance multi-chunk pin, cupy intermediate-atol small/large pair, dask+cupy intermediate-atol single/multi-chunk small + single-chunk large, cupy integer atol-ignored matrix, dask+cupy integer atol-ignored single-chunk + multi-chunk, cupy rtol-only large/small matrix. Mutation against _polygonize_cupy float branch (drop atol/rtol kwargs in the _polygonize_numpy forward call at polygonize.py:823-825) flips 3 of 5 cupy tests red; mutation against dask.delayed(_polygonize_chunk)(...) at polygonize.py:1748-1754 (drop atol, rtol args) flips 2 of 6 dask+cupy tests red. Confirmed clean restore via md5sum. Source untouched. Filed issue #2537 (test-only). Cat 4 MEDIUM (parameter coverage on cupy + dask+cupy atol/rtol forwarding). Pass 1 (2026-05-19): added test_polygonize_coverage_2026_05_19.py with 58 tests, all passing on a CUDA host. Closes Cat 3 HIGH 1x1 / Nx1 single-column geometric gaps (Nx1 exercises the nx==1 padding path at polygonize.py:565 and the cupy nx==1 numpy-fallback at polygonize.py:671), Cat 3 MEDIUM 1xN single-row and all-equal-value rasters on all four backends. Closes Cat 2 HIGH NaN parity for cupy + dask+cupy (numpy/dask were already covered by test_polygonize_nan_pixels_excluded*), Cat 2 MEDIUM all-NaN raster on all four backends, Cat 2 HIGH +/-Inf pins on all four backends. Filed source-bug issue #2155: numpy/dask/dask+cupy backends silently absorb Inf cells into adjacent finite polygons because _is_close reduces abs(inf-inf) to nan; cupy backend handles Inf correctly. Pins lock the asymmetric behaviour so the fix is visible. Closes Cat 1 MEDIUM simplify_tolerance + mask= parity gaps on dask+cupy backend (numpy/cupy/dask were already covered). Closes Cat 4 MEDIUM column_name non-default value across geopandas/spatialpandas/geojson return types and Cat 4 MEDIUM validation error paths (bad connectivity, bad transform length, mask shape mismatch, mask underlying-type mismatch). Cat 5 N/A: polygonize returns lists/dataframes, not a DataArray with attrs to propagate."
11
+ proximity,2026-06-02,2692,HIGH,1;2;3;4;5,"Pass 2 (2026-06-02): added 18 tests to test_proximity.py closing the two MEDIUM gaps Pass 1 left open, all RUN and passing on a CUDA host across numpy/cupy/dask+numpy/dask+cupy (15 cross-backend + 3 error-path). Source untouched. Cat 4 MEDIUM (error path): _process raises ValueError when raster.dims != (y, x) (proximity.py:1043) but no test exercised the swapped x/y guard; test_wrong_dim_order_raises pins it for proximity/allocation/direction. Cat 2 MEDIUM (all-NaN input): Pass 1 noted all-NaN/all-zero on eager numpy+cupy was unpinned; test_all_nan_raster_all_nan_output pins an all-NaN 6x6 raster -> all-NaN float32 output on all four backends x three functions. Remaining LOW (documented): invalid distance_metric string silently falls back to EUCLIDEAN (proximity.py:1049-1051). || PREVIOUS: Pass 1 (2026-05-29): added 65 tests to test_proximity.py closing three coverage gaps, all RUN and passing on a CUDA host (numpy/cupy/dask+numpy/dask+cupy). Issue #2692, PR opened. Source untouched. Cat 3 HIGH: degenerate raster shapes (1x1 single pixel, Nx1 column strip, 1xN row strip) had zero coverage for proximity/allocation/direction on any backend; they stress the line-sweep kernel boundaries (_process_proximity_line) and the GPU brute-force kernel grid sizing (_proximity_cuda_kernel via cuda_args). Pinned all three shapes x three functions x four backends against hand-checked expected values; mutation of a pinned direction expectation confirms teeth. Cat 1/4 HIGH: allocation and direction only ran EUCLIDEAN across backends; MANHATTAN and GREAT_CIRCLE were cross-backend-tested for proximity only. Pinned both metrics x two functions x four backends against the numpy baseline (all match). Cat 5 MEDIUM: no test set non-empty res/crs attrs so the attrs-preservation assertion in general_output_checks compared two empty dicts. proximity reads attrs['res'] via get_dataarray_resolution for bounded-dask chunk padding, so added attrs round-trip tests on four backends plus a bounded-dask test where a res attr matching the coordinate spacing must equal the numpy baseline. A res attr that lies about the spacing mis-sizes the map_overlap depth; source fragility, not a test gap, left for a separate accuracy issue. Cat 2 (NaN/Inf input) already covered by the shared test_raster fixture (embeds np.inf and np.nan, runs on four backends). Remaining LOW: all-NaN / all-zero input on eager numpy+cupy not directly pinned."
12
+ rasterize,2026-05-29,2614,MEDIUM,4,"Pass 4 (2026-05-29): added test_rasterize_coverage_2026_05_29.py with 11 tests, all passing (pure-Python validation paths, no CUDA needed); filed issue #2614 and opened a test-only PR. Closes Cat 4 MEDIUM error-path gaps that all three prior passes left untouched. (1) Partial width/height: the (width is None) != (height is None) guard in rasterize() raises ValueError naming the given and missing dimension, documented in the docstring, but neither the width-only nor height-only branch had a test; pin both directions plus the width-only+resolution case proving the guard fires before the resolution branch. (2) resolution= input type/shape validation: the type/shape branches (non-number/non-sequence string|dict; wrong-ndim numpy array; wrong-length sequence len 1|3|4; non-numeric elements) had no coverage -- test_rasterize.py's test_invalid_resolution_scalar/tuple only exercise non-finite/non-positive VALUES, not these type/shape guards, so a regression loosening or reordering them would ship silently; pin each branch to its message plus a positive control that a 1-D length-2 numpy array is still accepted. Source untouched."
13
+ reproject,2026-05-29,2618,HIGH,3,"Pass 2026-05-29: reproject already has a deep suite (369 tests in test_reproject.py + coverage/gate files) covering all 4 backends, NaN/Inf/all-NaN/all-Inf, 1x1/2x2, metadata, vertical shift, bounds_policy x backends, integer nodata x backends. Gaps found: Cat 3 HIGH single-row (1xN) and single-col (Nx1) strip rasters never tested (hit size<2 branch of _validate_regular_axis + degenerate resampling axis); Cat 3 MEDIUM constant-value/zero-gradient raster never reprojected. Added TestDegenerateShapeReproject (12 tests): 1xN+Nx1 strips x numpy/dask/cupy/dask+cupy, constant raster numpy value-preservation + cross-backend parity. All 12 executed and passed on a CUDA host. Test-only, no source change (#2618). LOW (documented only): _merge._merge_arrays_cupy imported but never called by merge() (host-bounces via _merge_arrays_numpy) - dead-code source observation not a test gap; non-square cellsize reproject only covered via resolution-tuple validation errors not a successful anisotropic run."
14
+ resample,2026-05-29,2547;2615,HIGH,1;2;3;5,"Pass 2 (2026-05-29): added test_resample_cupy_agg_fallback_2615.py (6 tests, all passing on CUDA host). Closes Cat 1 MEDIUM backend-coverage gap: the cupy eager aggregate CPU fallback for average/min/max at a NON-integer downsample factor (_run_cupy fy==int(fy) branch in resample.py ~L957-973) was never exercised; existing TestCuPyParity used 12x12 scale 0.5 (integer factor 2 -> GPU reshape path) and only median/mode hit the host fallback. New tests use 10x10 scale 0.3 (factor 3.33) for average/min/max parity vs numpy plus a NaN-masked variant. Issue #2615. Module is otherwise very thoroughly covered (test_resample.py + 3 supplementary files); no remaining HIGH gaps found. Pass 1 (2026-05-27): added test_resample_coverage_2026_05_27.py with 70 tests (68 passing, 2 skipped). Closes Cat 3 HIGH Nx1 single-column gap across numpy/cupy/dask+numpy/dask+cupy x 8 methods (nearest/bilinear/cubic/average/min/max/median/mode) plus Nx1 upsample-nearest parity and Nx1 cross-backend aggregate parity. Closes Cat 2 MEDIUM NaN-parity gap on cupy and dask+cupy (existing TestCuPyParity/TestDaskCuPyParity used random data without NaN; the weight-mask gate and spline-prepad had no GPU NaN coverage). Closes Cat 3 MEDIUM all-equal-value raster across 8 methods (downsample) and 3 interp methods (upsample) plus a constant-with-NaN aggregate variant. Closes Cat 5 MEDIUM non-default dim-name propagation: lat/lon, latitude/longitude, and (channel, lat, lon) 3D round-trip without being renamed to y/x; per-dim attrs (units) preserved. Closes Cat 3 MEDIUM empty-raster behaviour pin: 0-row and 0-col rasters raise (currently IndexError) -- contract covered. Filed source-bug issue #2547: cubic on dask backends fails for Nx1 / arrays smaller than depth=16; the 2 skipped tests in this file gate on that fix landing. Source untouched."
15
+ slope,2026-05-29,2697,MEDIUM,3,"PR #2703: added degenerate-shape tests (1x1/1xN/Nx1) for all 4 planar backends + geodesic; no live bug, pins all-NaN+shape contract. CUDA host: cupy/dask+cupy ran. Backend/NaN/param/metadata coverage already complete."
16
+ viewshed,2026-05-29,2693,HIGH,1;2;5,"Pass 1 (2026-05-29): added 4 new test groups to test_viewshed.py (13 new tests + 1 xfail, all passing/xfailing on a CUDA+RTX host). Closes Cat 1 HIGH backend-coverage gap: the dask+cupy dispatch path in _viewshed_dask (Tier B) and _viewshed_windowed (max_distance) was registered but never invoked by any test -- added test_viewshed_dask_cupy_flat (analytical-angle parity, atol 0.03) and test_viewshed_dask_cupy_max_distance (windowed GPU run; observer cell 180, corners INVISIBLE). Both use non-zero flat terrain (1.3) because the RTX mesh builder rejects an all-zero raster (#1378). Closes Cat 5 HIGH metadata-preservation gap: only the numpy test_viewshed called general_output_checks; the cupy/dask/dask+cupy and max_distance paths never asserted attrs/coords/dims/array-type preservation. Added parametrised test_viewshed_metadata_preserved over {numpy,cupy,dask+numpy,dask+cupy} x {full, max_distance=2.0}: asserts attrs==, dims==, shape==, x/y coords allclose; runs general_output_checks (full type parity) for all backends except dask+cupy. Closes Cat 2 HIGH NaN-input gap and surfaced source bug #2693: viewshed on a numpy raster crashes with ValueError 'node not found' from _delete_from_tree when a NaN cell sits at certain positions (e.g. (2,4) in a 5x5 with observer at (2,2)), while NaN at (1,1)/(0,0)/(4,4) runs fine. Added test_viewshed_nan_input_supported_positions (parametrised working positions, asserts observer=180 and NaN cell is INVISIBLE/NaN) plus test_viewshed_nan_input_crashing_position (xfail strict, raises, links #2693). Noted but NOT fixed (source change out of scope for test sweep): the dask+cupy backend does not preserve the cupy backing -- _viewshed_dask computes then rewraps via da.from_array(result_np), so the output computes to numpy not cupy; general_output_checks is skipped for dask+cupy for that reason (candidate for the metadata/backend-parity sweep). LOW (documented only): non-square cell sizes; 1x1 and 1xN geometry covered behaviourally by probing (run without error). Test-only PR; viewshed.py untouched."
17
+ zonal,2026-05-29,2619,MEDIUM,1,"Pass 2 (2026-05-29): one Cat 1 MEDIUM backend-coverage gap remained after pass 1 -- 3D crosstab on cupy / dask+cupy. The 3D GPU paths (_crosstab_cupy / _crosstab_dask_cupy with a 3D categorical values array, layer=, agg='count') were reachable and correct but untested; the existing 3D crosstab tests (test_crosstab_3d_count, test_crosstab_3d_agg_method, test_nodata_values_crosstab_3d) only parametrize numpy / dask+numpy. Added 3 parity tests to test_zonal_backend_coverage_2026_05_27.py (test_crosstab_3d_count_cupy_matches_numpy, test_crosstab_3d_count_dask_cupy_matches_numpy, test_crosstab_3d_nodata_cupy_matches_numpy) asserting cupy and dask+cupy results match numpy for agg='count' including a nodata_values case. All passed live on a CUDA host. Issue #2619, PR #2625. Test-only, no source change. Remaining LOW (documented, not fixed): get_full_extent has no direct unit test (exercised indirectly via suggest_zonal_canvas); non-square cellsize handling not exercised. Pass 1 (2026-05-27): added test_zonal_backend_coverage_2026_05_27.py with 32 tests, all passing on a CUDA host. Closes Cat 1 HIGH backend-coverage gaps: crosstab cupy + dask+cupy (_crosstab_cupy / _crosstab_dask_cupy were dispatched but never invoked by tests), regions cupy + dask+cupy (_regions_cupy via cupyx.scipy.ndimage + _regions_dask_cupy), trim dask+numpy + cupy + dask+cupy (_trim_bounds_dask isnan path and cupy data.get() path), crop dask+numpy + cupy + dask+cupy (_crop_bounds_dask + cupy data.get() path), apply 3D cupy + dask+cupy (per-layer kernel launch over the third axis in _apply_cupy and _apply_dask_cupy). Existing test_zonal.py covered only numpy + dask+numpy for crosstab/regions/trim/crop and 2D-only for cupy apply. Closes Cat 3 MEDIUM 1x1 / 1xN / Nx1 strip edge cases for trim, crop, and regions. Closes Cat 4 LOW pins: regions(neighborhood=6) ValueError, suggest_zonal_canvas(crs='Geographic') aspect-ratio pin and invalid-crs KeyError, crosstab cupy zone_ids/cat_ids filter, crosstab cupy agg='percentage'. Closes Cat 5 MEDIUM: regions coords/attrs propagation across numpy + dask+numpy, trim/crop name='trim'/'crop' default + attrs preservation. Also pins the documented numpy-vs-dask trim asymmetry on NaN sentinel (numpy _trim does equality which never matches NaN; dask _trim_bounds_dask has dedicated isnan branch). Mutation against the cupy.asnumpy() conversion in _crosstab_cupy flipped test_crosstab_cupy_matches_numpy red. Source untouched."
@@ -0,0 +1,159 @@
1
+ # Backend Parity: Cross-Backend Consistency Audit
2
+
3
+ Verify that all implemented backends produce consistent results for a given
4
+ function or set of functions. The prompt is: {{ARGUMENTS}}
5
+
6
+ ---
7
+
8
+ ## Step 1 -- Identify targets
9
+
10
+ 1. If {{ARGUMENTS}} names specific functions (e.g. `slope`, `aspect`), use those.
11
+ 2. If {{ARGUMENTS}} names a category (e.g. `hydrology`, `surface`, `focal`), read
12
+ `README.md` to find all functions in that category.
13
+ 3. If {{ARGUMENTS}} is empty or says "all", scan the full feature matrix in `README.md`
14
+ and test every function that claims support for 2+ backends.
15
+ 4. For each function, read its source file and find the `ArrayTypeFunctionMapping`
16
+ call to determine which backends are actually implemented (not just what the
17
+ README claims).
18
+
19
+ ## Step 2 -- Build test inputs
20
+
21
+ For each target function, create test rasters at three scales:
22
+
23
+ | Name | Size | Purpose |
24
+ |---------|---------|--------------------------------------------------|
25
+ | tiny | 8x6 | Fast, easy to inspect cell-by-cell |
26
+ | medium | 64x64 | Catches chunk-boundary artifacts in dask |
27
+ | large | 256x256 | Stress test, exposes numerical accumulation drift |
28
+
29
+ For each size, generate two variants:
30
+ - **Clean:** no NaN, realistic value range for the function
31
+ (e.g. 0-5000m for elevation, 0-1 for NDVI inputs)
32
+ - **Dirty:** 5-10% random NaN, some extreme values near dtype limits
33
+
34
+ Use `np.random.default_rng(42)` for reproducibility. For functions that require
35
+ specific input structure (e.g. `flow_direction` needs a DEM with drainage, not
36
+ random noise), use the project's `perlin` module or a synthetic cone/valley.
37
+
38
+ Also test with at least two dtypes: `float32` and `float64`.
39
+
40
+ ## Step 3 -- Run every backend
41
+
42
+ For each function, input variant, and dtype:
43
+
44
+ 1. **NumPy:** `create_test_raster(data, backend='numpy')` -- always the baseline.
45
+ 2. **Dask+NumPy:** test with two chunk configurations:
46
+ - `chunks=(size//2, size//2)` -- even split
47
+ - `chunks=(size//3, size//3)` -- ragged remainder
48
+ 3. **CuPy:** `create_test_raster(data, backend='cupy')` -- skip if CUDA unavailable.
49
+ 4. **Dask+CuPy:** `create_test_raster(data, backend='dask+cupy')` -- skip if CUDA
50
+ unavailable.
51
+
52
+ If the function has parameter variants (e.g. `boundary`, `method`), test the
53
+ default parameters first. If {{ARGUMENTS}} includes "thorough", also sweep all
54
+ parameter combinations.
55
+
56
+ ## Step 4 -- Pairwise comparison
57
+
58
+ For every non-NumPy result, compare against the NumPy baseline. Extract data using
59
+ the project conventions:
60
+ - Dask: `.data.compute()`
61
+ - CuPy: `.data.get()`
62
+ - Dask+CuPy: `.data.compute().get()`
63
+
64
+ For each pair, compute and record:
65
+
66
+ ### 4a. Value agreement
67
+ ```python
68
+ abs_diff = np.abs(result - baseline)
69
+ max_abs = np.nanmax(abs_diff)
70
+ rel_diff = abs_diff / (np.abs(baseline) + 1e-30) # avoid div-by-zero
71
+ max_rel = np.nanmax(rel_diff)
72
+ mean_abs = np.nanmean(abs_diff)
73
+ ```
74
+
75
+ ### 4b. NaN mask agreement
76
+ ```python
77
+ nan_match = np.array_equal(np.isnan(result), np.isnan(baseline))
78
+ nan_only_in_result = np.sum(np.isnan(result) & ~np.isnan(baseline))
79
+ nan_only_in_baseline = np.sum(np.isnan(baseline) & ~np.isnan(result))
80
+ ```
81
+
82
+ ### 4c. Metadata preservation
83
+ Using `general_output_checks` from `general_checks.py`:
84
+ - Output type matches input type (DataArray backed by the same array type)
85
+ - Shape, dims, coords, attrs preserved
86
+
87
+ ### 4d. Pass/fail thresholds
88
+
89
+ | Comparison | rtol | atol |
90
+ |-----------------------|----------|----------|
91
+ | NumPy vs Dask+NumPy | 1e-5 | 0 |
92
+ | NumPy vs CuPy | 1e-6 | 1e-6 |
93
+ | NumPy vs Dask+CuPy | 1e-6 | 1e-6 |
94
+
95
+ A comparison **fails** if `max_abs > atol` AND `max_rel > rtol`, or if NaN masks
96
+ disagree.
97
+
98
+ ## Step 5 -- Chunk boundary analysis
99
+
100
+ Dask backends are the most likely source of parity issues due to `map_overlap`
101
+ boundary handling. For any Dask comparison that fails or is borderline:
102
+
103
+ 1. Identify which cells diverge from the NumPy result.
104
+ 2. Map those cells to chunk boundaries (cells within `depth` pixels of a chunk edge).
105
+ 3. Report what percentage of divergent cells are at chunk boundaries vs interior.
106
+ 4. If all divergence is at boundaries, the issue is likely in the `map_overlap`
107
+ `depth` or `boundary` parameter. Say so explicitly.
108
+
109
+ ## Step 6 -- Generate the report
110
+
111
+ ```
112
+ ## Backend Parity Report
113
+
114
+ ### Functions tested
115
+ | Function | Backends implemented | Source file |
116
+ |---------------------|---------------------------|--------------------------|
117
+ | slope | numpy, cupy, dask, dask+cupy | xrspatial/slope.py |
118
+ | ... | ... | ... |
119
+
120
+ ### Parity Matrix
121
+
122
+ #### <function_name>
123
+ | Comparison | Input | Dtype | Max |Δ| | Max |Δ/ref| | NaN match | Metadata | Status |
124
+ |-----------------------|-------------|---------|----------|------------|-----------|----------|--------|
125
+ | NumPy vs Dask+NumPy | tiny clean | float32 | ... | ... | yes | ok | PASS |
126
+ | NumPy vs Dask+NumPy | medium dirty| float64 | ... | ... | yes | ok | PASS |
127
+ | NumPy vs CuPy | tiny clean | float32 | ... | ... | no (3) | ok | FAIL |
128
+ | ... | ... | ... | ... | ... | ... | ... | ... |
129
+
130
+ ### Failures
131
+ For each FAIL row:
132
+ - Which cells diverged
133
+ - Whether divergence correlates with chunk boundaries (Dask) or specific
134
+ input values (CuPy)
135
+ - Likely root cause
136
+ - Suggested fix
137
+
138
+ ### Summary
139
+ - Functions tested: N
140
+ - Total comparisons: N
141
+ - Passed: N
142
+ - Failed: N
143
+ - Skipped (no CUDA): N
144
+ ```
145
+
146
+ ---
147
+
148
+ ## General rules
149
+
150
+ - Do not modify any source or test files. This command is read-only.
151
+ - Use `create_test_raster` from `general_checks.py` for all raster construction.
152
+ - Any temporary files must include the function name for uniqueness.
153
+ - If CUDA is unavailable, skip CuPy and Dask+CuPy gracefully. Report them
154
+ as SKIPPED, not FAIL.
155
+ - If {{ARGUMENTS}} includes "fix", still do not auto-fix. Report the issue and ask.
156
+ - If a function is not in `ArrayTypeFunctionMapping` (e.g. it only has a numpy
157
+ path), note it as "single-backend only" and skip parity checks for it.
158
+ - If {{ARGUMENTS}} includes a specific tolerance (e.g. `rtol=1e-3`), override the
159
+ defaults in the threshold table.
@@ -0,0 +1,127 @@
1
+ # Bench: Local Performance Comparison
2
+
3
+ Run ASV benchmarks for the current branch against main and report regressions
4
+ and improvements. The prompt is: {{ARGUMENTS}}
5
+
6
+ ---
7
+
8
+ ## Step 1 -- Identify what changed
9
+
10
+ 1. If {{ARGUMENTS}} names specific benchmark classes or functions (e.g. `Slope`,
11
+ `flow_accumulation`), use those directly.
12
+ 2. If {{ARGUMENTS}} is empty or says "auto", run `git diff origin/main --name-only`
13
+ to find changed source files under `xrspatial/`. Map each changed file to the
14
+ corresponding benchmark module in `benchmarks/benchmarks/`. Use the filename
15
+ and imports to match (e.g. changes to `slope.py` map to `benchmarks/benchmarks/slope.py`).
16
+ 3. If no benchmark exists for the changed code, note this in the report and
17
+ suggest whether one should be added.
18
+
19
+ ## Step 2 -- Check prerequisites
20
+
21
+ 1. Verify ASV is installed: `python -c "import asv"`. If missing, tell the user
22
+ to install it (`pip install asv`) and stop.
23
+ 2. Verify the benchmarks directory exists at `benchmarks/`.
24
+ 3. Read `benchmarks/asv.conf.json` to confirm the project name and branch settings.
25
+ 4. Check whether the ASV machine file exists (`.asv/machine.json`). If not, run
26
+ `cd benchmarks && asv machine --yes` to initialize it.
27
+
28
+ ## Step 3 -- Run the comparison
29
+
30
+ Run ASV in continuous-comparison mode from the `benchmarks/` directory:
31
+
32
+ ```bash
33
+ cd benchmarks && asv continuous origin/main HEAD -b "<regex>" -e
34
+ ```
35
+
36
+ Where `<regex>` is a pattern matching the benchmark classes identified in Step 1
37
+ (e.g. `Slope|Aspect` or `FlowAccumulation`). The `-e` flag shows stderr on failure.
38
+
39
+ If {{ARGUMENTS}} contains "quick", add `--quick` to run each benchmark only once
40
+ (faster but noisier).
41
+
42
+ If {{ARGUMENTS}} contains "full", omit the `-b` filter to run all benchmarks.
43
+
44
+ ## Step 4 -- Parse and interpret results
45
+
46
+ ASV continuous outputs lines like:
47
+ ```
48
+ BENCHMARKS NOT SIGNIFICANTLY CHANGED.
49
+ ```
50
+ or:
51
+ ```
52
+ REGRESSION: benchmarks.slope.Slope.time_numpy 3.45ms -> 5.67ms (1.64x)
53
+ IMPROVED: benchmarks.slope.Slope.time_dask 8.12ms -> 4.23ms (0.52x)
54
+ ```
55
+
56
+ Parse the output and classify each result:
57
+
58
+ | Category | Criteria |
59
+ |--------------|-----------------------------|
60
+ | REGRESSION | Ratio > 1.2x (matches CI) |
61
+ | IMPROVED | Ratio < 0.8x |
62
+ | UNCHANGED | Between 0.8x and 1.2x |
63
+
64
+ ## Step 5 -- Generate the report
65
+
66
+ ```
67
+ ## Benchmark Report: <branch> vs main
68
+
69
+ ### Changed files
70
+ - <list of changed source files>
71
+
72
+ ### Benchmarks run
73
+ - <list of benchmark classes/functions matched>
74
+
75
+ ### Results
76
+
77
+ | Benchmark | main | HEAD | Ratio | Status |
78
+ |------------------------------------|-----------|-----------|-------|------------|
79
+ | slope.Slope.time_numpy | 3.45 ms | 3.51 ms | 1.02x | UNCHANGED |
80
+ | slope.Slope.time_dask_numpy | 8.12 ms | 4.23 ms | 0.52x | IMPROVED |
81
+ | ... | ... | ... | ... | ... |
82
+
83
+ ### Regressions
84
+ <details for each regression: which benchmark, how much slower, likely cause>
85
+
86
+ ### Improvements
87
+ <details for each improvement>
88
+
89
+ ### Missing benchmarks
90
+ <list any changed functions that have no benchmark coverage>
91
+
92
+ ### Recommendation
93
+ - [ ] Safe to merge (no regressions)
94
+ - [ ] Add "performance" label to PR (regressions found, CI will recheck)
95
+ - [ ] Consider adding benchmarks for: <uncovered functions>
96
+ ```
97
+
98
+ ## Step 6 -- Suggest benchmark additions (if gaps found)
99
+
100
+ If Step 1 found changed functions with no benchmark coverage:
101
+
102
+ 1. Read an existing benchmark file in `benchmarks/benchmarks/` that covers a
103
+ similar function (same category or same backend pattern).
104
+ 2. Describe what a new benchmark should test:
105
+ - Which function and parameter variants
106
+ - Suggested array sizes (match `common.py` conventions)
107
+ - Which backends to benchmark (numpy at minimum, dask if applicable)
108
+ 3. Ask the user whether they want you to write the benchmark file.
109
+
110
+ Do NOT write benchmark files automatically. Report the gap and propose, then wait.
111
+
112
+ ---
113
+
114
+ ## General rules
115
+
116
+ - Always run benchmarks from the `benchmarks/` directory, not the project root.
117
+ - The regression threshold is 1.2x, matching `.github/workflows/benchmarks.yml`.
118
+ Do not change this unless {{ARGUMENTS}} overrides it.
119
+ - If ASV setup or machine detection fails, report the error clearly and suggest
120
+ the fix. Do not retry in a loop.
121
+ - If benchmarks take longer than 5 minutes per class, note the elapsed time so
122
+ the user can plan accordingly.
123
+ - Do not modify any source, test, or benchmark files. This command is read-only
124
+ analysis (unless the user explicitly asks for a benchmark to be written in
125
+ response to Step 6).
126
+ - If {{ARGUMENTS}} says "compare <branch1> <branch2>", run
127
+ `asv continuous <branch1> <branch2>` instead of the default origin/main vs HEAD.
@@ -0,0 +1,148 @@
1
+ # Dask ETL Notebook
2
+
3
+ Create a Jupyter notebook that sets up a Dask distributed LocalCluster and walks
4
+ through an ETL (Extract, Transform, Load) workflow. The prompt is: {{ARGUMENTS}}
5
+
6
+ Use the prompt to determine the data domain, transformations, and output format.
7
+ If no prompt is given, use a geospatial raster ETL as the default domain
8
+ (consistent with the xarray-spatial project).
9
+
10
+ ---
11
+
12
+ ## Notebook structure
13
+
14
+ Every Dask ETL notebook follows this cell sequence:
15
+
16
+ ```
17
+ 0 [markdown] # Title + one-line description of the pipeline
18
+ 1 [markdown] ### Overview (what the pipeline does, what you'll learn)
19
+ 2 [markdown] One-liner about the imports
20
+ 3 [code ] Imports
21
+ 4 [markdown] ## Cluster Setup
22
+ 5 [code ] Create and inspect a dask.distributed LocalCluster + Client
23
+ 6 [markdown] Brief note on the dashboard URL and how to read it
24
+ 7 [markdown] ## Extract
25
+ 8 [code ] Load or generate source data as lazy Dask arrays
26
+ 9 [markdown] Describe the raw data: shape, dtype, chunk layout
27
+ 10 [code ] Inspect / visualize a sample of the raw data
28
+ 11 [markdown] ## Transform
29
+ 12 [code ] Apply transformations (filtering, rechunking, computation)
30
+ 13 [markdown] Explain what the transform does and why it benefits from Dask
31
+ 14 [code ] (Optional) Additional transform step(s)
32
+ 15 [markdown] ## Load
33
+ 16 [code ] Write results to disk (Zarr, Parquet, GeoTIFF, etc.)
34
+ 17 [markdown] Confirm output and show summary statistics
35
+ 18 [code ] Read back and verify the output
36
+ 19 [markdown] ## Cleanup
37
+ 20 [code ] Close the client and cluster
38
+ 21 [markdown] ### Summary + next steps
39
+ ```
40
+
41
+ Sections can be repeated or extended when the prompt calls for more transform
42
+ steps. The core requirement is that every notebook has all five phases: Cluster
43
+ Setup, Extract, Transform, Load, Cleanup.
44
+
45
+ ---
46
+
47
+ ## Cluster Setup cell
48
+
49
+ Always use this pattern for the cluster:
50
+
51
+ ```python
52
+ from dask.distributed import Client, LocalCluster
53
+
54
+ cluster = LocalCluster(
55
+ n_workers=4,
56
+ threads_per_worker=2,
57
+ memory_limit="2GB",
58
+ )
59
+ client = Client(cluster)
60
+ client
61
+ ```
62
+
63
+ Include a markdown cell after the cluster cell noting:
64
+ - The dashboard link (usually `http://localhost:8787/status`)
65
+ - That `n_workers` and `memory_limit` should be tuned for the machine
66
+
67
+ If the prompt asks for a specific cluster configuration (GPU workers, adaptive
68
+ scaling, remote scheduler), adjust accordingly but keep the default simple.
69
+
70
+ ---
71
+
72
+ ## Code conventions
73
+
74
+ ### Imports
75
+
76
+ Standard import block for a Dask ETL notebook:
77
+
78
+ ```python
79
+ import numpy as np
80
+ import xarray as xr
81
+ import dask
82
+ import dask.array as da
83
+ from dask.distributed import Client, LocalCluster
84
+ ```
85
+
86
+ Add extras only when needed (e.g. `import pandas as pd`, `import rioxarray`,
87
+ `from xrspatial import slope`). Keep the import cell minimal.
88
+
89
+ ### Dask best practices to demonstrate
90
+
91
+ - **Lazy by default**: build the computation graph before calling `.compute()`.
92
+ Show the repr of a lazy array at least once so the reader sees the task graph.
93
+ - **Chunking**: explain chunk choices. Use `dask.array.from_array(..., chunks=)`
94
+ or `xr.open_dataset(..., chunks={})` depending on the source.
95
+ - **Avoid full materialization mid-pipeline**: no `.values` or `.compute()` until
96
+ the Load phase unless there is a good reason (and if so, explain why).
97
+ - **Persist when reused**: if an intermediate result is used in multiple
98
+ downstream steps, call `client.persist(result)` and explain why.
99
+ - **Progress feedback**: use `dask.diagnostics.ProgressBar` or point the reader
100
+ to the dashboard.
101
+
102
+ ### Data handling
103
+
104
+ - Generate or load data lazily. For synthetic data, use `dask.array.random` or
105
+ wrap numpy arrays with `da.from_array(..., chunks=...)`.
106
+ - For file-based sources, prefer `xr.open_dataset` / `xr.open_mfdataset` with
107
+ explicit `chunks=` to get lazy Dask-backed arrays.
108
+ - For the Load phase, prefer Zarr (`to_zarr()`) as the default output format
109
+ since it supports parallel writes natively. Mention Parquet or GeoTIFF as
110
+ alternatives when relevant.
111
+
112
+ ### Cleanup
113
+
114
+ Always close the client and cluster at the end:
115
+
116
+ ```python
117
+ client.close()
118
+ cluster.close()
119
+ ```
120
+
121
+ ---
122
+
123
+ ## Writing rules
124
+
125
+ 1. **Run all markdown cells and code comments through [TOOL: humanize].**
126
+ 2. Never use em dashes.
127
+ 3. Short and direct. Technical but not sterile.
128
+ 4. Title cell (h1): describe the pipeline, e.g.
129
+ `Dask ETL: Raster Slope Analysis at Scale` or
130
+ `Dask ETL: Aggregating Sensor Readings to Parquet`.
131
+ 5. Overview cell: 2-3 sentences on what the pipeline does and what Dask concepts
132
+ the reader will pick up. No hype.
133
+ 6. Each phase (Extract, Transform, Load) gets a brief markdown intro (2-4
134
+ sentences) explaining what happens and why.
135
+ 7. Use inline comments in code cells sparingly. Let the markdown cells carry the
136
+ explanation.
137
+
138
+ ---
139
+
140
+ ## Checklist
141
+
142
+ When creating the notebook:
143
+
144
+ 1. Pick a data domain from the prompt (or default to geospatial raster).
145
+ 2. Write the full cell sequence following the structure above.
146
+ 3. Verify all code cells are syntactically correct and self-contained.
147
+ 4. Run all markdown through [TOOL: humanize].
148
+ 5. Ensure the notebook cleans up after itself (cluster closed, temp files noted).