xarray-spatial 0.10.12__tar.gz → 0.10.13__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 (570) hide show
  1. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.claude/sweep-accuracy-state.csv +1 -1
  2. xarray_spatial-0.10.13/.claude/sweep-documentation-state.csv +6 -0
  3. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.claude/sweep-metadata-state.csv +1 -0
  4. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.claude/sweep-performance-state.csv +1 -1
  5. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.claude/sweep-style-state.csv +1 -1
  6. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.claude/sweep-test-coverage-state.csv +3 -2
  7. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/CHANGELOG.md +33 -0
  8. {xarray_spatial-0.10.12/xarray_spatial.egg-info → xarray_spatial-0.10.13}/PKG-INFO +15 -31
  9. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/README.md +14 -30
  10. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13/xarray_spatial.egg-info}/PKG-INFO +15 -31
  11. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xarray_spatial.egg-info/SOURCES.txt +5 -0
  12. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xarray_spatial.egg-info/scm_file_list.json +5 -0
  13. xarray_spatial-0.10.13/xarray_spatial.egg-info/scm_version.json +8 -0
  14. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/__init__.py +1 -14
  15. xarray_spatial-0.10.13/xrspatial/_template_data.py +2006 -0
  16. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/_version.py +3 -3
  17. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/accessor.py +64 -8
  18. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/classify.py +167 -20
  19. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/fire.py +133 -4
  20. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/flood.py +150 -8
  21. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_pam.py +37 -1
  22. xarray_spatial-0.10.13/xrspatial/geotiff/_symbology.py +272 -0
  23. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_writers/eager.py +78 -12
  24. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_writers/gpu.py +13 -2
  25. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/unit/test_exception_exports_3265.py +1 -2
  26. xarray_spatial-0.10.13/xrspatial/geotiff/tests/write/test_category_sidecar_backends_3483.py +97 -0
  27. xarray_spatial-0.10.13/xrspatial/geotiff/tests/write/test_symbology_sidecar_3537.py +286 -0
  28. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/__init__.py +53 -9
  29. xarray_spatial-0.10.13/xrspatial/hydro/tests/test_routing_public_api.py +100 -0
  30. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/reproject/_lite_crs.py +9 -0
  31. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/templates.py +140 -28
  32. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/terrain.py +8 -2
  33. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_accessor.py +2 -1
  34. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_classify.py +124 -4
  35. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_dask_task_names.py +1 -2
  36. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_fire.py +155 -1
  37. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_flood.py +198 -0
  38. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_categorical_3482.py +40 -0
  39. xarray_spatial-0.10.13/xrspatial/tests/test_rasterize_coregister_3492.py +122 -0
  40. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_templates.py +217 -7
  41. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_terrain.py +58 -0
  42. xarray_spatial-0.10.12/.claude/sweep-documentation-state.csv +0 -2
  43. xarray_spatial-0.10.12/xarray_spatial.egg-info/scm_version.json +0 -8
  44. xarray_spatial-0.10.12/xrspatial/_template_data.py +0 -311
  45. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.claude/sweep-api-consistency-state.csv +0 -0
  46. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.claude/sweep-security-state.csv +0 -0
  47. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.codex/sweep-accuracy-state.csv +0 -0
  48. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.codex/sweep-api-consistency-state.csv +0 -0
  49. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.codex/sweep-metadata-state.csv +0 -0
  50. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.codex/sweep-performance-state.csv +0 -0
  51. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.codex/sweep-security-state.csv +0 -0
  52. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.codex/sweep-style-state.csv +0 -0
  53. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.codex/sweep-test-coverage-state.csv +0 -0
  54. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.efficiency-audit-baseline.json +0 -0
  55. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.efficiency-audit-baseline.prev.json +0 -0
  56. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.gitattributes +0 -0
  57. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  58. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.github/ISSUE_TEMPLATE/feature-proposal.md +0 -0
  59. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.github/ISSUE_TEMPLATE/new-contributor.md +0 -0
  60. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.github/labeler.yml +0 -0
  61. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.github/pull_request_template.md +0 -0
  62. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.github/workflows/benchmarks.yml +0 -0
  63. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.github/workflows/copilot-review.yml +0 -0
  64. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.github/workflows/docs.yml +0 -0
  65. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.github/workflows/labeler.yml +0 -0
  66. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.github/workflows/pypi-publish.yml +0 -0
  67. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.github/workflows/test-cog-validator.yml +0 -0
  68. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.github/workflows/test-geotiff-corpus.yml +0 -0
  69. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.github/workflows/test.yml +0 -0
  70. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.github/workflows/welcome-contributor.yml +0 -0
  71. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.gitignore +0 -0
  72. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.kilo/sweep-accuracy-state.csv +0 -0
  73. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.kilo/sweep-api-consistency-state.csv +0 -0
  74. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.kilo/sweep-metadata-state.csv +0 -0
  75. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.kilo/sweep-performance-state.csv +0 -0
  76. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.kilo/sweep-security-state.csv +0 -0
  77. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.kilo/sweep-style-state.csv +0 -0
  78. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.kilo/sweep-test-coverage-state.csv +0 -0
  79. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/.readthedocs.yml +0 -0
  80. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/AI_POLICY.md +0 -0
  81. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/CLAUDE.md +0 -0
  82. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/CODE_OF_CONDUCT.md +0 -0
  83. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/CONTRIBUTING.md +0 -0
  84. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/Citation-styles.md +0 -0
  85. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/LICENSE.txt +0 -0
  86. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/MANIFEST.in +0 -0
  87. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/RELEASE.md +0 -0
  88. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/codecov.yml +0 -0
  89. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/pyproject.toml +0 -0
  90. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/setup.cfg +0 -0
  91. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/setup.py +0 -0
  92. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xarray_spatial.egg-info/dependency_links.txt +0 -0
  93. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xarray_spatial.egg-info/entry_points.txt +0 -0
  94. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xarray_spatial.egg-info/not-zip-safe +0 -0
  95. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xarray_spatial.egg-info/requires.txt +0 -0
  96. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xarray_spatial.egg-info/top_level.txt +0 -0
  97. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/__main__.py +0 -0
  98. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/analytics.py +0 -0
  99. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/aspect.py +0 -0
  100. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/balanced_allocation.py +0 -0
  101. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/bilateral.py +0 -0
  102. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/bump.py +0 -0
  103. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/contour.py +0 -0
  104. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/convolution.py +0 -0
  105. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/corridor.py +0 -0
  106. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/cost_distance.py +0 -0
  107. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/curvature.py +0 -0
  108. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/dasymetric.py +0 -0
  109. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/dataset_support.py +0 -0
  110. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/datasets/__init__.py +0 -0
  111. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/datasets/sentinel-2/blue_band.nc +0 -0
  112. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/datasets/sentinel-2/green_band.nc +0 -0
  113. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/datasets/sentinel-2/nir_band.nc +0 -0
  114. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/datasets/sentinel-2/red_band.nc +0 -0
  115. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/datasets/sentinel-2/swir1_band.nc +0 -0
  116. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/datasets/sentinel-2/swir2_band.nc +0 -0
  117. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/diagnostics.py +0 -0
  118. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/diffusion.py +0 -0
  119. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/edge_detection.py +0 -0
  120. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/emerging_hotspots.py +0 -0
  121. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/erosion.py +0 -0
  122. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/experimental/__init__.py +0 -0
  123. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/experimental/min_observable_height.py +0 -0
  124. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/focal.py +0 -0
  125. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geodesic.py +0 -0
  126. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/__init__.py +0 -0
  127. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_attrs.py +0 -0
  128. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_backends/__init__.py +0 -0
  129. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_backends/_gpu_helpers.py +0 -0
  130. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_backends/dask.py +0 -0
  131. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_backends/gpu.py +0 -0
  132. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_backends/vrt.py +0 -0
  133. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_cog_http.py +0 -0
  134. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_compression.py +0 -0
  135. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_coords.py +0 -0
  136. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_crs.py +0 -0
  137. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_decode.py +0 -0
  138. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_dtypes.py +0 -0
  139. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_encode.py +0 -0
  140. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_errors.py +0 -0
  141. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_geotags.py +0 -0
  142. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_gpu_decode.py +0 -0
  143. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_header.py +0 -0
  144. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_layout.py +0 -0
  145. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_nodata.py +0 -0
  146. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_overview.py +0 -0
  147. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_overview_kernels.py +0 -0
  148. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_reader.py +0 -0
  149. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_runtime.py +0 -0
  150. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_safe_xml.py +0 -0
  151. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_sidecar.py +0 -0
  152. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_sources.py +0 -0
  153. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_validation.py +0 -0
  154. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_vrt.py +0 -0
  155. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_vrt_validation.py +0 -0
  156. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_write_layout.py +0 -0
  157. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_writer.py +0 -0
  158. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_writers/__init__.py +0 -0
  159. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_writers/vrt.py +0 -0
  160. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/_xarray_backend.py +0 -0
  161. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/__init__.py +0 -0
  162. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/_geotiff_fixtures.py +0 -0
  163. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/_helpers/__init__.py +0 -0
  164. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/_helpers/markers.py +0 -0
  165. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/_helpers/tiff_builders.py +0 -0
  166. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/_helpers/tiff_surgery.py +0 -0
  167. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/attrs/__init__.py +0 -0
  168. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/attrs/test_contract.py +0 -0
  169. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/bench_vs_rioxarray.py +0 -0
  170. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/conftest.py +0 -0
  171. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/README.md +0 -0
  172. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/__init__.py +0 -0
  173. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/_marks.py +0 -0
  174. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/_oracle.py +0 -0
  175. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/cog_internal_overview_uint16.tif +0 -0
  176. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/compression_deflate_predictor2_uint16.tif +0 -0
  177. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/compression_deflate_predictor3_float32.tif +0 -0
  178. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/compression_jpeg_uint8_ycbcr.tif +0 -0
  179. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/compression_lerc_float32.tif +0 -0
  180. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/compression_lzw_predictor2_int16.tif +0 -0
  181. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/compression_none_uint8.tif +0 -0
  182. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/crs_citation_only.tif +0 -0
  183. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/crs_epsg_3857.tif +0 -0
  184. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/crs_wkt_utm10n.tif +0 -0
  185. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/dtype_float32.tif +0 -0
  186. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/dtype_float64.tif +0 -0
  187. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/dtype_int16.tif +0 -0
  188. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/dtype_int32.tif +0 -0
  189. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/dtype_int8.tif +0 -0
  190. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/dtype_uint16.tif +0 -0
  191. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/dtype_uint32.tif +0 -0
  192. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/dtype_uint8.tif +0 -0
  193. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/extra_tags_uint16.tif +0 -0
  194. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/gdal_metadata_namespaced_uint16.tif +0 -0
  195. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/nodata_int_sentinel_uint16.tif +0 -0
  196. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/nodata_miniswhite_uint8.tif +0 -0
  197. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/nodata_nan_float32.tif +0 -0
  198. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/overview_external_ovr_uint16.tif +0 -0
  199. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/overview_external_ovr_uint16.tif.ovr +0 -0
  200. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/overview_internal_uint16.tif +0 -0
  201. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/planar_separate_uint8_rgb.tif +0 -0
  202. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/sparse_tiled_uint16.tif +0 -0
  203. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/stripped_be_uint16.tif +0 -0
  204. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/stripped_le_uint16.tif +0 -0
  205. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/tiled_be_uint16.tif +0 -0
  206. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/fixtures/tiled_le_uint16.tif +0 -0
  207. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/generate.py +0 -0
  208. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/manifest.yaml +0 -0
  209. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/test_compression.py +0 -0
  210. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/test_corpus_determinism.py +0 -0
  211. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/test_dask_gpu.py +0 -0
  212. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/test_dask_numpy.py +0 -0
  213. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/test_dtype_variants.py +0 -0
  214. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/test_eager_numpy.py +0 -0
  215. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/test_fsspec.py +0 -0
  216. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/test_gpu.py +0 -0
  217. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/test_http.py +0 -0
  218. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/test_layout_endian.py +0 -0
  219. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/test_manifest.py +0 -0
  220. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/test_metadata_tags.py +0 -0
  221. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/test_nodata_sentinels.py +0 -0
  222. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/test_oracle.py +0 -0
  223. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/test_overview_cog.py +0 -0
  224. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/golden_corpus/test_vrt.py +0 -0
  225. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/gpu/__init__.py +0 -0
  226. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/gpu/test_codec.py +0 -0
  227. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/gpu/test_kernels_and_kwargs.py +0 -0
  228. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/gpu/test_reader.py +0 -0
  229. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/gpu/test_writer.py +0 -0
  230. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/integration/__init__.py +0 -0
  231. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/integration/test_dask_pipeline.py +0 -0
  232. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/integration/test_gpu_pipeline.py +0 -0
  233. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/integration/test_http_sources.py +0 -0
  234. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/integration/test_sidecar.py +0 -0
  235. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/parity/__init__.py +0 -0
  236. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/parity/test_api_consolidation.py +0 -0
  237. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/parity/test_backend_matrix.py +0 -0
  238. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/parity/test_finalization.py +0 -0
  239. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/parity/test_pixel_equality.py +0 -0
  240. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/parity/test_reference.py +0 -0
  241. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/parity/test_signature_contract.py +0 -0
  242. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/__init__.py +0 -0
  243. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/test_basic.py +0 -0
  244. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/test_bbox_2555.py +0 -0
  245. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/test_bbox_vrt_2668.py +0 -0
  246. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/test_cloud_source_concurrency_3361.py +0 -0
  247. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/test_compression.py +0 -0
  248. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/test_coords.py +0 -0
  249. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/test_crs.py +0 -0
  250. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/test_degenerate_shapes.py +0 -0
  251. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/test_dtypes.py +0 -0
  252. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/test_endianness.py +0 -0
  253. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/test_georef.py +0 -0
  254. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/test_mask_and_scale_dtype_parity_3066.py +0 -0
  255. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/test_nodata.py +0 -0
  256. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/test_overview.py +0 -0
  257. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/test_rioxarray_compat_2961.py +0 -0
  258. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/test_scale_zero_3104.py +0 -0
  259. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/test_streaming.py +0 -0
  260. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/test_tiling.py +0 -0
  261. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/read/test_unpack_noop_doc_3263.py +0 -0
  262. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/release_gates/__init__.py +0 -0
  263. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/release_gates/test_features.py +0 -0
  264. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/release_gates/test_stable_features.py +0 -0
  265. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/test_edge_cases.py +0 -0
  266. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/test_fuzz_hypothesis.py +0 -0
  267. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/test_polish.py +0 -0
  268. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/test_round_trip.py +0 -0
  269. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/test_security.py +0 -0
  270. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/test_shutdown_cleanup_2486.py +0 -0
  271. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/test_stable_only_bbox_ordering_2869.py +0 -0
  272. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/test_stable_only_remote_2821.py +0 -0
  273. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/test_xarray_backend_3365.py +0 -0
  274. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/test_xarray_backend_coregister_3376.py +0 -0
  275. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/test_xarray_backend_coregister_target_3379.py +0 -0
  276. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/unit/__init__.py +0 -0
  277. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/unit/test_codec_roundtrip.py +0 -0
  278. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/unit/test_compression.py +0 -0
  279. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/unit/test_degenerate_pixel_size_3331.py +0 -0
  280. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/unit/test_geotags.py +0 -0
  281. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/unit/test_header.py +0 -0
  282. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/unit/test_ifd.py +0 -0
  283. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/unit/test_input_validation.py +0 -0
  284. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/unit/test_metadata.py +0 -0
  285. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/unit/test_photometric.py +0 -0
  286. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/unit/test_predictor.py +0 -0
  287. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/unit/test_safe_xml.py +0 -0
  288. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/unit/test_signatures.py +0 -0
  289. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/vrt/__init__.py +0 -0
  290. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/vrt/test_dtype_conversion.py +0 -0
  291. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/vrt/test_metadata.py +0 -0
  292. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/vrt/test_missing_sources.py +0 -0
  293. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/vrt/test_non_georef_placement_3116.py +0 -0
  294. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/vrt/test_parity.py +0 -0
  295. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/vrt/test_source_opt_ins_2672.py +0 -0
  296. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/vrt/test_validation.py +0 -0
  297. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/vrt/test_window.py +0 -0
  298. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/write/__init__.py +0 -0
  299. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/write/test_basic.py +0 -0
  300. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/write/test_bigtiff.py +0 -0
  301. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/write/test_cog.py +0 -0
  302. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/write/test_crs.py +0 -0
  303. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/write/test_nodata.py +0 -0
  304. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/write/test_overview.py +0 -0
  305. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/write/test_pack_3064.py +0 -0
  306. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/write/test_pack_64bit_sentinel_3264.py +0 -0
  307. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/write/test_pack_band_subset_3161.py +0 -0
  308. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/write/test_pack_float_width_3080.py +0 -0
  309. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/write/test_pack_lazy_nan_guard_3235.py +0 -0
  310. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/write/test_pack_nodata_kwarg_3168.py +0 -0
  311. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/write/test_pack_range_guard_3260.py +0 -0
  312. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/write/test_streaming.py +0 -0
  313. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/geotiff/tests/write/test_vrt_atomic.py +0 -0
  314. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/glcm.py +0 -0
  315. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/gpu_rtx/__init__.py +0 -0
  316. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/gpu_rtx/_memory.py +0 -0
  317. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/gpu_rtx/cuda_utils.py +0 -0
  318. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/gpu_rtx/hillshade.py +0 -0
  319. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/gpu_rtx/mesh_utils.py +0 -0
  320. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/gpu_rtx/viewshed.py +0 -0
  321. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hillshade.py +0 -0
  322. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/_boundary_store.py +0 -0
  323. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/basin_d8.py +0 -0
  324. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/fill_d8.py +0 -0
  325. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/flow_accumulation_d8.py +0 -0
  326. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/flow_accumulation_dinf.py +0 -0
  327. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/flow_accumulation_mfd.py +0 -0
  328. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/flow_direction_d8.py +0 -0
  329. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/flow_direction_dinf.py +0 -0
  330. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/flow_direction_mfd.py +0 -0
  331. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/flow_length_d8.py +0 -0
  332. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/flow_length_dinf.py +0 -0
  333. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/flow_length_mfd.py +0 -0
  334. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/flow_path_d8.py +0 -0
  335. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/flow_path_dinf.py +0 -0
  336. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/flow_path_mfd.py +0 -0
  337. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/hand_d8.py +0 -0
  338. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/hand_dinf.py +0 -0
  339. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/hand_mfd.py +0 -0
  340. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/sink_d8.py +0 -0
  341. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/snap_pour_point_d8.py +0 -0
  342. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/stream_link_d8.py +0 -0
  343. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/stream_link_dinf.py +0 -0
  344. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/stream_link_mfd.py +0 -0
  345. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/stream_order_d8.py +0 -0
  346. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/stream_order_dinf.py +0 -0
  347. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/stream_order_mfd.py +0 -0
  348. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/__init__.py +0 -0
  349. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/conftest.py +0 -0
  350. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_basin_d8.py +0 -0
  351. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_fill_d8.py +0 -0
  352. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_flow_accumulation_d8.py +0 -0
  353. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_flow_accumulation_dinf.py +0 -0
  354. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_flow_accumulation_mfd.py +0 -0
  355. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_flow_direction_d8.py +0 -0
  356. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_flow_direction_dinf.py +0 -0
  357. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_flow_direction_mfd.py +0 -0
  358. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_flow_length_d8.py +0 -0
  359. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_flow_length_dinf.py +0 -0
  360. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_flow_length_mfd.py +0 -0
  361. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_flow_path_d8.py +0 -0
  362. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_flow_path_dinf.py +0 -0
  363. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_flow_path_mfd.py +0 -0
  364. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_hand_d8.py +0 -0
  365. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_hand_dinf.py +0 -0
  366. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_hand_mfd.py +0 -0
  367. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_sink_d8.py +0 -0
  368. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_snap_pour_point_d8.py +0 -0
  369. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_stream_link_d8.py +0 -0
  370. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_stream_link_dinf.py +0 -0
  371. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_stream_link_mfd.py +0 -0
  372. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_stream_order_d8.py +0 -0
  373. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_stream_order_dinf.py +0 -0
  374. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_stream_order_mfd.py +0 -0
  375. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_twi_d8.py +0 -0
  376. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_validate_cellsize.py +0 -0
  377. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_validate_mfd_companion_shape.py +0 -0
  378. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_validate_mfd_fractions.py +0 -0
  379. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_validate_scalar_params.py +0 -0
  380. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_validate_secondary_args.py +0 -0
  381. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_watershed_d8.py +0 -0
  382. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_watershed_dinf.py +0 -0
  383. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/tests/test_watershed_mfd.py +0 -0
  384. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/twi_d8.py +0 -0
  385. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/watershed_d8.py +0 -0
  386. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/watershed_dinf.py +0 -0
  387. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/hydro/watershed_mfd.py +0 -0
  388. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/interpolate/__init__.py +0 -0
  389. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/interpolate/_idw.py +0 -0
  390. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/interpolate/_kriging.py +0 -0
  391. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/interpolate/_spline.py +0 -0
  392. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/interpolate/_validation.py +0 -0
  393. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/interpolate/_vector.py +0 -0
  394. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/kde.py +0 -0
  395. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/mahalanobis.py +0 -0
  396. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/mcda/__init__.py +0 -0
  397. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/mcda/combine.py +0 -0
  398. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/mcda/constrain.py +0 -0
  399. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/mcda/sensitivity.py +0 -0
  400. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/mcda/standardize.py +0 -0
  401. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/mcda/weights.py +0 -0
  402. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/morphology.py +0 -0
  403. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/multispectral.py +0 -0
  404. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/normalize.py +0 -0
  405. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/pathfinding.py +0 -0
  406. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/perlin.py +0 -0
  407. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/polygon_clip.py +0 -0
  408. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/polygonize.py +0 -0
  409. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/preview.py +0 -0
  410. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/proximity.py +0 -0
  411. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/rasterize.py +0 -0
  412. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/reproject/__init__.py +0 -0
  413. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/reproject/_crs_utils.py +0 -0
  414. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/reproject/_grid.py +0 -0
  415. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/reproject/_interpolate.py +0 -0
  416. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/reproject/_itrf.py +0 -0
  417. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/reproject/_merge.py +0 -0
  418. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/reproject/_projections.py +0 -0
  419. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/reproject/_projections_cuda.py +0 -0
  420. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/reproject/_transform.py +0 -0
  421. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/reproject/_vertical.py +0 -0
  422. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/reproject/grids/us_nga_egm96_15.tif +0 -0
  423. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/resample.py +0 -0
  424. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/sieve.py +0 -0
  425. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/sky_view_factor.py +0 -0
  426. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/slope.py +0 -0
  427. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/surface_distance.py +0 -0
  428. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/terrain_metrics.py +0 -0
  429. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/__init__.py +0 -0
  430. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/bench_reproject_vs_rioxarray.py +0 -0
  431. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/conftest.py +0 -0
  432. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/general_checks.py +0 -0
  433. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_analytics.py +0 -0
  434. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_aspect.py +0 -0
  435. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_balanced_allocation.py +0 -0
  436. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_bilateral.py +0 -0
  437. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_bump.py +0 -0
  438. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_contour.py +0 -0
  439. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_contract_validate.py +0 -0
  440. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_convolution.py +0 -0
  441. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_corridor.py +0 -0
  442. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_cost_distance.py +0 -0
  443. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_curvature.py +0 -0
  444. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_dask_cupy_gaps.py +0 -0
  445. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_dask_laziness.py +0 -0
  446. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_dasymetric.py +0 -0
  447. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_dataset_support.py +0 -0
  448. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_datasets.py +0 -0
  449. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_diagnostics.py +0 -0
  450. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_diffusion.py +0 -0
  451. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_edge_detection.py +0 -0
  452. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_emerging_hotspots.py +0 -0
  453. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_erosion.py +0 -0
  454. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_focal.py +0 -0
  455. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_fused_overlap.py +0 -0
  456. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_geodesic_aspect.py +0 -0
  457. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_geodesic_slope.py +0 -0
  458. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_glcm.py +0 -0
  459. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_glcm_metric_order.py +0 -0
  460. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_gpu_rtx_has_rtx.py +0 -0
  461. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_gpu_rtx_memory.py +0 -0
  462. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_gpu_rtx_mesh.py +0 -0
  463. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_hillshade.py +0 -0
  464. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_hypsometric_integral.py +0 -0
  465. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_interpolate_vector.py +0 -0
  466. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_interpolation.py +0 -0
  467. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_kde.py +0 -0
  468. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_lite_crs.py +0 -0
  469. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_mahalanobis.py +0 -0
  470. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_mcda.py +0 -0
  471. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_min_observable_height.py +0 -0
  472. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_morphology.py +0 -0
  473. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_morphology_derived.py +0 -0
  474. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_multi_overlap.py +0 -0
  475. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_multispectral.py +0 -0
  476. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_normalize.py +0 -0
  477. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_northness_eastness.py +0 -0
  478. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_open_geotiff_coregister.py +0 -0
  479. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_open_geotiff_resampling.py +0 -0
  480. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_optional_shapely.py +0 -0
  481. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_pathfinding.py +0 -0
  482. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_perlin.py +0 -0
  483. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_polygon_clip.py +0 -0
  484. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_polygonize.py +0 -0
  485. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_polygonize_atol_rtol_backend_coverage_2026_05_27.py +0 -0
  486. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_polygonize_coverage_2026_05_19.py +0 -0
  487. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_polygonize_dask_row_batch_2608.py +0 -0
  488. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_polygonize_issue_2172.py +0 -0
  489. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_polygonize_issue_2583.py +0 -0
  490. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_polygonize_issue_2606.py +0 -0
  491. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_polygonize_issue_2666.py +0 -0
  492. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_polygonize_issue_2677.py +0 -0
  493. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_polygonize_issue_3292.py +0 -0
  494. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_polygonize_issue_3303.py +0 -0
  495. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_polygonize_mask_chunk_mismatch_3299.py +0 -0
  496. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_polygonize_mask_dtype_coverage_2026_05_29.py +0 -0
  497. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_preview.py +0 -0
  498. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_proximity.py +0 -0
  499. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize.py +0 -0
  500. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_accuracy.py +0 -0
  501. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_all_touched_dask_grid_line_3384.py +0 -0
  502. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_all_touched_supercover_2169.py +0 -0
  503. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_alloc_3107.py +0 -0
  504. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_coverage_2026_05_17.py +0 -0
  505. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_coverage_2026_05_21.py +0 -0
  506. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_coverage_2026_05_27.py +0 -0
  507. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_coverage_2026_05_29.py +0 -0
  508. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_coverage_2026_06_09.py +0 -0
  509. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_crs_mismatch_3058.py +0 -0
  510. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_descending_x_2568.py +0 -0
  511. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_dtype_annot_3291.py +0 -0
  512. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_fill_dtype_3054.py +0 -0
  513. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_geom_crs_3087.py +0 -0
  514. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_gpu_alias_3089.py +0 -0
  515. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_gpu_callable_warn_3057.py +0 -0
  516. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_gpu_race_2167.py +0 -0
  517. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_int_precision_3056.py +0 -0
  518. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_linearring_3055.py +0 -0
  519. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_lines_all_touched_3102.py +0 -0
  520. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_merge_dedup_3304.py +0 -0
  521. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_mixed_type_ordered_merge_3296.py +0 -0
  522. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_nan_int_fill_2504.py +0 -0
  523. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_nan_propagation_2255.py +0 -0
  524. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_nonfinite_burn_3085.py +0 -0
  525. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_nonfinite_burn_3088.py +0 -0
  526. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_nonfinite_coords_3295.py +0 -0
  527. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_partial_dims_2569.py +0 -0
  528. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_props_hoist_2506.py +0 -0
  529. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_resolution_exact_2573.py +0 -0
  530. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_resolution_validation_2576.py +0 -0
  531. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_signature_annot_2250.py +0 -0
  532. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_signed_step_2566.py +0 -0
  533. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rasterize_tile_props_slice_2020.py +0 -0
  534. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_rechunk_no_shuffle.py +0 -0
  535. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_reproject.py +0 -0
  536. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_reproject_coverage_2026_05_27.py +0 -0
  537. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_reproject_cupy_gate_2564.py +0 -0
  538. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_reproject_cupy_promotion_3281.py +0 -0
  539. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_reproject_inverse_kernels_3274.py +0 -0
  540. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_reproject_itrf_scale_3276.py +0 -0
  541. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_reproject_parallel_kernels_3141.py +0 -0
  542. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_reproject_pyproj_warning_3242.py +0 -0
  543. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_reproject_sphere_ellipsoid_guard_3275.py +0 -0
  544. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_reproject_streaming_3101.py +0 -0
  545. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_resample.py +0 -0
  546. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_resample_coverage_2026_05_27.py +0 -0
  547. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_resample_cupy_agg_fallback_2615.py +0 -0
  548. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_resample_input_validation_2574.py +0 -0
  549. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_resample_irregular_coords_2663.py +0 -0
  550. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_resample_nodata_dask_parity_3073.py +0 -0
  551. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_resample_signature_annot_2544.py +0 -0
  552. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_sieve.py +0 -0
  553. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_sieve_gdal_parity.py +0 -0
  554. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_sky_view_factor.py +0 -0
  555. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_slope.py +0 -0
  556. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_surface_distance.py +0 -0
  557. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_sweep_state_csv_merge_2754.py +0 -0
  558. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_terrain_metrics.py +0 -0
  559. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_utils.py +0 -0
  560. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_validation.py +0 -0
  561. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_viewshed.py +0 -0
  562. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_visibility.py +0 -0
  563. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_zonal.py +0 -0
  564. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/tests/test_zonal_backend_coverage_2026_05_27.py +0 -0
  565. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/utils.py +0 -0
  566. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/validate.py +0 -0
  567. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/viewshed.py +0 -0
  568. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/visibility.py +0 -0
  569. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/worley.py +0 -0
  570. {xarray_spatial-0.10.12 → xarray_spatial-0.10.13}/xrspatial/zonal.py +0 -0
@@ -11,7 +11,7 @@ diffusion,2026-05-01,,LOW,1;2;5,"LOW: no Kahan summation across long iterations
11
11
  edge_detection,2026-05-01,,,,Thin wrappers around convolve_2d with fixed Sobel/Prewitt/Laplacian kernels; no issues found
12
12
  emerging_hotspots,2026-04-30,,MEDIUM,2;3,MEDIUM: threshold_90 uses int() (truncation) instead of ceil() so n_times=11 requires only 9/11 (81.8%) instead of 90%. MEDIUM: NaN time steps produce gi_bin=0 which classifier counts as 'non-significant' rather than missing; threshold_90 uses full n_times not valid count. LOW: 'global_std == 0' check does not catch NaN std for fully/mostly NaN inputs.
13
13
  fire,2026-06-19,3394,MEDIUM,5,"Cat5: dask+numpy map_blocks declared float64 (meta default) while ngjit kernels return float32; numpy/cupy/dask+cupy all float32. Fixed 6 wrappers with dtype=np.float32 (PR #3396); bsc already dtype=int8. Cats 1-4 clean: per-pixel ops, no stencil/accumulation/projected-distance; NaN via x!=x; CUDA bounds strict <; rdnbr/ros divisions guarded. cupy+dask+cupy tests run on GPU host."
14
- flood,2026-04-30,,MEDIUM,2;5,"MEDIUM (not fixed): dask backend preserves float32 input dtype while numpy promotes to float64 in flood_depth and curve_number_runoff; DataArray inputs for curve_number, mannings_n bypass scalar > 0 (and CN <= 100) range validation, silently producing NaN/garbage."
14
+ flood,2026-06-25,3499,MEDIUM,5,"Cat5 backend dtype divergence (#3499/this PR): flood_depth and curve_number_runoff document float64 output; numpy/cupy cast to float64 but _flood_depth_dask/_cn_runoff_dask skipped the cast, so float32 input leaked float32 on dask + dask+cupy (numpy/cupy=float64, dask/dask+cupy=float32). Confirms the 2026-04-30 note. Fix: cast hand/p to float64 at the top of both dask helpers; dask+cupy wrappers reuse them so all 4 backends now return float64. Other flood fns unaffected: travel_time/flood_depth_vegetation upcast via float64 _TAN_MIN clamp, inundation via 1.0/0.0 literals, vegetation_roughness via np.interp. 8 new dtype tests across all 4 backends. Cats 1-4 clean; pure vectorized numpy/cupy/dask, no numba/cuda kernels, no neighborhood stencil, no geodesic math. CUDA available; cupy + dask+cupy verified (96 tests pass). LOW (not fixed, documented): curve_number/mannings_n DataArray inputs bypass scalar range validation (CN in (0,100], n>0)."
15
15
  focal,2026-06-10,3214,MEDIUM,1;5,"mean() dtype divergence: numpy/dask+numpy cast to float64 (astype(float)) while cupy/dask+cupy forced float32, so output dtype was backend-dependent and float64 rasters lost precision on GPU (offset 1e7: GPU error 0.58 > true spread 0.42, same class as fixed #2831). mean() was left out of the #2769 _promote_float contract that apply/focal_stats follow. Fix #3214: _promote_float in mean(), drop hardcoded cupy.float32 in _mean_cupy/_mean_dask_cupy, excludes cast to working dtype for cross-backend match parity. CUDA available; all 4 backends executed (245 focal tests pass incl new 3214 dtype tests). Cats 2-4 clean: GPU kernels two-pass std/var (#2831 fix verified), NaN checks via v!=v, map_overlap depths == kernel radius, Gi* validated against reference test. LOW (documented, not fixed): mean() excludes mask only the center pixel; excluded sentinel values (e.g. -9999) still contribute to neighboring cells' means on all backends -- docstring says 'left unchanged rather than averaged', backend-consistent."
16
16
  geotiff,2026-06-14,3331,MEDIUM,2,"Pass 28 (2026-06-14, deep-sweep accuracy): MEDIUM fixed -- issue #3331 / PR #3332. Direct GeoTIFF read accepted a zero or non-finite ModelPixelScale (33550) / ModelTransformation (34264) diagonal: _geotags._extract_transform read sx=scale[0]/sy=scale[1] (and m[0]/m[5]) with no finite-nonzero check, then coords_from_pixel_geometry built arange(N)*pixel_width+origin, so a zero scale collapsed the whole axis onto the origin (constant, non-georeferenced coords) and a NaN/Inf scale produced an all-NaN/all-Inf axis -- a degenerate raster returned with no error on all four backends. Asymmetric: the VRT read path already rejected zero res_x/res_y (VRTUnsupportedError, _vrt_validation.py:202, comment notes the eager read 'would currently surface this as an opaque coord error' -- but it actually surfaced nothing) and the writer rejected zero-step coords (NonUniformCoordsError, _validation.py:1361). Fix adds DegeneratePixelSizeError (GeoTIFFAmbiguousMetadataError subclass, exported + docs table) and a _check_finite_nonzero_pixel_size guard in _extract_transform covering the scale-only, tiepoint+scale, and ModelTransformation paths; the unit-scale tiepoint fallback (literal 1.0) and the allow_rotated no-georef path are untouched. Guard sits in the shared extract path so numpy/dask/gpu/dask+gpu all reject identically -- verified all four locally on a degenerate fixture (CUDA available). NaN ModelTransformation diagonal still rejected: the rotation-tol check short-circuits on NaN (x>NaN is False) and falls through to the new guard. 29 tests in tests/unit/test_degenerate_pixel_size_3331.py (zero/NaN/+-Inf x 3 paths, subclass contract, positive controls, end-to-end open_geotiff). Existing geotiff unit+read suites: 2271 passed, 5 skipped; flake8+isort clean. Scope this pass: numerical core re-audit (overview kernels, coords transform math, decode predictor/nodata, dtypes) via 3 parallel readers -- overview float32-vs-float64 mean accumulator 'divergence' DISMISSED (the ngjit kernel float path and the numpy nanmean float path are mutually exclusive: float mean/min/max/median route to the kernel and return early, _overview.py:340-353); coords single-neighbor pixel-size recovery LOW (standard, negligible, bypassed by transform attr); decode/predictor/nodata clean (#3098 int-sentinel-before-float64-promote pattern not repeated). cuda-available. | Pass 27 (2026-06-12, deep-sweep): HIGH fixed -- issue #3260. to_geotiff(pack=True) cast the packed values to the integer dtype from attrs['mask_and_scale_dtype'] with no range or finiteness check: finite values outside the dtype range wrapped in the astype (4000.0 with SCALE=0.1 on int16 packs to 40000 and landed on disk as -25536, unpacking to -2553.6) and +/-Inf cast to a platform-defined integer (0 on linux/x86), all silently on all four backends; dask deferred the cast into the write's compute so there was no warning at all. Internally inconsistent: the adjacent NaN-no-sentinel guard exists precisely because 'the astype below would silently wrap'. Fix adds _pack_guard_int_range after the round, before the cast (eager raises at call time, dask via map_blocks from the write's single compute, mirroring #3235); exclusive upper bound iinfo.max+1 stays exact in float64 so int64/uint64 reject exactly-2**63 instead of wrapping. Also fixed en route: eager cupy no-sentinel integer pack crashed with TypeError in bool(out.isnull().any()) (implicit cupy->numpy); now routed through the cupy-safe _pack_guard_no_nan. 15 new tests in tests/write/test_pack_range_guard_3260.py covering all four backends (CUDA available, gpu legs executed), boundary iinfo.min/max round trips, round-back-into-range, uint underflow, and the 2**63 float64 bound. Scope this pass: post-2026-06-09 commits only (pack/unpack #3174/#3175/#3239/#3240/#3241, VRT offsets #3135, GPU streaming writer) since Pass 26 covered the rest 3 days earlier; overview kernels, _coords transform math, _decode predictor/orientation/LERC fill, and _nodata lifecycle re-read with no new findings; GPU streaming writer reviewed (per-band NaN rewrite and tile-row alignment mirror the full-array path). cuda-available. | Pass 26 (2026-06-09, deep-sweep): MEDIUM fixed -- issue #3098. _apply_eager_nodata_mask in _attrs.py compared the integer nodata sentinel AFTER promoting the buffer to float64, so int64/uint64 sentinels above 2**53 (INT64_MAX/UINT64_MAX) swallowed up to 512/1024 nearby valid values into NaN on the numpy-eager and cupy-eager backends, while the dask per-chunk mask (_delayed_read_window), the GPU GDS chunk path (_apply_nodata_mask_gpu), and the VRT path all compare at native integer width and masked only exact hits. Reproduced end-to-end: int64 file with nodata=INT64_MAX and values INT64_MAX-1..-513 gave 4 NaN eager vs 1 NaN dask. Same function was internally inconsistent: the mask_nodata=False pixels_present scan already compared at native width. Fix computes the mask at source dtype width before the float64 promotion (promotion itself stays unconditional per #2990); one site fixes both numpy and cupy eager since GPU routes through the helper via duck typing. 5 regression tests in tests/read/test_nodata.py (int64 exact-hit, eager-vs-dask parity, uint64, near-sentinel-no-hit pixels_present, gpu eager); verified on all 4 backends with CUDA. Also audited this pass with no findings: overview reduce kernels CPU vs GPU (empirical parity run incl. float32 median midpoint analysis: RN(a+b)/2 == RN((a+b)/2) so no divergence), unpack/pack scale-offset paths (#3075/#3065, mask-before-scale ordering consistent eager/dask, dask+gpu reuses CPU dask graph), bbox-to-window floor/ceil (GDAL touched semantics), VRT nearest mapping floor((out+0.5)*src/out) and Int64 nodata native-width round-trip, predictor 2/3 GPU kernels (lossless), writer NaN-to-sentinel gates. cuda-available; GPU paths executed, not just reviewed. | Pass 25 (2026-05-15): HIGH fixed -- issue #1975. _block_reduce_2d's cubic branch in xrspatial/geotiff/_writer.py gated the sentinel-to-NaN mask on arr2d.dtype.kind=='f', so to_geotiff(cog=True, overview_resampling='cubic', nodata=<finite>) on an integer raster fell through to an unmasked zoom(arr2d, 0.5, order=3). The bicubic spline blended the sentinel (e.g. -9999) into neighbouring valid cells; cast back to the source integer dtype, the boundary pixels surfaced as silent garbage. Reproduction (1024x1024 int16 + 256x256 nodata corner + nodata=-9999): lvl1 boundary [128, 124:132] showed [1082, 1082, 1085, 1134, 5, 93, 100, 100] instead of [-9999/NaN, ..., 100, 100, 100, 100]; max poisoned value 1134 (11x the actual data value of 100) and min -11104 (below the sentinel -9999). Same root cause as #1623 (float cubic + nodata) but for the integer dtype branch. Both CPU and GPU writers affected because _block_reduce_2d_gpu's cubic path falls back to _block_reduce_2d on CPU. Fix mirrors the float branch: promote the cropped block to float64, mask sentinel to NaN via the integer-range guard (mirrors _int_nodata_in_range), run scipy.ndimage.zoom(prefilter=False), rewrite NaN back to the sentinel, then np.round(...).astype(source_int_dtype) so the integer cast is well-defined. 12 regression tests in test_cog_cubic_int_overview_nodata_1975.py: helper-level cubic per int dtype (int16, uint16, int32), no-nodata regression, out-of-range sentinel no-op, fractional sentinel no-op, all-sentinel block fallback, float cubic regression guard, end-to-end 1024x1024 round-trip, non-constant int regression, cubic-vs-mean sentinel-mask parity, and GPU/CPU byte parity. All 3186 non-stale geotiff tests still pass (2 pre-existing failures unrelated: test_predictor2_big_endian_gpu references the hidden read_to_array symbol, and test_size_param_validation_gpu_vrt_1776 asserts pre-#1767 tile_size=4 behaviour). Categories: Cat 1 (precision loss from cubic spline blending sentinel into valid cells) + Cat 2 (NaN-equivalent corruption: the read-side int-to-NaN mask only catches exact sentinel hits, so the poisoned values survive as legitimate measurements) + Cat 5 (backend parity: CPU and GPU writers shared the same wrong cubic path). | Pass 23 (2026-05-14): HIGH fixed -- issue #1847. extract_geo_info parsed GDAL_NODATA via float() unconditionally, which loses 1 ULP on uint64 max (2**64-1) and int64 max (2**63-1). The downstream integer-mask gate info.min <= int(nodata) <= info.max then rejects the cast because float-rounded sentinel is one above the dtype max; the sentinel pixel survives as a literal valid integer instead of NaN. Same float-only parse in _reader._resolve_masked_fill (LERC fill) and _reader._sparse_fill_value (SPARSE_OK fill). VRT _vrt._parse_band_nodata had already fixed this for the XML parse path (PR #1833) but TIFF source-of-truth was never updated, so write_vrt([uint64.tif]) stringified the float-parsed nodata as '1.8446744073709552e+19' into XML where the VRT reader then rejected it for being out of range. Fix: lift the int-first parse into shared helper _parse_nodata_str in _geotags.py and reuse across the three TIFF-side sites. The helper tries int(text) first to preserve full precision, falls back to float(text) for NaN/Inf/scientific/fractional. Downstream gates already handle int values transparently because np.isfinite(int) works and int(int) is a no-op. 25 regression tests in test_nodata_int64_precision_1847.py: unit-level _parse_nodata_str matrix (int vs float branches, edge cases), eager open_geotiff (uint64 max / int64 max / int64 min / uint16 / int32 / float regression guards), read_geotiff_dask (uint64 max, int64 max), write_vrt + read_vrt round-trip with XML literal assertion, and a GPU parity test. All 2434 non-stale geotiff tests still pass (1 pre-existing test_size_param_validation_gpu_vrt_1776 failure unrelated -- test asserts pre-#1767 tile_size=4 behaviour). Categories: Cat 2 (NaN propagation: sentinel pixel survived as literal valid number on all 4 backends) + Cat 5 (backend inconsistency: VRT XML parse path handled 64-bit sentinels via _parse_band_nodata but TIFF parse path did not, even though write_vrt fed the latter into the former). Audited but did not file: LOW silent kwarg drop -- to_geotiff(da, 'out.vrt', photometric='miniswhite') drops the photometric arg at _write_vrt_tiled call (per-tile files written as MinIsBlack). Data round-trips correctly because no inversion happens on either side; only the tile photometric tag disagrees with the user's request. Niche path + no data corruption + metadata-only drift = LOW, not filed. | Pass 22 (2026-05-13): HIGH fixed -- issue #1809. MinIsWhite (photometric=0) inversion ran before the sentinel-to-NaN nodata mask on all four backends (eager numpy in open_geotiff, dask chunk reader, eager GPU in read_geotiff_gpu, GPU stripped fallback). Because the inversion rewrites the original sentinel value (e.g. uint8 nodata=0 becomes 255, float32 nodata=-9999 becomes 9999), the post-inversion mask matched the wrong pixels: cells whose stored value happened to equal iinfo.max - sentinel were flagged NaN while real sentinel cells survived as inverted values. PR #1804 (a5d78e4) had refactored the helper but kept the original ordering. Fix: introduce _miniswhite_inverted_nodata in _reader.py and stash the inverted sentinel on geo_info._mask_nodata; route every backend mask through that field, keeping geo_info.nodata + attrs[nodata] at the original value for write-side round-trip. Dask path also re-inverts the closure nodata at graph-build time, picking up _ifd_photometric / _ifd_samples_per_pixel stashed in _read_geo_info. 9 regression tests in test_miniswhite_nodata_1809.py cover uint8 nodata=0, uint16 nodata=65535, float32 nodata=-9999 across numpy, dask, and GPU backends plus no-collision and no-nodata controls. All 2424 non-stale geotiff tests pass (4 pre-existing failures unrelated to this fix). Categories: Cat 2 (NaN propagation: real data became NaN while sentinel survived as inverted value) + Cat 5 (backend inconsistency: all four backends share the identical wrong result, so they agreed on the wrong answer rather than diverged). | Pass 21 (2026-05-13): MEDIUM fixed -- issue #1774. open_geotiff / read_geotiff_dask / _apply_nodata_mask_gpu crashed with ValueError: cannot convert float NaN to integer when reading an integer TIFF whose GDAL_NODATA tag was the string ""nan"" / ""inf"" / ""-inf"". Three sites in xrspatial/geotiff/__init__.py called int(nodata) on the integer-dtype branch without first checking np.isfinite. _geotags.py:extract_geo_info parses the GDAL_NODATA tag through float(nodata_str) so a ""nan"" tag surfaces as Python NaN; the integer mask code then explodes. Sibling helpers _resolve_masked_fill and _sparse_fill_value in _reader.py already gate on not math.isnan(v) and not math.isinf(v) (the unfinished pass of #1581). Fix: gate each int(nodata) cast on np.isfinite(nodata). A non-finite sentinel on an integer file cannot match any pixel, so the mask is a no-op and the file dtype is preserved; attrs['nodata'] still carries the raw NaN/Inf sentinel so a write round-trip keeps the original GDAL_NODATA tag. The read_geotiff_dask effective_dtype branch already used try/except and was safe in practice, but tightened with the same isfinite gate for readability. 15 regression tests in test_nodata_nan_int_1774.py covering eager numpy (3 NaN variants + 6 Inf variants), in-range finite still masks regression guard, dask (NaN + Inf), and GPU (NaN + Inf + finite). All pass; 2023 existing geotiff tests still pass (7 pre-existing test_predictor2_big_endian_gpu failures unrelated: they reference xrspatial.geotiff.read_to_array which was hidden from the public namespace in #1708, 3 pre-existing matplotlib palette failures in test_features.py unrelated). Categories: Cat 2 (NaN propagation: NaN nodata produced a crash instead of being treated as missing) + Cat 5 (backend inconsistency: _resolve_masked_fill / _sparse_fill_value already guarded; the three __init__.py sites did not). | Pass 20 (2026-05-12): HIGH fixed -- PR #1691 (no issue created; agent harness blocked gh issue create). Integer COG overview pyramid mixed sentinel into reduced pixels. _block_reduce_2d (_writer.py:258-264) and _block_reduce_2d_gpu (_gpu_decode.py:3027-3028) promoted integer blocks to float64 but never masked the sentinel to NaN before nanmean / nanmin / nanmax / nanmedian. The reduction averaged the sentinel into surrounding valid cells (e.g. (-9999 + 100 + 100 + 100)/4 = -2425 cast back to int16), producing overview pixels that the read-side int-to-NaN mask in open_geotiff couldn't recover because they didn't equal the sentinel. Silent garbage at every zoom above level 0 for to_geotiff(int_data, cog=True, nodata=N). Methods affected: mean, min, max, median; nearest/mode safe (no averaging). Fix: gate the sentinel-to-NaN mask on representability in the source integer dtype (mirrors _int_nodata_in_range in _reader.py) so uint16+GDAL_NODATA=""-9999"" stays a no-op; rewrite all-sentinel-block NaN back to sentinel before the integer dtype cast so the cast is well-defined (the caller's post-overview loop in write() only runs for floats). GPU mirror gets the same path with cupy.where + cupy.isnan for byte parity with CPU. 38 regression tests in test_cog_int_overview_nodata_2026_05_12.py: _block_reduce_2d per-dtype/per-method matrix (uint8/uint16/int16/int32 x mean/min/max/median), all-sentinel-block, no-nodata regression, out-of-range sentinel no-op, end-to-end uint16 + int16 round-trip, 3-band integer COG, GPU per-dtype/per-method matrix, CPU/GPU byte-match parity. All 1606 existing geotiff tests still pass. Categories: Cat 1 (precision/representation loss in nan-aware reduction) + Cat 2 (silent NaN-equivalent corruption from sentinel poisoning) + Cat 5 (backend parity between float and integer code paths within the same writer). Deferred LOW: HTTP COG path (_read_cog_http at _reader.py:1638) skips the band-range validation that local/dask/GPU added in #1673; band=-1 silently selects the last channel on HTTP while local raises IndexError. Cat 5, MEDIUM-leaning but separate concern from the overview fix; one-finding-per-PR per project policy. | Pass 19 (2026-05-12): MEDIUM fixed -- issue #1655. read_vrt silently dropped <NODATA>0</NODATA> on a SimpleSource because of src.nodata or nodata at _vrt.py:370. Python treats 0.0 as falsy, so the per-source sentinel fell through to the band-level <NoDataValue> (or None when missing) and pixels equal to 0.0 in the source file survived as valid data. The in-code comment acknowledged the quirk as backward compat, but the resulting behaviour silently biased every NaN-aware aggregation on VRT mosaics whose sources used 0 as a sentinel (a common convention for unsigned remote-sensing imagery). Fix: src_nodata = src.nodata if src.nodata is not None else nodata. Five regression tests in test_vrt_source_nodata_zero_1655.py covering source NODATA=0, integer XML literal, non-zero unchanged, band-level NoDataValue=0 still honoured, and source-overrides-band precedence. All 100 vrt-related geotiff tests still pass; 3 pre-existing test_features.py matplotlib palette failures unrelated. Categories: Cat 2 (NaN propagation) + Cat 5 (backend inconsistency: read_geotiff masks 0 correctly when GDAL_NODATA tag is set; only VRT path was broken). | Pass 18 (2026-05-11): MEDIUM fixed -- issue #1642. PR #1641 (issue #1640) inherited level-0 georef on overview reads but kept the level-0 origin_x/origin_y unchanged. That is correct for PixelIsArea (origin = upper-left corner of pixel (0,0)) but wrong for PixelIsPoint (origin = center of pixel (0,0), GeoKey 1025 = 2). For a 1024x1024 PixelIsPoint COG with 10 m pixels and origin (0, 0), open_geotiff(overview_level=1) returned x[:3]=[0,20,40] instead of [5,25,45] (level-1 pixel 0 covers level-0 pixels 0-1 whose centers are 0 and 10, centroid 5); same for y. Downstream sel/interp/reproject silently snaps to the wrong pixel for any DEM-style PixelIsPoint COG (USGS, OpenTopography, Copernicus DEM). Categories: Cat 3 (off-by-one / boundary handling) + Cat 5 (raster_type-dependent backend convention). Fix: in extract_geo_info_with_overview_inheritance (_geotags.py), pick the effective raster_type first (overview-declared if non-default, otherwise inherited from parent), then when it is PixelIsPoint apply origin_shift = (scale - 1) * 0.5 * pixel_size_lvl0 along each axis before building the new GeoTransform. PixelIsArea path is byte-equivalent. 13 regression tests in test_overview_pixel_is_point_1642.py: centroid identity across all 4 backends, transform tuple across all 4 backends, uniform grid step, unit-level helper tests for both raster_types via stubbed extract_geo_info, own-geokeys-not-clobbered path on PixelIsPoint, and a PixelIsArea regression check. All 1397 existing non-network geotiff tests still pass (3 pre-existing matplotlib palette failures unrelated). Deferred LOW: non-power-of-two overview dimensions cause scale = base_w/ov_w to diverge from the true 2^level reduction (writer drops the right/bottom strip via h2=(h//2)*2; for h=1023 a level-1 overview has 511 rows so scale=2.0019 not 2.0). Fix would need to either (a) emit explicit geo tags on overview IFDs from the writer or (b) pass the level number into the inheritance helper; neither is a one-line change and the resulting coord error is sub-pixel of level 0. | Pass 17 (2026-05-11): MEDIUM fixed -- issue #1634. open_geotiff eager path windowed read produced confusing CoordinateValidationError when window extended past source extent. read_to_array clamped the window internally and returned a smaller array, but the eager code path used unclamped window indices for y/x coord generation (xrspatial/geotiff/__init__.py lines 562-572), so the coord array length differed from the data and xarray refused to construct the DataArray. Same bug affected the windowed transform shift in _populate_attrs_from_geo_info. The dask path (read_geotiff_dask) already validated up front since #1561, raising a clear ValueError with the format 'window=... is outside the source extent (HxW) or has non-positive size.' so the two backends diverged on the contract. Fix: validate the window up front in open_geotiff's eager branch via _read_geo_info (metadata-only read, no extra pixel cost) using the exact same condition the dask path uses, raising the same ValueError message format. Reproduction: 10x10 raster + window=(5,5,15,15) on eager raised CoordinateValidationError('conflicting sizes ... length 5 ... length 10'); now raises ValueError('window=(5, 5, 15, 15) is outside the source extent (10x10) or has non-positive size.'). Categories: Cat 3 (off-by-one / boundary handling) + Cat 5 (backend inconsistency). 12 regression tests in test_window_out_of_bounds_1634.py: negative start, past-right-edge, past-bottom-edge, past-both-edges, zero-size, inverted window, full-extent ok, interior subset, edge-aligned, eager-vs-dask parity, message-format parity, issue reproducer. All 1286 existing non-network geotiff tests still pass. | Pass 16 (2026-05-11): HIGH fixed -- issue #1623. to_geotiff(cog=True, overview_resampling='cubic', nodata=<finite>) on a float raster with NaN regions produced overview pixels with severe ringing artefacts near nodata borders. Same class of bug as #1613 but for the cubic branch: writer rewrites NaN to the sentinel upstream, then _block_reduce_2d(method=cubic) handed the sentinel-poisoned array straight to scipy.ndimage.zoom(order=3). The cubic spline blended the sentinel (e.g. -9999) into neighbouring cells, producing values like 1133.44, -10290.08 where the data was a constant 100. Repro on 16x16 float32 with a 4x4 NaN corner showed 18 polluted pixels in the 8x8 overview. Fix: when nodata is supplied on a float dtype and the sentinel is found, mask sentinel to NaN, run cubic with prefilter=False so a single NaN cannot poison the entire row/column (default B-spline prefilter is global), then rewrite any NaN in the result back to the sentinel. prefilter=False only fires when a sentinel is present so the non-nodata cubic semantics are unchanged. GPU side: _block_reduce_2d_gpu previously raised on method='cubic'; added a CPU fallback (same pattern as 'mode') so GPU writer produces byte-equivalent overviews. GPU_OVERVIEW_METHODS now includes 'cubic'. 12 regression tests in test_cog_cubic_overview_nodata_1623.py (helper no-ringing, poisoning repro, no-nodata unchanged, end-to-end round-trip, GPU fallback, CPU/GPU byte-match, +/-inf nodata mask, NaN-sentinel no-op, GPU_OVERVIEW_METHODS contract). All 1256 existing geotiff tests still pass (3 pre-existing matplotlib failures unrelated). | Pass 15 (2026-05-11): HIGH fixed -- issue #1613. to_geotiff(cog=True, nodata=<finite>) on a float raster with NaN produced a corrupted overview pyramid. The NaN-to-sentinel rewrite in __init__.py:1202 (CPU) and :2852 (GPU write_geotiff_gpu) ran BEFORE _make_overview / make_overview_gpu, so the nan-aware aggregations (np.nanmean/min/max/median, cupy.nanmean/min/max/median) saw the sentinel as a real number and biased every overview pixel. Reproduction with -9999 sentinel produced [[-4998.75,-4997.75],..] where np.nanmean gives [[1.5,3.5],..]. Both CPU and GPU paths affected; backend results matched each other but were both wrong (CAT 2 NaN propagation + CAT 5 documents the parity). Fix: _block_reduce_2d / _block_reduce_2d_gpu accept a nodata kwarg that masks the sentinel back to NaN for float dtypes before the reduction; the writer's overview loop passes nodata in, then rewrites all-sentinel reductions (which surface as NaN from the reducer) back to the sentinel for the on-disk pyramid. 11 regression tests in test_cog_overview_nodata_1613.py (CPU mean / partial-block / min/max/median / no-nodata passthrough / helper kwarg / all-sentinel block / GPU mean / GPU helper / CPU-GPU agreement). All 235 nodata/overview/cog tests still pass. | Pass 14 (2026-05-11): HIGH fixed -- issue #1611. read_vrt(band=None) on a multi-band integer VRT with per-band <NoDataValue> tags only masks band 0's sentinel. __init__.py lines 2795-2809 in read_vrt apply vrt.bands[0].nodata to the full ndim==3 array; bands 1+ keep their integer sentinels as literal finite values (e.g. 65000 surfaces as 65000.0 after the dtype=float64 cast, not NaN). Float-VRT path masks per-band correctly in _vrt._read_data lines 296-297 + 347-351. PR #1602 fixed the single-band band=N case for issue #1598; the band=None multi-band case is the same class of bug. Repro: 2-band uint16 VRT with NoDataValue 65535 / 65000 returns r.values[1,1,1] == 65000.0 instead of NaN; r.values[1,1,0] is NaN (band 0 sentinel masked). Fix scope: in read_vrt, when band is None, iterate over vrt.bands and mask each arr[..., i] slice against its own <NoDataValue> (gated by the same _int_nodata_in_range guard PR #1583 introduced). Severity HIGH (Cat 2 NaN propagation + Cat 5 backend inconsistency: identical input semantics produce different masking outcomes based on dtype, with finite garbage values where NaN expected). Fix in PR #1612: walks vrt.bands when band is None and ndim==3, masks each arr[..., i] slice against its own <NoDataValue> via the refactored _sentinel_for_dtype helper (reuses PR #1583's range guard so out-of-range/non-finite/fractional sentinels are a no-op). attrs['nodata'] still carries band 0's sentinel for band=None reads (documented contract). 7 regression tests in test_vrt_multiband_int_nodata_1611.py: uint16 per-band, int32 negative, mixed presence, dtype preservation when no sentinel hit, out-of-range gating, band=N non-regression, attrs contract. 135 existing vrt/nodata geotiff tests still pass. | Pass 13 (2026-05-11): HIGH fixed -- issue #1599. write_geotiff_gpu (and to_geotiff gpu=True) emitted raw NaN bytes for missing pixels even when nodata=<finite> was supplied, while the CPU writer substituted NaN with the sentinel before encoding. xrspatial-only round-trips were unaffected (the reader masks both NaN and the sentinel), but external readers (rasterio/GDAL/QGIS) that mask only on the GDAL_NODATA tag saw NaN pixels as valid data -- rasterio reported 100% valid pixels on a 25-NaN file vs CPU's 25-invalid report. Root cause: __init__.py lines 2579-2587 jumped from shape/dtype resolution straight to compression, missing the equivalent of the CPU writer's NaN-to-sentinel rewrite at to_geotiff line ~1156. Fix: cupy.isnan + masked write on a defensive copy of arr, gated on np_dtype.kind=='f' and not np.isnan(float(nodata)). Caller's CuPy buffer preserved (copy before mutate). 7 regression tests in test_gpu_writer_nan_sentinel_1599.py: substitution lands as sentinel, CPU/GPU byte-equivalent, caller buffer not mutated, no-NaN no-op, NaN sentinel skips substitution, rasterio sees identical invalid count on CPU/GPU, multiband 3D path. All other GPU writer tests still pass (50 passed across band-first, attrs, nodata, dask+cupy, writer, nodata aliases). | Pass 12 (2026-05-11): HIGH fixed -- issue #1581. Reading a uint TIFF with a negative GDAL_NODATA sentinel (e.g. uint16 + -9999) raised OverflowError on every backend because the nodata-mask code did arr.dtype.type(int(nodata)) with no range check. Three identical cast sites in __init__.py (numpy eager, _apply_nodata_mask_gpu, _delayed_read_window) plus _resolve_masked_fill and _sparse_fill_value in _reader.py. Fix: _int_nodata_in_range helper gates the cast; out-of-range sentinels are a no-op for value matching (the file can never contain that value), file dtype is preserved, attrs['nodata'] still surfaces the original sentinel so write round-trips keep the GDAL_NODATA tag intact. Matches rasterio behavior. 8 regression tests in test_nodata_out_of_range_1581.py cover the helper, both eager and dask read paths, in-range sentinel non-regression, and GPU helper (cupy-gated). | Pass 11 (2026-05-10): CLEAN. Audited the one additional commit since pass 10 -- #1559 (PR 1548, Centralise GeoTIFF attrs population across all read backends). Refactor extracts _populate_attrs_from_geo_info helper and routes eager numpy, dask, GPU stripped, GPU tiled read paths through it; before the fix dask only emitted crs/transform/raster_type/nodata while numpy emitted the full attrs set including x/y_resolution, resolution_unit, image_description, extra_samples, GDAL metadata, and the CRS-description fields. No data-path arithmetic touched; only attrs dict population. Windowed origin math (origin_x + c0*pixel_width, origin_y + r0*pixel_height) verified to produce -98.0 / 48.75 origin for window=(10,20,50,70) on a (0.1,-0.125) pixel-size raster, with PixelIsArea half-pixel offset preserved on coord lookups (-97.95, 48.6875). Cross-backend attrs parity re-verified: numpy/dask/cupy all emit identical key set on deflate+predictor3+nodata round-trip (crs, crs_wkt, nodata, transform, x_resolution, y_resolution). Data bit-parity re-verified across numpy/dask/cupy on same payload (np.array_equal with equal_nan=True). test_attrs_parity_1548.py (5 tests), test_reader.py/test_writer.py/test_dask_cupy_combined.py (25 tests), GPU orientation/predictor2-BE/LERC-mask/nodata/byteswap suites (65 tests) all green. No accuracy or backend-divergence findings. | Pass 10 (2026-05-10): CLEAN. Audited 5 recent commits: #1558 drop-defensive-copies (frombuffer path still .copy()s before in-place predictor decode at _reader.py:778), #1556 fp-predictor ngjit (writer pre-ravels so 1-D slice arg is correct, float32/64 LE+BE bit-exact), #1552 batched D2H (OOM guard fires before cupy.concatenate, host_buf offsets correct), #1551 parallel-decode gate (>= vs > sends 256x256 default to parallel path, no value diff confirmed via partial-tile parity), #1549 nvjpeg constants (gray + RGB GPU JPEG decode pixel-identical to Pillow CPU, max diff = 0). Cross-backend parity re-verified clean: numpy/dask+numpy/cupy/dask+cupy equal .data/.dtype/.coords/nodata/NaN-mask on deflate+predictor3+nodata; orientations 1-8 numpy==GPU; partial edge tiles 100x150, 257x383, 512x257 numpy==GPU==dask; predictor2 LE/BE round-trip uint8/int16/uint16/int32/uint32 pass; predictor3 LE/BE float32/64 pass. Deferred LOW (pre-existing, not opened): float16 (bps=16, SampleFormat=3) absent from tiff_dtype_to_numpy map - writer never emits, asymmetric but unreachable. | Pass 9 (2026-05-09): TWO HIGH fixed -- (a) PR #1539 closes #1537: TIFF Orientation tag 2/3/4 (mirror flips) on georeferenced files left y/x coords computed from the un-flipped transform, so xarray label lookups returned the wrong pixel even though _apply_orientation flipped the buffer. PR #1521 only updated the transform for the 5-8 axis-swap branch. Fix updates origin and pixel-scale signs along whichever axes were flipped, for both PixelIsArea (origin shifts by N*step) and PixelIsPoint (shifts by (N-1)*step). 10 new tests in test_orientation.py. (b) PR #1546 closes #1540: read_geotiff_gpu ignored Orientation tag completely; CPU correctly applied 2-8 (PR #1521) but GPU returned the raw stored buffer. Cross-backend disagreement on every non-default orientation. Fix adds _apply_orientation_gpu (cupy slicing mirror of the CPU helper) and _apply_orientation_geo_info, threads them into the tiled GPU pipeline, reuses CPU-fallback geo_info for the stripped path to avoid double-applying. 28 new tests in test_orientation_gpu.py (every orientation, single-band tiled, single-band stripped, 3-band tiled, mirror-flip sel-fidelity, default no-tag passthrough). Re-confirmed clean: HTTP coalesce_ranges with overlapping ranges and zero-length ranges, parallel streaming write thread-safety (each tile gets independent buffer via copy or padded zeros), planar=2 + chunky GPU LERC mask propagation matches CPU, IFD chain cap MAX_IFDS=256, max_z_error round-trip on tiled write, _resolve_masked_fill float vs integer dtype semantics. Deferred LOW: per-sample LERC mask (3D mask (h,w,samples)) collapsed to per-pixel ""any sample invalid"" on GPU while CPU honours per-sample; LERC implementations rarely emit 3D masks (verified: lerc.encode with 2D mask on 3-band returns 2D mask). Documented planar=2 + LERC + GPU silently drops mask (rare in practice, source comment acknowledges). | Pass 8 (2026-05-07): HIGH fixed in fix-jpeg-tiff-disable -- to_geotiff(compression='jpeg') wrote files that no external reader can decode. The writer tags compression=7 (new-style JPEG) but emits a self-contained JFIF stream per tile/strip and never writes the JPEGTables tag (347) that the TIFF spec requires for that codec. libtiff/GDAL/rasterio all reject the file with TIFFReadEncodedStrip() failed; our reader round-trips because Pillow decodes the standalone JFIF, hiding the break. Pass-4 notes flagged the read side of the same JPEGTables gap and deferred it; pass-8 covers the write side. Fix: reject compression='jpeg' at the to_geotiff entry with a clear ValueError pointing at deflate/zstd/lzw. The internal _writer.write is untouched so the existing self-decoding tests still cover the codec; re-enabling the public path needs a JPEGTables-aware encoder. PR diffs reviewed but not merged: #1512 (BytesIO source) and #1513 (LERC max_z_error) -- both look correct; #1512 file-like read path goes through read_all() once so the per-call BytesIOSource lock is theoretical, and #1513 forwards max_z_error through every overview/tile/strip/streaming path including _write_vrt_tiled and _compress_block. No regressions found in either open PR. Other surfaces audited clean: predictor=3 with float16 (writer auto-promotes to float32 on both eager and streaming paths, value-exact round-trip); planar=2 multi-tile read uses band_idx*tiles_per_band offset so no cross-contamination between planes; _header.py multi-byte tag parsing uses bo (byte_order) consistently; Pillow YCbCr-vs-tagged-RGB photometric mismatch becomes moot once JPEG is disabled. Deferred (LOW/MEDIUM, not filed): JPEG2000 writer accepts arbitrary dtype with no validation (rare codec, narrow risk); float16 dtype not in tiff_dtype_to_numpy decode map (writer never emits it - asymmetric but unreachable); Orientation tag (274) still ignored on read (pass-4 deferral). | Pass 7 (2026-05-07): HIGH fixed in fix-mmap-cache-refcount-after-replace -- _MmapCache.release() looked up the cache entry by realpath, so a holder that acquired the OLD mmap before an os.replace and released it AFTER another caller had acquired the post-replace entry would decrement the new holder's refcount. Subsequent eviction (cache full, or another acquire) closed the still-in-use mmap, breaking reads with 'mmap closed or invalid'. Real exposure: any concurrent reader/writer pattern where to_geotiff replaces a file that another reader had just opened via open_geotiff with chunks= or via _FileSource. PR #1506 added stale-replacement detection but did not fix the refcount confusion across the pop. Fix: acquire returns an opaque entry token; release takes the token and decrements that exact entry, regardless of cache state. Orphaned (popped) entries close their fh+mmap when their own refcount hits zero. _FileSource updated to pass the token. Regression test test_release_after_path_replacement_does_not_clobber_new_holder added. All 665 geotiff tests pass; GPU path verified. | Pass 6 (2026-05-07) PR #1507: BE pred2 numba TypingError. | Pass 5 (2026-05-06) PR #1506: mmap cache stale after file replace. | Pass 4 (2026-05-06) PR #1501: sparse COG tiles. | Pass 3 (2026-05-06) PR #1500: predictor=3 byte order. | Pass 2 (2026-05-05) PR #1498: predictor=2 sample-wise. | Pass 1 (2026-04-23) PR #1247. Re-confirmed clean over passes 2-7: items 2 (writer always emits LE TIFFs - hardcoded b'II'), 3 (RowsPerStrip default = height when missing), 4 (StripByteCounts missing raises clear ValueError), 5 (TileWidth without TileLength caught by 'tw <= 0 or th <= 0' check at _reader.py:688), 9 (read determinism on compressed+tiled+multiband), 11 (predictor=2 with awkward sample stride round-trips), 18 (compression_level=99 raises ValueError 'out of range for deflate (valid: 1-9)'), 21 (concurrent writes serialize correctly via mkstemp+os.replace), 24 (uint16 dtype preserved on numpy backend, dask honors chunks param), 26 (chunks rounds correctly with remainder chunk for non-tile-aligned). Deferred: item 8 (BytesIO/file-like sources are not supported, source.lower() error) - documented as 'str' parameter, not a bug; item 19 (LERC max_z_error not user-exposed by to_geotiff) - missing feature, not a bug."
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"
@@ -0,0 +1,6 @@
1
+ module,last_inspected,issue,severity_max,categories_found,notes,doc_coverage
2
+ flood,2026-06-25,,HIGH,1;4;5,"Cat4 HIGH: vegetation_roughness, vegetation_curve_number, flood_depth_vegetation public but absent from reference/flood.rst; Cat1 MEDIUM: no Examples on any of 7 public funcs; Cat5 MEDIUM: backend support undocumented (all 4 backends) + NaN propagation undocumented for curve_number_runoff/travel_time. Fixed in deep-sweep-documentation-flood-2026-06-25: added 3 rst entries, Examples+backend Notes to all 7 funcs (examples executed OK on CUDA host), NaN notes. PR #3502 opened with the fix; gh issue create blocked by auto-mode classifier so no issue number.",7/7
3
+ classify,2026-06-25,3506,MEDIUM,1;3,"Cat3: reclassify (numpy/dask/cupy blocks) + equal_interval example outputs were stale/wrong, binary used np.nan in array repr; corrected to actual output (tests confirm code is correct). Cat1: added missing Examples to std_mean, head_tail_breaks, percentiles, maximum_breaks, box_plot. Fixed in deep-sweep-documentation-classify-2026-06-25 (PR for #3506). Cat2 natural_breaks num_sample-None omission already tracked in #3501 (left alone). All 10 public funcs listed in reference/classification.rst (no Cat4 gap). CUDA available: ran numpy examples; cupy/dask reprs reviewed statically.",10/10
4
+ geotiff,2026-06-25,,MEDIUM,1,"to_geotiff (public write entry point) had Parameters/Returns/Raises but no Examples section while open_geotiff does (Cat1 MEDIUM); added Examples block (plain GeoTIFF, cog=True, .vrt mosaic) modeled on open_geotiff; fixed on deep-sweep-documentation-geotiff-2026-06-25; repo issues disabled so no issue number. Cat2/3/4/5 clean: open_geotiff/to_geotiff signature-docstring parity locked by parity/test_signature_contract.py + write/test_bigtiff.py + test_polish.py; both funcs in autosummary in reference/geotiff.rst; reference page mirrors SUPPORTED_FEATURES tiers (tier-parity gate); CUDA available, all docstring examples are +SKIP illustrative only",2/2
5
+ fire,2026-06-25,,MEDIUM,1;5,"all 7 public funcs (dnbr, rdnbr, burn_severity_class, fireline_intensity, flame_length, rate_of_spread, kbdi) lacked Examples section (Cat1 MEDIUM) and backend-support note (Cat5 MEDIUM); fixed in deep-sweep-documentation-fire-2026-06-25-01; repo issues disabled so no issue number; examples run and outputs match numpy backend; all 7 listed in reference/fire.rst; no Cat2/Cat3/Cat4 issues",7/7
6
+ perlin,2026-06-23,,MEDIUM,2;5,"name param undocumented (Cat2) + float-dtype requirement/ValueError undocumented, no Raises section (Cat5); fixed in deep-sweep-documentation-perlin-2026-06-23; repo has issues disabled so no issue number; example runs and output matches; 1 public func (perlin) listed in reference/surface.rst",1/1
@@ -1,5 +1,6 @@
1
1
  module,last_inspected,issue,severity_max,categories_found,notes
2
2
  aspect,2026-05-29,2682,MEDIUM,4;5,"Audited 2026-05-29 (agent-a3b7c82e34312ffcb worktree, branch deep-sweep-metadata-aspect-2026-05-29). CUDA available; all 4 backends (numpy/cupy/dask+numpy/dask+cupy) run live for aspect/northness/eastness across planar and geodesic methods. Cat 1 attrs, Cat 2 coords, Cat 3 dims, and .name all preserved correctly on every backend: the 3 public functions re-emit coords=agg.coords, dims=agg.dims, attrs=agg.attrs at the xr.DataArray constructor. NEW MEDIUM finding #2682 (Cat 4 + Cat 5): the planar dask backends (_run_dask_numpy, _run_dask_cupy) called map_overlap with a default-dtype meta (np.array(()) / cupy.array(())), so the lazy DataArray advertised float64 while the chunk functions _cpu / _run_cupy cast to and return float32. numpy and cupy backends already reported float32, and the geodesic dask paths already passed dtype=np.float32, so only the two planar dask paths were inconsistent: a backend-inconsistent metadata bug where agg.dtype differs by backend and silently flips float64->float32 on .compute(). Fix in PR #2741: pass dtype=np.float32 / dtype=cupy.float32 to the planar dask meta. northness/eastness derive from aspect so they inherit the corrected dtype. 5 new tests (test_dask_numpy_advertised_dtype_matches_computed parametrized over 4 boundary modes, plus test_dask_cupy_advertised_dtype_matches_computed) assert lazy dtype == computed dtype == float32. Full aspect suite 69 passed. slope.py and curvature.py share the same default-dtype meta pattern on their planar dask paths (out of scope for this aspect-only sweep; likely same inconsistency). No CRITICAL/HIGH/LOW findings."
3
+ classify,2026-06-25,3508,MEDIUM,4;5,"Audited 2026-06-25 (agent-a5f16f6137723fc77 worktree, branch deep-sweep-metadata-classify-2026-06-25). CUDA available; all 4 backends (numpy/cupy/dask+numpy/dask+cupy) run live. All 10 public classifiers (binary/reclassify/quantile/natural_breaks/equal_interval/std_mean/head_tail_breaks/percentiles/maximum_breaks/box_plot) re-emit name=, dims=agg.dims, coords=agg.coords, attrs=agg.attrs at the xr.DataArray constructor, so Cat 1 attrs (res/crs/transform/nodatavals), Cat 2 coords (values+dtype), Cat 3 dims, and .name all preserved and identical across the 4 backends. NEW MEDIUM finding #3508 (Cat 4 + Cat 5): binary() output dtype differed by backend -- _cpu_binary allocated dtype=data.dtype so numpy/dask+numpy returned the input dtype while _run_cupy_binary used dtype='f4'. The docstring documents float32 and every other classifier emits float32 via _bin/_cpu_bin; binary was the only outlier, and for integer input the numpy path returned an integer dtype that can't hold the NaN sentinel. The _cpu_binary float32 fix + verify_dtype=True backend tests + a float64/float32/int32 dtype test landed on main via the duplicate accuracy-sweep PR #3514; PR #3513 was then rebased onto that and is now scoped to the remaining piece: _run_dask_cupy_binary passed an untyped meta=cupy.array(()) (float64) so the lazy dask+cupy array advertised float64 while computing float32 -- the same advertised-vs-computed mismatch class as aspect #2682 / focal #3217. #3513 types the meta as cupy.array((), dtype='f4') and asserts the lazy dtype in test_binary_dask_cupy. Full classify suite passes, GPU paths run live. The sibling classifiers' dask+cupy helpers (_run_dask_cupy_bin and friends) share the same untyped meta and likely the same latent lazy-dtype mismatch (out of scope, follow-up). Cat 4 nodatavals-vs-NaN is the library-wide attrs=agg.attrs convention, not classify-specific (documented, not fixed). No CRITICAL/HIGH/LOW findings."
3
4
  contour,2026-05-29,2700,HIGH,1;5,"Audited 2026-05-29 (agent-ab7fff484a8f57de2 worktree, branch deep-sweep-metadata-contour-2026-05-29). CUDA available; cupy and dask+cupy paths exercised live. contours() returns a list of (level, ndarray) tuples or a GeoDataFrame, not a DataArray, so Cat 2/3 DataArray checks reinterpreted as coordinate-transform + CRS propagation. Coordinate transform (np.interp over input dims, descending y respected) is correct and identical across all 4 backends (tracing is host-side via _contours_numpy). Cat 4 N/A: library convention is NaN-as-nodata; slope/aspect/curvature/focal do not read attrs['nodatavals'] either, so contour not reading it is consistent, not a bug. NEW HIGH finding #2700 (Cat 1/Cat 5): contours(return_type='geopandas') crashed with 'Assigning CRS to a GeoDataFrame without a geometry column is not supported' whenever the input had attrs['crs'] but the result was empty (flat raster, levels outside data range) because _to_geopandas built gpd.GeoDataFrame([], crs=crs) with no geometry column; separately the all-NaN early-return passed crs=None and silently dropped the CRS. Fix (PR #2708): _to_geopandas builds an empty frame with an explicit geometry column so the CRS attaches; all-NaN early-return forwards agg.attrs['crs']. Both empty paths now return a well-formed empty GeoDataFrame carrying the CRS. 4 new tests in TestGeoDataFrame cover populated-CRS, empty-with-CRS, all-NaN-with-CRS, and empty-without-CRS. Full contour suite 28 passed. numpy-return path emits no DataArray attrs by design (list of tuples)."
4
5
  corridor,2026-06-22,3446,HIGH,1;5,"Audited 2026-06-22 (agent-a8b2674b815bdfa3f worktree, branch deep-sweep-metadata-corridor-2026-06-22). CUDA available; all 4 backends (numpy/cupy/dask+numpy/dask+cupy) run live end-to-end for least_cost_corridor across single/threshold/relative/unreachable/pairwise paths. Cat 2 coords (x/y values + float64 dtype) and Cat 3 dims (y,x) preserved on every backend: they flow through cost_distance (coords=raster.coords, dims=raster.dims) and survive xarray's binary intersection. NEW HIGH finding #3446 (Cat 1 + Cat 5): the corridor is cd_a + cd_b where each cost-distance surface carries its SOURCE raster's attrs (cost_distance copies attrs from the source, not friction). xarray's default keep_attrs on binary + keeps only attrs present-and-equal in both operands, so when the source masks are plain marker rasters with no geo-attrs (the common case) the corridor came back with attrs=={} even though the friction surface that defines the grid had res/crs/transform/nodatavals; a downstream slope/clip on the corridor silently lost cellsize/CRS. Secondary Cat 5: .name was None whenever the two sources had different names (cost_distance renames each surface to its source .name; summing differently-named arrays drops the name). Fix (PR on this branch): non-precomputed path re-emits friction.attrs + friction.name on every output via new _apply_geo_metadata helper (single, threshold, all-NaN-unreachable, and pairwise-Dataset paths); precomputed path left on the existing source-derived behaviour since there is no friction to draw from. Only .attrs/.name set -- data values, coords, dims, dtype untouched, dask stays lazy (no compute). 10 new tests (test_corridor_inherits_friction_geo_attrs x4 backends, test_corridor_threshold_keeps_geo_attrs x4 backends, test_corridor_unreachable_keeps_geo_attrs, test_pairwise_inherits_friction_geo_attrs, test_precomputed_keeps_source_attrs_not_friction). Full corridor suite 43 passed. Cat 4 N/A: NaN-as-nodata is the library convention; corridor never reads attrs['nodatavals'] for masking. No CRITICAL/MEDIUM/LOW findings."
5
6
  cost_distance,2026-06-15,3344,MEDIUM,5,"Audited 2026-06-15 (agent-ad0b84e7f7b212360 worktree, branch deep-sweep-metadata-cost_distance-2026-06-15). CUDA available; all 4 backends (numpy/cupy/dask+numpy/dask+cupy) run live end-to-end with a rich attrs set (res/crs/transform/nodatavals/_FillValue/units). Cat 1 attrs, Cat 2 coords (values + float64 dtype), and Cat 3 dims (y,x) all preserved and identical across the 4 backends -- public cost_distance() wraps with xr.DataArray(coords=raster.coords, dims=raster.dims, attrs=raster.attrs). NEW MEDIUM finding #3344 (Cat 5): the dask+numpy and dask+cupy backends leaked the internal dask graph name (_trim-<hash> from map_overlap, asarray-<hash> from the dask+cupy convert-back path) into result.name while numpy/cupy returned None; .name was a nondeterministic per-run token that breaks .to_dataset() variable keys and any name-keyed pipeline. Same .name-leak class as proximity #2723 and zonal #2611. Fix (PR #3349 on this branch): return result.rename(raster.name) -- a constructor name= kwarg does not override a named dask array, and name=None is treated as infer-from-data, so .rename() is required. supports_dataset path unaffected (keys by var_name, verified live). New parametrized regression test test_result_name_matches_input over 4 backends x {None, named}; full cost_distance suite 63 passed (post-merge with origin/main). LOW (documented, not fixed): output float32 uses NaN as the unreachable sentinel but input nodatavals/_FillValue (e.g. -9999) are carried through verbatim, so a downstream reader masks a value that never appears -- this is the library-wide attrs=raster.attrs convention shared by proximity/slope/aspect/focal, not a cost_distance-specific bug, so fixing it in isolation would diverge this module from every peer. No CRITICAL/HIGH findings."
@@ -15,7 +15,7 @@ edge_detection,2026-03-31T18:00:00Z,SAFE,compute-bound,0,,
15
15
  emerging_hotspots,2026-03-31T18:00:00Z,SAFE,compute-bound,0,,
16
16
  erosion,2026-03-31T18:00:00Z,WILL OOM,memory-bound,2,1120,Memory guard added. Algorithm inherently global.
17
17
  fire,2026-03-31T18:00:00Z,SAFE,compute-bound,0,,
18
- flood,2026-03-31T18:00:00Z,SAFE,compute-bound,0,,
18
+ flood,2026-06-25,RISKY,compute-bound,1,3503,"Cat1 HIGH: _validate_mannings_n_dataarray used .values, eagerly materialized dask/cupy roughness raster (OOM); fixed lazy-safe. Core elementwise ops SAFE. LOW: dask+cupy host round-trip (map_blocks b.get) shared with cost_distance/surface_distance, documented not fixed."
19
19
  focal,2026-05-29,SAFE,compute-bound,1,2734,"HIGH: _hotspots_dask_cupy chunk fn round-tripped each chunk host<->GPU (cupy.asnumpy classify cupy.asarray); fixed PR 2739 to reuse _run_gpu_hotspots on device. LOW (not fixed): _apply_numpy/_hotspots_cupy use zeros_like where empty would suffice. CUDA kernels regs<=62, no register-pressure issue."
20
20
  geodesic,2026-03-31T18:00:00Z,N/A,compute-bound,0,,
21
21
  geotiff,2026-06-11,SAFE,IO-bound,0,3235,"Pass 15 (2026-06-11): 1 MEDIUM found and fixed. _pack (_attrs.py:~1795) guarded the no-sentinel integer restore with an eager bool(out.isnull().any()), which executed the whole upstream dask graph at to_geotiff(pack=True) call time; the streaming writer then executed it again, so every source chunk computed twice (measured 32 decode-task executions for 16 chunks on a 512x512 int16 SCALE/OFFSET no-GDAL_NODATA source; 71->33 total task starts post-fix). Filed #3235, fixed by mapping a per-chunk NaN guard (_pack_guard_no_nan) into the graph for dask-backed data (raises from the write's single compute; numpy keeps the eager call-time check; meta= preserves cupy backing). 9 new tests in test_pack_lazy_nan_guard_3235.py incl. fusion-proof execution counter and cupy-chunk guard unit test (dask+cupy e2e still blocked upstream by #3112). Scrutinised all 16 commits since 2026-06-08 (pack/unpack series #3065/#3075/#3079/#3129/#3174/#3175, VRT placement #3135, compression_level gate #3176, streaming banding #3136, dask+cupy writer order fix #3171): no other regressions; #3171's get-then-asarray order is intentional D2H for gpu=False. GPU validated on-device this pass: eager GPU unpack returns cupy with exact parity (387ms incl warmup, only 0-d scalar .get()s -- no bulk host round trip), dask+GPU unpack lazy (112 tasks/16 chunks, cupy meta, compute returns cupy, parity 0.0), GDS fast path intact without unpack (4 tasks/chunk); unpack disqualifying GDS is documented intentional. Dask CPU probe 4 tasks/chunk, 50k-task cap intact. Note: #1714 (_write_vrt_tiled synchronous scheduler) is now FIXED+CLOSED (scheduler='threads' at _writers/eager.py:1517) -- drop from the open-issue list. LOW noted (no fix): _pack does identity (data-0.0)/1.0 arithmetic allocating two full-array temporaries when scale==1/offset==0 (masked_nodata-only pack); prior deferred LOWs unchanged. SAFE/IO-bound holds. | Pass 14 (2026-06-09): MEDIUM found and fixed -- _write_streaming ran one dask .compute() per 256-row tile-row/strip, so a source chunk taller than the band re-executed once per band it overlapped (measured 2x at chunks=512, 4x at chunks=1024, whole upstream graph re-runs for computed pipelines). Filed #3117, fixed via _stream_row_bands: consecutive tile-rows/strips group into row bands sized by the source chunk-row span (one-chunk halo, #3007 accounting) under streaming_buffer_bytes; each band computes once and tiles/strips are carved from the materialised band. Wide rasters needing column segmentation keep the per-tile-row path. Post-fix per-chunk executions == 1 on the default read->write round trip. 5 new tests (TestRowBandRecompute3117 + _stream_row_bands unit); write/integration/parity suites pass (2195). LOW deferred (no fix): _read_geotiff_gpu_chunked parses header+all IFDs twice at graph build (_backends/gpu.py ~1367-1419, cap check then GDS probe; build-time only). GPU paths validated on-device this pass: eager gpu read returns cupy with parity, dask+GPU chunked read lazy (17 tasks/4 chunks) with parity; GPU writer full materialisation is documented intentional (streaming_buffer_bytes no-op). Read path keeps 50k-task graph cap; dask read probe 4 tasks/chunk. SAFE/IO-bound holds. | Pass 13 (2026-05-20): 1 MEDIUM found and fixed. _nvjpeg_batch_encode (_gpu_decode.py:~L1560) and _nvjpeg2k_batch_encode (~L2958) called cupy.cuda.Device().synchronize() inside the per-tile encode loops, a whole-device fence that blocked every CUDA stream and serialised concurrent work (e.g. predictor encodes on other streams). The decode-side counterpart _try_nvjpeg_batch_decode already used cupy.cuda.Stream.null.synchronize() at L1442; the encoder side was inconsistent. Filed #2212 and fixed both encoders to use Stream.null.synchronize(), scoping the per-tile sync to the default stream the encode/retrieve calls were issued on. nvJPEG / nvJPEG2000 encoders maintain a single shared state per encoder so encodes within a batch are inherently serial; the fix removes the device-wide blocker without changing the API ordering contract. 5 new tests in test_nvjpeg_encode_stream_sync_2212.py (AST checks that neither encoder contains Device().synchronize() inside a for-loop, that both call Stream.null.synchronize() in the loop, and that the decoder reference pattern stays pinned). All 5 new tests + 19 existing related encode/decode tests pass. nvjpeg/nvjpeg2k shared libs not present on this host so end-to-end encode verification is gated; add cuda-unavailable-libs note to re-validate on a host with the RAPIDS conda env. SAFE/IO-bound verdict holds; no change in dask graph cost. Dask probe: 2560x2560 deflate-tiled file via read_geotiff_dask(chunks=256) yields 400 tasks for 100 chunks (4 tasks/chunk), well under the 50K cap. LOW deferred (no fix in this PR): _build_ifd called twice per IFD level in _assemble_standard_layout (_writer.py:1531+1543), _assemble_cog_layout (1582+1625), and the COG overview path (2519+2546+2740) -- the first call's bytes are discarded; only the overflow byte length is used to compute pixel_data_offset. Cost is bounded by IFD count (typically 1-5 overview levels) so absolute impact is minor. Pre-existing pattern. | Pass 12 (2026-05-18): 1 MEDIUM found and fixed. _try_nvjpeg2k_batch_decode at _gpu_decode.py:~L2725-2778 allocated per-tile per-component cupy.empty buffers (N*S round-trips through the cupy memory pool) and called cupy.cuda.Device().synchronize() once per tile, forcing default-stream serialisation that defeats nvJPEG2000's internal pipelining. Filed #2107 and fixed: pre-allocate a single d_comp_pool sized n_tiles*samples*tile_height*pitch under a _check_gpu_memory guard, derive per-tile/per-component views as slab offsets, and replace the per-tile sync with a single batch-end sync. Same pattern as #1659 (_try_nvcomp_from_device_bufs), #1688 (_try_kvikio_read_tiles), #1712 (_nvcomp_batch_compress). 7 new tests in test_nvjpeg2k_single_alloc_2107.py: AST-level structural assertions confirm no cupy.empty inside the for-loop and no Device().synchronize() inside the loop, plus pool/per_tile_comp_bytes presence and _check_gpu_memory guard checks; lib-absent short-circuit; unsupported-dtype cleanup contract; cupy-only pool slab-non-overlap test (gpu-marked). libnvjpeg2k.so not present on this host so the end-to-end nvJPEG2000 decode is gated -- note added to re-validate on a host with the RAPIDS conda env. All 30 jpeg2000/compression tests + 7 new tests pass. SAFE/IO-bound verdict holds (no change in dask graph cost). Dask probe: 4096x4096 deflate-tiled file via read_geotiff_dask(chunks=512) yields 256 tasks for 64 chunks (4 tasks/chunk), well under the 50K cap. | Pass 11 (2026-05-18): 1 MEDIUM found and fixed. _read_strips (_reader.py:~L1972) and _fetch_decode_cog_http_strips (_reader.py:~L2670) decoded strips sequentially in a Python for-loop while the tile counterparts (_read_tiles L2146, _fetch_decode_cog_http_tiles L2898) gated parallel decode on _PARALLEL_DECODE_PIXEL_THRESHOLD via ThreadPoolExecutor. Filed #2100 and fixed: both strip paths now collect jobs, parallel-decode when n_strips > 1 and strip_pixels >= 64K, then place sequentially. Measured (uint16, 4-core): 4096x4096 deflate 130ms->34ms (3.82x), 8192x8192 deflate 531ms->146ms (3.63x), 8192x8192 zstd 211ms->85ms (2.48x), uncompressed 25ms->22ms (1.14x). 5 new tests in test_parallel_strip_decode_2100.py (parallel/serial parity, pool-engaged on multi-strip, serial-path for single-strip, windowed cross-strip read, HTTP COG strip parity). 3998 tests pass; 8 pre-existing failures predating this change (predictor2 BE + size_param_validation_gpu_vrt reference now-private read_to_array attr). SAFE/IO-bound verdict holds. | Pass 10 (2026-05-15): 1 new MEDIUM found and fixed; 2 LOW noted. MEDIUM (_reader.py:2737): _fetch_decode_cog_http_tiles decoded tiles sequentially in a Python for-loop after the concurrent fetch landed (issue #1480). Local _read_tiles parallelises decode whenever tile_pixels >= 64K via ThreadPoolExecutor (_reader.py:2017); the HTTP path was structurally similar but never picked up the same gate, so wide windowed reads of multi-tile COGs left deflate/zstd decode single-threaded. Mirrored the local-path threshold + pool. 5 new tests in test_cog_http_parallel_decode_2026_05_15.py (parallel + serial round-trip correctness, pool-instantiation branch selection above the threshold, single-tile path skips the pool, structural _decode_strip_or_tile call count == n_tiles). All 262 COG/HTTP tests pass; 3162 of 3164 selected geotiff tests pass overall (2 pre-existing failures predating Pass 9 per prior notes -- test_predictor2_big_endian_gpu_1517 references the now-private read_to_array attr, and the test_size_param_validation_gpu_vrt_1776 tile_size=4 validator failure). LOW deferred (no fix in this PR): (1) _block_reduce_2d_gpu (_gpu_decode.py:3142/3163/3189) does bool(mask.any().item()) per overview level when nodata is set, paying one device sync per level; the alternative (unconditional cupy.putmask) always pays the work cost and the short-circuit is correct under the current API. (2) _nvcomp_batch_compress adler32 staging (_gpu_decode.py:2543-2546) issues n_tiles slice-assign kernels into a fresh contig buffer despite all callers passing slices of a single underlying d_tile_buf; an API refactor to accept the source buffer directly would skip the rebuild. SAFE/IO-bound verdict holds. Dask probe: 2560x2560 chunks=256 yields 400 tasks (4 per chunk), well under the 50000 cap. GPU probe: 1024x1024 float32 zstd read returns CuPy-backed in 236 ms with no host round-trip. | Rockout 2026-05-15: LOW filed #1934 -- _apply_nodata_mask_gpu used cupy.where (allocating); switched to cupy.putmask on the already-owned buffer (float path) and on the post-astype float64 buffer (int path). Saves one chunk-sized device allocation per call. 7 new tests in test_apply_nodata_mask_gpu_inplace_1934.py; 52 related nodata tests pass. | Pass 8 (2026-05-12): 1 new MEDIUM found and fixed. _assemble_standard_layout/_assemble_cog_layout returned bytes(bytearray), doubling peak memory transiently during eager writes. Filed #1756, fixed by returning the bytearray directly. Measured: 95 MB uint8 raster peak drops 202 MB -> 107 MB. _write_bytes / parse_header already accepted the buffer protocol so the change is transparent to callers. 6 new tests in test_assemble_layout_no_bytes_copy_1756.py. 2123 existing geotiff tests pass; the 10 unrelated failures (test_no_georef_windowed_coords_1710, test_predictor2_big_endian_gpu_1517) reference the now-private read_to_array attribute (commit 8adb749, issue #1708) and predate this change. SAFE/IO-bound verdict holds. | Pass 7 (2026-05-12): re-audit identified 4 MEDIUM findings, all real, all backed by microbenches. (1) unpack_bits sub-byte loops for bps=2/4/12 in _compression.py:836-878 were 100-200x slower than vectorised numpy (filed #1713, fixed in this branch: bps=4 2M pixels drops from 165ms to 3ms = 55x; bps=2/12 similar). (2) _write_vrt_tiled at __init__.py:1708 uses scheduler='synchronous' on independent tile writes; measured 33% slowdown on 256-tile zstd write vs threads scheduler (filed #1714, no fix yet). (3) _nvcomp_batch_compress at _gpu_decode.py:2522-2526 still does per-tile cupy.get().tobytes() despite #1552 / #1659 fixing the same pattern elsewhere; measured 45% reduction with concat+single get on n=1024 (filed #1712, no fix yet). (4) _nvcomp_batch_compress at _gpu_decode.py:2457 uses per-tile cupy.empty allocations; 1024 tiles 16KB drops from 4.7ms to 1.0ms with single contiguous + views (bundled into #1712). Cat 6 OOM verdict: SAFE/IO-bound holds -- read_geotiff_dask caps task count at _MAX_DASK_CHUNKS=50_000 and per-chunk memory is bounded by chunk size. _inflate_tiles_kernel resource usage on Ampere: 67 regs/thread, 2896B local/thread, 8192B shared/block (LZW kernel: 29 regs, 24576B shared) -- register pressure under control; high local memory in inflate is unavoidable (LZ77 state) but only thread 0 in each block uses it. | Pass 4 (2026-05-10): re-audit after #1559 (centralise attrs across all read backends). New _populate_attrs_from_geo_info helper at __init__.py:301 runs once per read, not per-chunk -- no perf impact. Probe: 2560x2560 deflate-tiled file opened via read_geotiff_dask yields 400 tasks (4 tasks/chunk for 100 chunks), well under 1M cap. read_geotiff_gpu(1024x1024) returns cupy.ndarray end-to-end with no host round-trip (226ms incl. write+decode). No new HIGH/MEDIUM findings. SAFE/IO-bound holds. | Pass 3 (2026-05-10): SAFE/IO-bound. Audited 4 perf commits: #1558 (in-place NaN writes on uniquely-owned buffers correct), #1556 (fp-predictor ngjit ~297us/tile for 256x256 float32), #1552 (single cupy.concatenate + one .get() for batched D2H at _gpu_decode.py:870-913), #1551 (parallel decode threshold >=65536px engages 256x256 default at _reader.py:1121). Bench: 8192x8192 f32 deflate+pred2 256-tile write 782ms; 4096x4096 f32 deflate read 83ms with parallel decode. Deferred LOW (none filed, all <10% MEDIUM threshold): _writer.py:459/1109 redundant .copy() before predictor encode (~1% per tile), _compression.py:280 lzw_decompress dst[:n].copy() (~2% per LZW tile decode), _writer.py:1419 seg_np.copy() before in-place NaN substitution (negligible, conditional path), _CloudSource.read_range opens fresh fsspec handle per range (pre-existing, predates audit scope). nvCOMP per-tile D2H batching break-even confirmed (variable sizes need staging buffer, no win). | Pass 3 (2026-05-10): audited f157746,39322c3,f23ec8f,1aac3b7. All 5 commits correct. Redundant .copy() in _writer.py:459,1109 and _compression.py:280 (1-2% overhead, LOW). _CloudSource.read_range() per-call open is pre-existing arch issue. No HIGH/MEDIUM regressions. SAFE. | re-audit 2026-05-02: 6 commits since 2026-04-16 (predictor=3 CPU encode/decode, GPU predictor stride fix, validate_tile_layout, BigTIFF LONG8 offsets, AREA_OR_POINT VRT, per-tile alloc guard). 1M dask chunk cap intact at __init__.py:948; adler32 batch transfer intact at _gpu_decode.py:1825. New code is metadata validation and dispatcher logic with no extra materialization or per-tile sync points. No HIGH/MEDIUM regressions. | Pass 5 (2026-05-12): re-audit identified MEDIUM in _gpu_decode.py:1577 _try_nvcomp_from_device_bufs: per-tile cupy.empty + trailing cupy.concatenate doubled peak VRAM and added serial concat. Filed #1659 and fixed to single-buffer + pointer offsets (matches LZW/deflate/host-buffer patterns at L1847/L1878/L1114). Microbench (alloc+concat overhead only, not full nvCOMP latency): n=256 tile_bytes=65536 drops 3.66ms->0.69ms, n=256 tile_bytes=262144 drops 8.18ms->0.13ms. Tests: 5 new tests in test_nvcomp_from_device_bufs_single_alloc_1659.py (codec short-circuit, no-lib short-circuit, memory-guard contract, real ZSTD round-trip via nvCOMP, structural single-buffer check). 1458 existing geotiff tests pass, 3 unrelated matplotlib/py3.14 failures pre-existing. SAFE/IO-bound verdict holds. | Pass 6 (2026-05-12): re-audit on top of #1659. New HIGH in _try_kvikio_read_tiles at _gpu_decode.py:941: per-tile cupy.empty() + blocking IOFuture.get() inside loop serialised GDS reads to ~1 outstanding pread, missed parallelism the kvikio worker pool was designed for, paid per-tile cupy.empty setup (matches #1659 anti-pattern in nvCOMP path), and lacked _check_gpu_memory guard. Filed #1688 and fixed to single contiguous buffer + batched submit + guard. Microbench with 8-worker pool simulation: 256 tiles@1ms latency drops 256ms->38.7ms (~6.6x); single-thread simulation 256ms->28.5ms (9x). Tests: 9 new tests in test_kvikio_batched_pread_1688.py (kvikio-absent path, single-buffer pointer arithmetic, submit-before-get ordering, memory guard, partial-read fallback, round-trip data, zero-size/all-sparse tiles). All 1577 geotiff tests pass except pre-existing matplotlib/py3.14 failures."
@@ -7,7 +7,7 @@ dasymetric,2026-06-20,3411,HIGH,1;3;4,"F401 is_dask_cupy unused (HIGH); E128 lin
7
7
  diffusion,2026-06-20,3421,HIGH,3;4,F401 unused not_implemented_func (HIGH); isort import-ordering drift (MEDIUM); no Cat1/2/5. Fixed in PR off issue 3421.
8
8
  fire,2026-06-19,3395,HIGH,3;4,Cat3 F401 math.log unused + F841 S_T unused local; Cat4 isort utils import reorder. No Cat1/2/5. Fixed in #3395 rockout.
9
9
  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."
10
- geotiff,2026-06-14,3329,MEDIUM,4,"Re-sweep after #3259: flake8 baseline 0 across all 33 production files (flake8 7.3.0). Cat 4 only: isort (line_length=100) drift in _writers/eager.py (the from .._validation import block wrapped at ~80 cols, not 100); rewrapped, no behavioural change. Cat 1/2/3/5 clean: no E/W/F codes; grep hits for == False (comment), ': dict'/': list' annotations, and type=getaddrinfo kwarg are all false positives, not shadowed builtins or comparisons. isort+flake8 clean after fix; import smoke test ok. PR via #3329."
10
+ geotiff,2026-06-25,,MEDIUM,4,"Re-sweep 2026-06-25 (file last modified 2026-06-24). flake8 baseline 0 across all 35 production files AND all tests (flake8 7.x, max-line-length=100). Cat 4 only: one isort (line_length=100) finding in tests/unit/test_exception_exports_3265.py - the deferred 'from xrspatial.geotiff._reader import PixelSafetyLimitError as reader_pixel_err' was written with a backslash continuation but joins to 83 chars, so isort wants it on one line. Joined it; formatting only, no behaviour change. Cat 1/2/3/5 clean: no E/W/F codes; grep hits for == False (a comment), ': dict'/': list' annotations, and type=getaddrinfo kwarg are false positives (not shadowed builtins or comparisons). isort+flake8 clean after fix; tests collect ok. Issue-create denied by auto-mode classifier; fix committed to deep-sweep branch; PR #3517 opened."
11
11
  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."
12
12
  interpolate,2026-06-12,3286,HIGH,3;4,"Full subpackage sweep (_idw, _kriging, _spline, _validation). Cat 3 F401: unused 'import math' in _idw.py L5 (IDW kernels are pure arithmetic; _spline.py keeps math for math.log in TPS kernels; not re-exported, __init__ exports only idw/kriging/spline). Cat 4 isort: _idw.py + _spline.py 5-line xrspatial.utils from-import reflowed to 2 lines under line_length=100, matching _kriging.py from #2916. Cat 1/2/5 clean (no E/W codes; grep: no bare except, mutable defaults, ==None/True, shadowed builtins). flake8+isort clean after fix; 66 interpolation tests pass (CUDA available). PR open."
13
13
  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."
@@ -6,9 +6,10 @@ corridor,2026-06-22,,HIGH,1;3;4;5,"Deep-sweep 2026-06-22 test-coverage on a CUDA
6
6
  cost_distance,2026-06-16,3367,MEDIUM,1;2,"Pass (2026-06-16 deep-sweep test-coverage, CUDA host). cost_distance is heavily tested: 1122 test lines for 1354 src lines, all 4 backends parametrized + regression tests for #1191/#880/#1252/#1262/#3340/#3341/#3343/#3344. Found one MEDIUM Cat 1+2 gap: _cost_distance_dask f_min<=0 early return (all-impassable friction, finite max_cost -> da.full NaN preserving chunks) was unreached -- numpy equiv covered by test_source_on_impassable_cell, iterative dask by test_iterative_narrow_corridor, but the bounded map_overlap wrapper shortcut was not. Filed #3367, added test_dask_all_impassable_friction_returns_nan (all-zero friction, dask+numpy chunks(3,3), max_cost=5; asserts all-NaN, dask-backed, npartitions>1). RAN + PASSED; -W error::UserWarning confirms early return taken (no iterative warning). Full file 85 passed on CUDA host. LOW (documented, not fixed): non-square cellsize numeric correctness untested (_make_meta_raster uses res=(2,3) but test_metadata_preserved checks metadata only)."
7
7
  dasymetric,2026-06-20,3407;3406,HIGH,2;3;4;5,"deep-sweep test-coverage on a CUDA host (CUDA available, GPU tests ran). Module is well covered (813 test loc / 834 src): 4-backend equivalence for disaggregate weighted+binary, conservation, NaN/nodata/negative-weight, limiting_variable + cupy/dask NotImplemented guards, pycnophylactic numpy+cupy+dask-raises, validate_disaggregation all backends, memory guards (#1261). Filed #3407 (test-only) for real gaps and added 4 new test classes (11 passed, 2 xfailed). Cat5 HIGH: metadata (attrs res/crs + coords) never asserted -> TestMetadataPreservation (numpy/dask). Cat3 HIGH: true 1x1 raster untested (only 1x2 strip) -> TestSinglePixel for disaggregate weighted/binary + pycnophylactic (degenerate no-shift smoothing) + dask parity. Cat2 MEDIUM: Inf weight collapses zone total to 0 (silent conservation break) -> TestInfWeight pins current behaviour. Cat4 MEDIUM: 3-class limiting_variable (multi-break + per-class caps) untested despite docstring -> TestLimitingVariableThreeClass. SOURCE BUG found (filed #3406, NOT fixed - test-only sweep): pycnophylactic raises ValueError (np.nanmax on zero-size array) when no pixel is valid for smoothing (all-NaN zones or no zone id in values); disaggregate handles same input gracefully (all-NaN). Pinned with TestPycnophylacticEmptyValid xfail(strict, raises=ValueError) -> flips red when #3406 fixed. LOW (documented, not fixed): non-square cellsize never exercised (all tests use res 0.5/0.5); disaggregate cupy/dask+cupy 1x1 + metadata not separately added (eager numpy gap was the real one, GPU dispatch already covered by TestCrossBackend)."
8
8
  diffusion,2026-06-20,3422,HIGH,1;2;3;4,"Pass 1 (2026-06-20, deep-sweep test-coverage, CUDA host). diffuse() dispatch table registers all 4 backends but test_diffusion.py only exercised numpy + dask+numpy. Cat 1 HIGH: cupy (_diffuse_cupy/_diffuse_step_gpu) and dask+cupy (_diffuse_dask_cupy/_diffuse_chunk_cupy) registered but never invoked -- no test ran them. Cat 4 HIGH: boundary accepts nan/nearest/reflect/wrap; only nearest+wrap tested, reflect had none. Cat 3 HIGH: 1x1 single-pixel and Nx1/1xN strip rasters never tested. Cat 2 MEDIUM: NaN tested numpy-only; Inf and all-NaN inputs untested. Filed #3422, added 14 tests (PR #3424, test-only, source untouched): cupy/dask+cupy parity vs numpy (incl. spatially-varying alpha + NaN propagation), reflect boundary across all 4 backends, 1x1 + Nx1 + 1xN (numpy + chunked dask strip), all-NaN stays NaN, Inf contamination smoke test. All 14 RAN+PASSED on a CUDA host; the 4 cupy/dask+cupy tests genuinely executed (not skipped); full file 39 passed. All paths verified correct before the tests were added -- coverage gap, not a bug. LOW (documented, not fixed): non-square cellsize (res[0]!=res[1]) never exercised -- diffuse uses res[0] as dx and assumes square cells; empty 0-row/0-col raster untested; asv benchmark absent; 'nan' boundary-mode edge=NaN behaviour not directly asserted on diffuse (covered indirectly via wrap/nearest)."
9
- fire,2026-06-19,,HIGH,1;5,"Cat1 HIGH: dask+cupy dispatch registered but untested for rdnbr/burn_severity_class/fireline_intensity/flame_length/rate_of_spread/kbdi (only dnbr had it). Cat5 MEDIUM: only dnbr asserted attrs/coords/dims preservation. Added 6 test_numpy_equals_dask_cupy + 6 test_output_preserves_metadata; all 66 fire tests pass on a CUDA host. Cat2 LOW (not fixed): Inf inputs untested (pure per-cell math, low risk)."
9
+ fire,2026-06-25,,HIGH,2,"Deep-sweep 2026-06-25 test-coverage on a CUDA host. Backend matrix already complete: all 7 public funcs (dnbr/rdnbr/burn_severity_class/fireline_intensity/flame_length/rate_of_spread/kbdi) x 4 backends present and green (Cat 1 no gap). NaN covered (per-func nan_propagation + #3394 dtype parity). Cat 4 covered: rate_of_spread tests all 13 fuel models + invalid 0/14; kbdi annual_precip invalid 0/-100; fireline heat_content default+custom. Cat 5 covered via general_output_checks on every func. Found one gap: Cat 2 +Inf/-Inf inputs were untested on every function. Probed all 4 backends live: behavior is fully consistent and well-defined (no divergence, no bug) -- e.g. dnbr inf-inf->nan, burn_severity_class +inf->7/-inf->1, kbdi prev=inf clamps to 800, rate_of_spread slope=inf->nan. Added test-only regression: per-func numpy Inf contract (locks exact values) + 4-backend Inf parity (28 new tests, all RAN and PASSED on GPU). No source change; the kernels' only finite guard is v!=v so these lock that contract. Cat 3 1x1/strip: per-pixel kernels (no neighborhood window) so no degeneracy risk, and 1x1/1xN already exercised by kbdi/rdnbr/flame tests -> LOW, not added."
10
+ flood,2026-06-25,,MEDIUM,1,"Deep-sweep 2026-06-25 test-coverage on a CUDA host. Module is densely tested (1051 test LOC vs 966 source). Backend matrix nearly complete: all 7 public funcs x 4 backends present and green EXCEPT vegetation_roughness mode='ndvi' on dask+cupy -- _veg_roughness_ndvi_dask_cupy was dispatched (flood.py:585) but never invoked by any test (nlcd dask+cupy, ndvi cupy, ndvi dask all tested). Cat 1 MEDIUM: added TestVegRoughnessDaskCuPy::test_ndvi_numpy_equals_dask_cupy mirroring the nlcd case; GPU-validated locally (passed, full file 89 passed). Cat 2 NaN well covered per-func incl #1104 (NaN curve_number) and #1437 (mannings_n DataArray) regressions; Inf inputs untested but low-risk (HAND/rainfall Inf -> NaN), not flagged. Cat 3 1xN strips + 1x1 covered for several funcs. Cat 5 metadata preserved is asserted on every backend test via general_output_checks (verify_attrs defaults True), so inundation/curve_number_runoff lacking a dedicated coords test is NOT a real gap. No source bugs found."
10
11
  focal,2026-06-10,3220;3219;3225,HIGH,1;2;3;4,"Deep-sweep 2026-06-10 on CUDA host, all 4 backends executed. Filed #3220 (coverage) and added 36 tests in PR branch: Inf inputs for mean/focal_stats (HIGH Cat2 - no Inf test existed anywhere), mean NaN input (HIGH Cat2 - default excludes=[nan] semantics never asserted), 1x1 + 1xN/Nx1 strips (HIGH Cat3), empty 0-row raster numpy-only (MEDIUM Cat3), mean passes=2 == mean(mean) and excludes sentinel -9999 behavioral tests (MEDIUM Cat4), dask+cupy non-default boundary modes for mean/apply/focal_stats (MEDIUM Cat1/4). Bugs surfaced, filed separately (NOT fixed here): #3219 hotspots silently returns all zeros on Inf input (nan global std passes the std==0 guard, all 4 backends); #3225 empty raster works on numpy but crashes cupy (raw CudaAPIError) and dask (map_overlap depth ValueError). hotspots+Inf and non-numpy empty behavior left unpinned until those are fixed. Backend matrix for the 4 public funcs was already solid (all 4 backends + parity); boundary modes covered except dask+cupy. Siblings filed #3214-3217 same day (dtype/docstring/apply-default-func) - no overlap."
11
- geotiff,2026-06-12,3266,MEDIUM,1,"Pass 22 (2026-06-12, deep-sweep test-coverage): delta audit of the ~20 commits since pass 21 (06-09..06-12, mostly pack/unpack fixes + #3241 GPU streaming writer + coregister #3254/#3248). Filed #3266 (tests). Cat 1 MEDIUM: pack=True gained working gpu/dask+gpu support in #3240, but three pack features were tested numpy+dask only: float32 width preservation (#3080, test_pack_float_width_3080.py), nodata kwarg fill (#3168, test_pack_nodata_kwarg_3168.py), band-subset per-band SCALE/OFFSET rewrite (#3161, test_pack_band_subset_3161.py). Live probe on this CUDA host: all six gpu/dask+gpu legs pass today (no source bug, pure coverage gap). Added one gpu/dask-gpu parametrized round-trip test per file (6 tests, requires_gpu, RUN+passing locally) and fixed two stale docstrings claiming unpack/pack is CPU-only (wrong since #3075/#3240). Verified NOT gaps this pass: #3128 int64 sentinel tests cover eager+dask+gpu; #3241 streaming writer landed with byte-identical band-first/band-last/BytesIO/small-buffer tests; #3104 scale-zero rejection has gpu legs; #3169 revived the dead compression-corpus oracle gate. Out of scope: coregister=True lives in accessor.py (excluded module); its multi-band + polar gaps are documented as experimental caveats in docs/source/reference/geotiff.rst (#3248). LOW (carried, documented not fixed): Inf as the declared nodata sentinel never tested. || PREVIOUS: Pass 21 (2026-06-09, deep-sweep test-coverage): filed #3114 (tests) + #3112 (source bug). Cat 1 HIGH: to_geotiff(pack=True) round-trip was tested only on numpy and dask+numpy (write/test_pack_3064.py); #3075 made unpack=True work on gpu and dask+gpu reads, but no test packed a GPU-read array back. Live probe on this CUDA host: BOTH GPU legs crash today -- eager gpu raises AttributeError (cupy has no astype, the known cupy 13.6/xarray 2025.12 where/astype incompat) and dask+gpu raises TypeError (numpy fill value inside cupy.where) -- both from _pack's out.fillna(nodata) in _attrs.py; _writers/gpu.py says the pre-dispatch re-pack is supposed to make every write path work. Source bug filed as #3112; test-only PR adds test_pack_round_trip_gpu (gpu + dask-gpu params, requires_gpu, xfail(strict=True) on #3112 so the fix flips them loudly) and fixes the stale module docstring claiming GPU rejects mask_and_scale. Ran on CUDA host: 13 passed, 2 xfailed. Verified NOT gaps this pass (probed before flagging): empty/zero-band writer guard is covered (test_basic.py 2075/2095 blocks incl. gpu + dask + streaming entry points); degenerate shapes covered on all 4 read backends (read/test_degenerate_shapes.py); overview_resampling all 7 modes parametrized; missing_sources raise/warn + invalid, band_nodata first/invalid, unpack on all 4 backends, masked/parse_coordinates/lock/cache/default_name/name-deprecation all exercised; attrs contract per-backend (attrs/test_contract.py). LOW (documented, not fixed): Inf as the declared nodata sentinel is never tested (only one nan+inf data round-trip in test_edge_cases.py). || PREVIOUS: 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."
12
+ geotiff,2026-06-25,3518,MEDIUM,1,"Pass 23 (2026-06-25, deep-sweep test-coverage, CUDA host): delta audit of the ~15 geotiff commits since pass 22 (06-12..06-24): #3483 categorical PAM sidecar, #3375/#3376/#3380 xarray backend engine, #3373/#3374 chunked GPU read-once, #3371/#3372 reject predictor+lossy codec, #3331/#3332 reject zero/non-finite ModelPixelScale, #3327 gate dict gdal_metadata behind rich-tag opt-in, #3323/#3325 masked_nodata dtype-cast, #3277 pack nodata native width. Filed #3518 (tests). Cat 1 MEDIUM: the #3483 categorical PAM sidecar (_pam.py + _write_category_sidecar in _writers/eager.py) is round-trip tested only on the eager numpy write path (xrspatial/tests/test_rasterize_categorical_3482.py); the dask streaming (eager.py:1063) and GPU/nvCOMP (eager.py:880) write branches each have their own sidecar emit call that no test touches. Live probe on this CUDA host: dask + GPU categorical round-trips both emit the sidecar and read back names+RGBA colors today (no source bug, pure coverage gap). Added geotiff/tests/write/test_category_sidecar_backends_3483.py: dask write round-trip + GPU write round-trip (requires_gpu, RAN+passing locally) + names-only (category_colors=None build branch + names-only read path, never hit by the existing colors-always suite). 3 tests pass. Verified NOT gaps this pass: #3327 dict gdal_metadata gate has 43-line test_contract.py coverage; #3331/#3332 zero pixel scale covered by unit/test_degenerate_pixel_size_3331.py; #3371/#3372 predictor+lossy-codec reject has 2 test files; #3375/#3376/#3380 xarray engine covered by test_xarray_backend_3365.py + coregister 3376/3379; #3277 pack native width has 3 test files. LOW (carried, documented not fixed): Inf as the declared nodata sentinel still never tested. || Pass 22 (2026-06-12, deep-sweep test-coverage): delta audit of the ~20 commits since pass 21 (06-09..06-12, mostly pack/unpack fixes + #3241 GPU streaming writer + coregister #3254/#3248). Filed #3266 (tests). Cat 1 MEDIUM: pack=True gained working gpu/dask+gpu support in #3240, but three pack features were tested numpy+dask only: float32 width preservation (#3080, test_pack_float_width_3080.py), nodata kwarg fill (#3168, test_pack_nodata_kwarg_3168.py), band-subset per-band SCALE/OFFSET rewrite (#3161, test_pack_band_subset_3161.py). Live probe on this CUDA host: all six gpu/dask+gpu legs pass today (no source bug, pure coverage gap). Added one gpu/dask-gpu parametrized round-trip test per file (6 tests, requires_gpu, RUN+passing locally) and fixed two stale docstrings claiming unpack/pack is CPU-only (wrong since #3075/#3240). Verified NOT gaps this pass: #3128 int64 sentinel tests cover eager+dask+gpu; #3241 streaming writer landed with byte-identical band-first/band-last/BytesIO/small-buffer tests; #3104 scale-zero rejection has gpu legs; #3169 revived the dead compression-corpus oracle gate. Out of scope: coregister=True lives in accessor.py (excluded module); its multi-band + polar gaps are documented as experimental caveats in docs/source/reference/geotiff.rst (#3248). LOW (carried, documented not fixed): Inf as the declared nodata sentinel never tested. || PREVIOUS: Pass 21 (2026-06-09, deep-sweep test-coverage): filed #3114 (tests) + #3112 (source bug). Cat 1 HIGH: to_geotiff(pack=True) round-trip was tested only on numpy and dask+numpy (write/test_pack_3064.py); #3075 made unpack=True work on gpu and dask+gpu reads, but no test packed a GPU-read array back. Live probe on this CUDA host: BOTH GPU legs crash today -- eager gpu raises AttributeError (cupy has no astype, the known cupy 13.6/xarray 2025.12 where/astype incompat) and dask+gpu raises TypeError (numpy fill value inside cupy.where) -- both from _pack's out.fillna(nodata) in _attrs.py; _writers/gpu.py says the pre-dispatch re-pack is supposed to make every write path work. Source bug filed as #3112; test-only PR adds test_pack_round_trip_gpu (gpu + dask-gpu params, requires_gpu, xfail(strict=True) on #3112 so the fix flips them loudly) and fixes the stale module docstring claiming GPU rejects mask_and_scale. Ran on CUDA host: 13 passed, 2 xfailed. Verified NOT gaps this pass (probed before flagging): empty/zero-band writer guard is covered (test_basic.py 2075/2095 blocks incl. gpu + dask + streaming entry points); degenerate shapes covered on all 4 read backends (read/test_degenerate_shapes.py); overview_resampling all 7 modes parametrized; missing_sources raise/warn + invalid, band_nodata first/invalid, unpack on all 4 backends, masked/parse_coordinates/lock/cache/default_name/name-deprecation all exercised; attrs contract per-backend (attrs/test_contract.py). LOW (documented, not fixed): Inf as the declared nodata sentinel is never tested (only one nan+inf data round-trip in test_edge_cases.py). || PREVIOUS: 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."
12
13
  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."
13
14
  interpolate,2026-06-12,3290,MEDIUM,2;3;4;5,"Deep-sweep 2026-06-12 on CUDA host. Backend coverage already complete: all 4 backends exercised for idw/kriging/spline incl. cross-backend equivalence and variance paths; no Cat 1 gaps. Filed #3290 for MEDIUM gaps, all verified correct-by-probe before filing (test-only fix): idw fill_value zero-weight branch (deterministic via 1e200 distance weight underflow; added numpy+dask+cupy, cupy RAN+PASSED), idw power only tested at default (exact oracle 10/(2^p+1)), spline collinear lstsq fallback, kriging duplicate points + all-equal-z (zero-variance variogram) + exactly-singular K regularisation retry (unit test on _build_kriging_matrix with all-zero variogram), spline/kriging 1x1 template, Inf/-Inf point filtering (only NaN was tested), lat/lon dim-name propagation (parametrized all 3 funcs), idw attrs preservation, 0-column template. Remaining minor untested: _build_kriging_matrix warn-then-NaN branch (needs mocked LinAlgError on retry). LOW documented not fixed: no asv benchmarks, non-uniform cell spacing unasserted. Full file 82 passed 0 skipped locally."
14
15
  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."
@@ -2,6 +2,39 @@
2
2
  -----------
3
3
 
4
4
 
5
+ ### Version 0.10.13 - 2026-06-26
6
+
7
+ #### New features
8
+ - geotiff: write continuous-raster symbology sidecars (.qml + PAM statistics) from to_geotiff (#3537) (#3538)
9
+ - templates: add list_templates() to discover from_template names (#3536)
10
+ - templates: add major world cities as bounding-box templates in from_template (#3534)
11
+ - fire: accept m/min spread rate in fireline_intensity by default (#3527) (#3529)
12
+ - hydrology: make routing the sole public hydrology API surface (#3528) (#3530)
13
+ - accessor: add coregister=True to .xrs.rasterize (#3492) (#3493)
14
+
15
+ #### Bug fixes and improvements
16
+ - flood: fix float32 dtype leak in flood_depth/curve_number_runoff dask backends (#3499) (#3509)
17
+ - flood: fix dask backends emitting float32 output for float32 input (#3498)
18
+ - flood: make mannings_n DataArray validation lazy-safe (#3503) (#3507)
19
+ - flood: fix E305 and collapse utils import (#3504)
20
+ - flood: add missing reference entries, examples, and backend notes (#3502)
21
+ - binary: output float32 on all backends (#3514)
22
+ - binary: type the dask+cupy meta so the lazy dtype matches float32 (#3508) (#3513)
23
+ - geotiff: stop a short-row PAM RAT from crashing open_geotiff (#3522)
24
+ - geotiff: add Examples section to to_geotiff docstring (#3521)
25
+ - geotiff: cover categorical PAM sidecar round-trip on dask and GPU write paths (#3518) (#3519)
26
+ - geotiff: join backslash-continued import in test (isort) (#3517)
27
+ - templates: use CF Conventions metadata instead of crs_units/crs_name (#3532)
28
+ - templates: fill all-NaN templates in generate_terrain (numpy/cupy) (#3526)
29
+ - templates: honor requested resolution exactly in from_template() (#3494) (#3495)
30
+ - classify: fix stale example outputs and add missing examples (#3516)
31
+ - classify: cover all-NaN degenerate input across classifiers (#3515)
32
+ - classify: document num_sample=None in natural_breaks docstring (#3505)
33
+ - fire: add Examples sections and backend-support notes to docstrings (#3512)
34
+ - fire: add Inf/-Inf regression tests (#3500)
35
+ - multispectral: test vegetation_roughness ndvi on dask+cupy backend (#3497)
36
+
37
+
5
38
  ### Version 0.10.12 - 2026-06-25
6
39
 
7
40
  #### New features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xarray-spatial
3
- Version: 0.10.12
3
+ Version: 0.10.13
4
4
  Summary: xarray-based spatial analysis tools
5
5
  Home-page: https://github.com/xarray-contrib/xarray-spatial
6
6
  Author: Xarray-Spatial Developers
@@ -349,7 +349,7 @@ Built-in Numba JIT and CUDA projection kernels bypass pyproj for per-pixel coord
349
349
 
350
350
  | Name | Description | Source | NumPy xr.DataArray | Dask xr.DataArray | CuPy GPU xr.DataArray | Dask GPU xr.DataArray |
351
351
  |:----------:|:------------|:------:|:----------------------:|:--------------------:|:-------------------:|:------:|
352
- | [from_template](xrspatial/templates.py) | Empty study-area grid for a named region (CONUS, NYC, ...) or country code; `preserve='area'/'shape'` picks an EPSG projection by property | Custom | ✅ | 🔼 | 🔼 | 🔼 |
352
+ | [from_template](xrspatial/templates.py) | Empty study-area grid for a named region (CONUS, NYC, ...), a world city (London, Tokyo, ... in its UTM zone) or country code; `preserve='area'/'shape'` picks an EPSG projection by property; `list_templates()` lists every accepted name | Custom | ✅ | 🔼 | 🔼 | 🔼 |
353
353
 
354
354
  -----------
355
355
 
@@ -385,35 +385,19 @@ Built-in Numba JIT and CUDA projection kernels bypass pyproj for per-pixel coord
385
385
 
386
386
  | Name | Description | Source | NumPy xr.DataArray | Dask xr.DataArray | CuPy GPU xr.DataArray | Dask GPU xr.DataArray |
387
387
  |:----------:|:------------|:------:|:----------------------:|:--------------------:|:-------------------:|:------:|
388
- | [Flow Direction (D8)](xrspatial/hydro/flow_direction_d8.py) | Computes D8 flow direction from each cell toward the steepest downhill neighbor | O'Callaghan & Mark 1984 | ✅ | 🔼 | 🔼 | 🔼 |
389
- | [Flow Direction (Dinf)](xrspatial/hydro/flow_direction_dinf.py) | Computes D-infinity flow direction as a continuous angle toward the steepest downslope facet | Tarboton 1997 | ✅ | 🔼 | 🔼 | 🔼 |
390
- | [Flow Direction (MFD)](xrspatial/hydro/flow_direction_mfd.py) | Partitions flow to all downslope neighbors with an adaptive exponent (Qin et al. 2007) | Qin et al. 2007 | | 🔼 | 🔼 | 🔼 |
391
- | [Flow Accumulation (D8)](xrspatial/hydro/flow_accumulation_d8.py) | Counts upstream cells draining through each cell in a D8 flow direction grid | Jenson & Domingue 1988 | | 🔼 | 🔼 | 🔼 |
392
- | [Flow Accumulation (Dinf)](xrspatial/hydro/flow_accumulation_dinf.py) | Accumulates upstream area by splitting flow proportionally between two neighbors (Tarboton 1997) | Tarboton 1997 | ✅ | 🔼 | 🔼 | 🔼 |
393
- | [Flow Accumulation (MFD)](xrspatial/hydro/flow_accumulation_mfd.py) | Accumulates upstream area through all MFD flow paths weighted by directional fractions | Qin et al. 2007 | ✅ | 🔼 | 🔼 | 🔼 |
394
- | [Flow Length (D8)](xrspatial/hydro/flow_length_d8.py) | Computes D8 flow path length from each cell to outlet (downstream) or from divide (upstream) | Standard (D8 tracing) | 🔼 | 🔼 | 🔼 | 🔼 |
395
- | [Flow Length (Dinf)](xrspatial/hydro/flow_length_dinf.py) | Proportion-weighted flow path length using D-inf angle decomposition (downstream or upstream) | Tarboton 1997 | 🔼 | 🔼 | 🔼 | 🔼 |
396
- | [Flow Length (MFD)](xrspatial/hydro/flow_length_mfd.py) | Proportion-weighted flow path length using MFD fractions (downstream or upstream) | Qin et al. 2007 | 🔼 | 🔼 | 🔼 | 🔼 |
397
- | [Fill (D8)](xrspatial/hydro/fill_d8.py) | Fills depressions in a DEM using Planchon-Darboux iterative flooding | Planchon & Darboux 2002 | ✅ | 🔼 | 🔼 | 🔼 |
398
- | [Sink (D8)](xrspatial/hydro/sink_d8.py) | Identifies and labels depression cells in a D8 flow direction grid | Standard (D8 tracing) | ✅ | 🔼 | 🔼 | 🔼 |
399
- | [Watershed (D8)](xrspatial/hydro/watershed_d8.py) | Labels each cell with the pour point it drains to via D8 flow direction | Standard (D8 tracing) | ✅ | 🔼 | 🔼 | 🔼 |
400
- | [Watershed (Dinf)](xrspatial/hydro/watershed_dinf.py) | Labels each cell with the pour point it drains to via D-infinity flow direction | Tarboton 1997 | ✅ | 🔼 | 🔼 | 🔼 |
401
- | [Watershed (MFD)](xrspatial/hydro/watershed_mfd.py) | Labels each cell with the pour point it drains to via MFD fractions | Qin et al. 2007 | ✅ | 🔼 | 🔼 | 🔼 |
402
- | [Basins](xrspatial/hydro/watershed_d8.py) | Delineates drainage basins by labeling each cell with its outlet ID | Standard (D8 tracing) | ✅ | 🔼 | 🔼 | 🔼 |
403
- | [Stream Order (D8)](xrspatial/hydro/stream_order_d8.py) | Assigns Strahler or Shreve stream order to cells in a drainage network | Strahler 1957, Shreve 1966 | ✅ | 🔼 | 🔼 | 🔼 |
404
- | [Stream Order (Dinf)](xrspatial/hydro/stream_order_dinf.py) | Strahler/Shreve stream ordering on D-infinity flow direction grids | Tarboton 1997 | ✅ | 🔼 | 🔼 | 🔼 |
405
- | [Stream Order (MFD)](xrspatial/hydro/stream_order_mfd.py) | Strahler/Shreve stream ordering on MFD fraction grids | Freeman 1991 | ✅ | 🔼 | 🔼 | 🔼 |
406
- | [Stream Link (D8)](xrspatial/hydro/stream_link_d8.py) | Assigns unique IDs to each stream segment between junctions | Standard | ✅ | 🔼 | 🔼 | 🔼 |
407
- | [Stream Link (Dinf)](xrspatial/hydro/stream_link_dinf.py) | Stream link segmentation on D-infinity flow direction grids | Tarboton 1997 | ✅ | 🔼 | 🔼 | 🔼 |
408
- | [Stream Link (MFD)](xrspatial/hydro/stream_link_mfd.py) | Stream link segmentation on MFD fraction grids | Freeman 1991 | ✅ | 🔼 | 🔼 | 🔼 |
409
- | [Snap Pour Point](xrspatial/hydro/snap_pour_point_d8.py) | Snaps pour points to the highest-accumulation cell within a search radius | Custom | ✅ | 🔼 | 🔼 | 🔼 |
410
- | [Flow Path (D8)](xrspatial/hydro/flow_path_d8.py) | Traces downstream flow paths from start points through a D8 direction grid | Standard (D8 tracing) | ✅ | 🔼 | 🔼 | 🔼 |
411
- | [Flow Path (Dinf)](xrspatial/hydro/flow_path_dinf.py) | Traces downstream flow paths using D-infinity dominant neighbor | Tarboton 1997 | 🔼 | 🔼 | 🔼 | 🔼 |
412
- | [Flow Path (MFD)](xrspatial/hydro/flow_path_mfd.py) | Traces downstream flow paths through MFD fraction-weighted neighbors | Qin et al. 2007 | 🔼 | 🔼 | 🔼 | 🔼 |
413
- | [HAND (D8)](xrspatial/hydro/hand_d8.py) | Computes Height Above Nearest Drainage by tracing D8 flow to the nearest stream cell | Nobre et al. 2011 | ✅ | 🔼 | 🔼 | 🔼 |
414
- | [HAND (Dinf)](xrspatial/hydro/hand_dinf.py) | Computes Height Above Nearest Drainage using D-infinity flow direction | Nobre et al. 2011 | ✅ | 🔼 | 🔼 | 🔼 |
415
- | [HAND (MFD)](xrspatial/hydro/hand_mfd.py) | Computes Height Above Nearest Drainage using MFD fractions | Nobre et al. 2011 | ✅ | 🔼 | 🔼 | 🔼 |
416
- | [TWI](xrspatial/hydro/twi_d8.py) | Topographic Wetness Index: ln(specific catchment area / tan(slope)) | Beven & Kirkby 1979 | ✅ | 🔼 | 🔼 | 🔼 |
388
+ | [Flow Direction](xrspatial/hydro/flow_direction_d8.py) | Direction of steepest descent out of each cell (D8 · Dinf · MFD via `routing=`) | O'Callaghan & Mark 1984; Tarboton 1997; Qin et al. 2007 | ✅ | 🔼 | 🔼 | 🔼 |
389
+ | [Flow Accumulation](xrspatial/hydro/flow_accumulation_d8.py) | Upstream cells or area draining through each cell (D8 · Dinf · MFD via `routing=`) | Jenson & Domingue 1988; Tarboton 1997; Qin et al. 2007 | ✅ | 🔼 | 🔼 | 🔼 |
390
+ | [Flow Length](xrspatial/hydro/flow_length_d8.py) | Flow path length to the outlet or from the divide (D8 · Dinf · MFD via `routing=`) | Tarboton 1997; Qin et al. 2007 | 🔼 | 🔼 | 🔼 | 🔼 |
391
+ | [Flow Path](xrspatial/hydro/flow_path_d8.py) | Traces downstream flow paths from start points (D8 · Dinf · MFD via `routing=`) | Tarboton 1997; Qin et al. 2007 | 🔼 | 🔼 | 🔼 | 🔼 |
392
+ | [Watershed](xrspatial/hydro/watershed_d8.py) | Labels each cell with the pour point it drains to (D8 · Dinf · MFD via `routing=`) | Tarboton 1997; Qin et al. 2007 | ✅ | 🔼 | 🔼 | 🔼 |
393
+ | [Stream Link](xrspatial/hydro/stream_link_d8.py) | Assigns unique IDs to stream segments above a threshold (D8 · Dinf · MFD via `routing=`) | Tarboton 1997; Freeman 1991 | ✅ | 🔼 | 🔼 | 🔼 |
394
+ | [Stream Order](xrspatial/hydro/stream_order_d8.py) | Strahler or Shreve stream ordering of the network (D8 · Dinf · MFD via `routing=`) | Strahler 1957, Shreve 1966 | | 🔼 | 🔼 | 🔼 |
395
+ | [HAND](xrspatial/hydro/hand_d8.py) | Height Above Nearest Drainage (D8 · Dinf · MFD via `routing=`) | Nobre et al. 2011 | | 🔼 | 🔼 | 🔼 |
396
+ | [Fill](xrspatial/hydro/fill_d8.py) | Fills depressions in a DEM using Planchon-Darboux iterative flooding (D8) | Planchon & Darboux 2002 | | 🔼 | 🔼 | 🔼 |
397
+ | [Sink](xrspatial/hydro/sink_d8.py) | Identifies and labels depression cells (D8) | Standard (D8 tracing) | ✅ | 🔼 | 🔼 | 🔼 |
398
+ | [Basin](xrspatial/hydro/basin_d8.py) | Labels each cell with the outlet of the basin it drains to (D8) | Standard (D8 tracing) | ✅ | 🔼 | 🔼 | 🔼 |
399
+ | [Snap Pour Point](xrspatial/hydro/snap_pour_point_d8.py) | Snaps pour points to the highest-accumulation cell within a search radius (D8) | Custom | ✅ | 🔼 | 🔼 | 🔼 |
400
+ | [TWI](xrspatial/hydro/twi_d8.py) | Topographic Wetness Index: ln(specific catchment area / tan(slope)) (D8) | Beven & Kirkby 1979 | ✅ | 🔼 | 🔼 | 🔼 |
417
401
 
418
402
  -----------
419
403
 
@@ -274,7 +274,7 @@ Built-in Numba JIT and CUDA projection kernels bypass pyproj for per-pixel coord
274
274
 
275
275
  | Name | Description | Source | NumPy xr.DataArray | Dask xr.DataArray | CuPy GPU xr.DataArray | Dask GPU xr.DataArray |
276
276
  |:----------:|:------------|:------:|:----------------------:|:--------------------:|:-------------------:|:------:|
277
- | [from_template](xrspatial/templates.py) | Empty study-area grid for a named region (CONUS, NYC, ...) or country code; `preserve='area'/'shape'` picks an EPSG projection by property | Custom | ✅ | 🔼 | 🔼 | 🔼 |
277
+ | [from_template](xrspatial/templates.py) | Empty study-area grid for a named region (CONUS, NYC, ...), a world city (London, Tokyo, ... in its UTM zone) or country code; `preserve='area'/'shape'` picks an EPSG projection by property; `list_templates()` lists every accepted name | Custom | ✅ | 🔼 | 🔼 | 🔼 |
278
278
 
279
279
  -----------
280
280
 
@@ -310,35 +310,19 @@ Built-in Numba JIT and CUDA projection kernels bypass pyproj for per-pixel coord
310
310
 
311
311
  | Name | Description | Source | NumPy xr.DataArray | Dask xr.DataArray | CuPy GPU xr.DataArray | Dask GPU xr.DataArray |
312
312
  |:----------:|:------------|:------:|:----------------------:|:--------------------:|:-------------------:|:------:|
313
- | [Flow Direction (D8)](xrspatial/hydro/flow_direction_d8.py) | Computes D8 flow direction from each cell toward the steepest downhill neighbor | O'Callaghan & Mark 1984 | ✅ | 🔼 | 🔼 | 🔼 |
314
- | [Flow Direction (Dinf)](xrspatial/hydro/flow_direction_dinf.py) | Computes D-infinity flow direction as a continuous angle toward the steepest downslope facet | Tarboton 1997 | ✅ | 🔼 | 🔼 | 🔼 |
315
- | [Flow Direction (MFD)](xrspatial/hydro/flow_direction_mfd.py) | Partitions flow to all downslope neighbors with an adaptive exponent (Qin et al. 2007) | Qin et al. 2007 | | 🔼 | 🔼 | 🔼 |
316
- | [Flow Accumulation (D8)](xrspatial/hydro/flow_accumulation_d8.py) | Counts upstream cells draining through each cell in a D8 flow direction grid | Jenson & Domingue 1988 | | 🔼 | 🔼 | 🔼 |
317
- | [Flow Accumulation (Dinf)](xrspatial/hydro/flow_accumulation_dinf.py) | Accumulates upstream area by splitting flow proportionally between two neighbors (Tarboton 1997) | Tarboton 1997 | ✅ | 🔼 | 🔼 | 🔼 |
318
- | [Flow Accumulation (MFD)](xrspatial/hydro/flow_accumulation_mfd.py) | Accumulates upstream area through all MFD flow paths weighted by directional fractions | Qin et al. 2007 | ✅ | 🔼 | 🔼 | 🔼 |
319
- | [Flow Length (D8)](xrspatial/hydro/flow_length_d8.py) | Computes D8 flow path length from each cell to outlet (downstream) or from divide (upstream) | Standard (D8 tracing) | 🔼 | 🔼 | 🔼 | 🔼 |
320
- | [Flow Length (Dinf)](xrspatial/hydro/flow_length_dinf.py) | Proportion-weighted flow path length using D-inf angle decomposition (downstream or upstream) | Tarboton 1997 | 🔼 | 🔼 | 🔼 | 🔼 |
321
- | [Flow Length (MFD)](xrspatial/hydro/flow_length_mfd.py) | Proportion-weighted flow path length using MFD fractions (downstream or upstream) | Qin et al. 2007 | 🔼 | 🔼 | 🔼 | 🔼 |
322
- | [Fill (D8)](xrspatial/hydro/fill_d8.py) | Fills depressions in a DEM using Planchon-Darboux iterative flooding | Planchon & Darboux 2002 | ✅ | 🔼 | 🔼 | 🔼 |
323
- | [Sink (D8)](xrspatial/hydro/sink_d8.py) | Identifies and labels depression cells in a D8 flow direction grid | Standard (D8 tracing) | ✅ | 🔼 | 🔼 | 🔼 |
324
- | [Watershed (D8)](xrspatial/hydro/watershed_d8.py) | Labels each cell with the pour point it drains to via D8 flow direction | Standard (D8 tracing) | ✅ | 🔼 | 🔼 | 🔼 |
325
- | [Watershed (Dinf)](xrspatial/hydro/watershed_dinf.py) | Labels each cell with the pour point it drains to via D-infinity flow direction | Tarboton 1997 | ✅ | 🔼 | 🔼 | 🔼 |
326
- | [Watershed (MFD)](xrspatial/hydro/watershed_mfd.py) | Labels each cell with the pour point it drains to via MFD fractions | Qin et al. 2007 | ✅ | 🔼 | 🔼 | 🔼 |
327
- | [Basins](xrspatial/hydro/watershed_d8.py) | Delineates drainage basins by labeling each cell with its outlet ID | Standard (D8 tracing) | ✅ | 🔼 | 🔼 | 🔼 |
328
- | [Stream Order (D8)](xrspatial/hydro/stream_order_d8.py) | Assigns Strahler or Shreve stream order to cells in a drainage network | Strahler 1957, Shreve 1966 | ✅ | 🔼 | 🔼 | 🔼 |
329
- | [Stream Order (Dinf)](xrspatial/hydro/stream_order_dinf.py) | Strahler/Shreve stream ordering on D-infinity flow direction grids | Tarboton 1997 | ✅ | 🔼 | 🔼 | 🔼 |
330
- | [Stream Order (MFD)](xrspatial/hydro/stream_order_mfd.py) | Strahler/Shreve stream ordering on MFD fraction grids | Freeman 1991 | ✅ | 🔼 | 🔼 | 🔼 |
331
- | [Stream Link (D8)](xrspatial/hydro/stream_link_d8.py) | Assigns unique IDs to each stream segment between junctions | Standard | ✅ | 🔼 | 🔼 | 🔼 |
332
- | [Stream Link (Dinf)](xrspatial/hydro/stream_link_dinf.py) | Stream link segmentation on D-infinity flow direction grids | Tarboton 1997 | ✅ | 🔼 | 🔼 | 🔼 |
333
- | [Stream Link (MFD)](xrspatial/hydro/stream_link_mfd.py) | Stream link segmentation on MFD fraction grids | Freeman 1991 | ✅ | 🔼 | 🔼 | 🔼 |
334
- | [Snap Pour Point](xrspatial/hydro/snap_pour_point_d8.py) | Snaps pour points to the highest-accumulation cell within a search radius | Custom | ✅ | 🔼 | 🔼 | 🔼 |
335
- | [Flow Path (D8)](xrspatial/hydro/flow_path_d8.py) | Traces downstream flow paths from start points through a D8 direction grid | Standard (D8 tracing) | ✅ | 🔼 | 🔼 | 🔼 |
336
- | [Flow Path (Dinf)](xrspatial/hydro/flow_path_dinf.py) | Traces downstream flow paths using D-infinity dominant neighbor | Tarboton 1997 | 🔼 | 🔼 | 🔼 | 🔼 |
337
- | [Flow Path (MFD)](xrspatial/hydro/flow_path_mfd.py) | Traces downstream flow paths through MFD fraction-weighted neighbors | Qin et al. 2007 | 🔼 | 🔼 | 🔼 | 🔼 |
338
- | [HAND (D8)](xrspatial/hydro/hand_d8.py) | Computes Height Above Nearest Drainage by tracing D8 flow to the nearest stream cell | Nobre et al. 2011 | ✅ | 🔼 | 🔼 | 🔼 |
339
- | [HAND (Dinf)](xrspatial/hydro/hand_dinf.py) | Computes Height Above Nearest Drainage using D-infinity flow direction | Nobre et al. 2011 | ✅ | 🔼 | 🔼 | 🔼 |
340
- | [HAND (MFD)](xrspatial/hydro/hand_mfd.py) | Computes Height Above Nearest Drainage using MFD fractions | Nobre et al. 2011 | ✅ | 🔼 | 🔼 | 🔼 |
341
- | [TWI](xrspatial/hydro/twi_d8.py) | Topographic Wetness Index: ln(specific catchment area / tan(slope)) | Beven & Kirkby 1979 | ✅ | 🔼 | 🔼 | 🔼 |
313
+ | [Flow Direction](xrspatial/hydro/flow_direction_d8.py) | Direction of steepest descent out of each cell (D8 · Dinf · MFD via `routing=`) | O'Callaghan & Mark 1984; Tarboton 1997; Qin et al. 2007 | ✅ | 🔼 | 🔼 | 🔼 |
314
+ | [Flow Accumulation](xrspatial/hydro/flow_accumulation_d8.py) | Upstream cells or area draining through each cell (D8 · Dinf · MFD via `routing=`) | Jenson & Domingue 1988; Tarboton 1997; Qin et al. 2007 | ✅ | 🔼 | 🔼 | 🔼 |
315
+ | [Flow Length](xrspatial/hydro/flow_length_d8.py) | Flow path length to the outlet or from the divide (D8 · Dinf · MFD via `routing=`) | Tarboton 1997; Qin et al. 2007 | 🔼 | 🔼 | 🔼 | 🔼 |
316
+ | [Flow Path](xrspatial/hydro/flow_path_d8.py) | Traces downstream flow paths from start points (D8 · Dinf · MFD via `routing=`) | Tarboton 1997; Qin et al. 2007 | 🔼 | 🔼 | 🔼 | 🔼 |
317
+ | [Watershed](xrspatial/hydro/watershed_d8.py) | Labels each cell with the pour point it drains to (D8 · Dinf · MFD via `routing=`) | Tarboton 1997; Qin et al. 2007 | ✅ | 🔼 | 🔼 | 🔼 |
318
+ | [Stream Link](xrspatial/hydro/stream_link_d8.py) | Assigns unique IDs to stream segments above a threshold (D8 · Dinf · MFD via `routing=`) | Tarboton 1997; Freeman 1991 | ✅ | 🔼 | 🔼 | 🔼 |
319
+ | [Stream Order](xrspatial/hydro/stream_order_d8.py) | Strahler or Shreve stream ordering of the network (D8 · Dinf · MFD via `routing=`) | Strahler 1957, Shreve 1966 | | 🔼 | 🔼 | 🔼 |
320
+ | [HAND](xrspatial/hydro/hand_d8.py) | Height Above Nearest Drainage (D8 · Dinf · MFD via `routing=`) | Nobre et al. 2011 | | 🔼 | 🔼 | 🔼 |
321
+ | [Fill](xrspatial/hydro/fill_d8.py) | Fills depressions in a DEM using Planchon-Darboux iterative flooding (D8) | Planchon & Darboux 2002 | | 🔼 | 🔼 | 🔼 |
322
+ | [Sink](xrspatial/hydro/sink_d8.py) | Identifies and labels depression cells (D8) | Standard (D8 tracing) | ✅ | 🔼 | 🔼 | 🔼 |
323
+ | [Basin](xrspatial/hydro/basin_d8.py) | Labels each cell with the outlet of the basin it drains to (D8) | Standard (D8 tracing) | ✅ | 🔼 | 🔼 | 🔼 |
324
+ | [Snap Pour Point](xrspatial/hydro/snap_pour_point_d8.py) | Snaps pour points to the highest-accumulation cell within a search radius (D8) | Custom | ✅ | 🔼 | 🔼 | 🔼 |
325
+ | [TWI](xrspatial/hydro/twi_d8.py) | Topographic Wetness Index: ln(specific catchment area / tan(slope)) (D8) | Beven & Kirkby 1979 | ✅ | 🔼 | 🔼 | 🔼 |
342
326
 
343
327
  -----------
344
328
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xarray-spatial
3
- Version: 0.10.12
3
+ Version: 0.10.13
4
4
  Summary: xarray-based spatial analysis tools
5
5
  Home-page: https://github.com/xarray-contrib/xarray-spatial
6
6
  Author: Xarray-Spatial Developers
@@ -349,7 +349,7 @@ Built-in Numba JIT and CUDA projection kernels bypass pyproj for per-pixel coord
349
349
 
350
350
  | Name | Description | Source | NumPy xr.DataArray | Dask xr.DataArray | CuPy GPU xr.DataArray | Dask GPU xr.DataArray |
351
351
  |:----------:|:------------|:------:|:----------------------:|:--------------------:|:-------------------:|:------:|
352
- | [from_template](xrspatial/templates.py) | Empty study-area grid for a named region (CONUS, NYC, ...) or country code; `preserve='area'/'shape'` picks an EPSG projection by property | Custom | ✅ | 🔼 | 🔼 | 🔼 |
352
+ | [from_template](xrspatial/templates.py) | Empty study-area grid for a named region (CONUS, NYC, ...), a world city (London, Tokyo, ... in its UTM zone) or country code; `preserve='area'/'shape'` picks an EPSG projection by property; `list_templates()` lists every accepted name | Custom | ✅ | 🔼 | 🔼 | 🔼 |
353
353
 
354
354
  -----------
355
355
 
@@ -385,35 +385,19 @@ Built-in Numba JIT and CUDA projection kernels bypass pyproj for per-pixel coord
385
385
 
386
386
  | Name | Description | Source | NumPy xr.DataArray | Dask xr.DataArray | CuPy GPU xr.DataArray | Dask GPU xr.DataArray |
387
387
  |:----------:|:------------|:------:|:----------------------:|:--------------------:|:-------------------:|:------:|
388
- | [Flow Direction (D8)](xrspatial/hydro/flow_direction_d8.py) | Computes D8 flow direction from each cell toward the steepest downhill neighbor | O'Callaghan & Mark 1984 | ✅ | 🔼 | 🔼 | 🔼 |
389
- | [Flow Direction (Dinf)](xrspatial/hydro/flow_direction_dinf.py) | Computes D-infinity flow direction as a continuous angle toward the steepest downslope facet | Tarboton 1997 | ✅ | 🔼 | 🔼 | 🔼 |
390
- | [Flow Direction (MFD)](xrspatial/hydro/flow_direction_mfd.py) | Partitions flow to all downslope neighbors with an adaptive exponent (Qin et al. 2007) | Qin et al. 2007 | | 🔼 | 🔼 | 🔼 |
391
- | [Flow Accumulation (D8)](xrspatial/hydro/flow_accumulation_d8.py) | Counts upstream cells draining through each cell in a D8 flow direction grid | Jenson & Domingue 1988 | | 🔼 | 🔼 | 🔼 |
392
- | [Flow Accumulation (Dinf)](xrspatial/hydro/flow_accumulation_dinf.py) | Accumulates upstream area by splitting flow proportionally between two neighbors (Tarboton 1997) | Tarboton 1997 | ✅ | 🔼 | 🔼 | 🔼 |
393
- | [Flow Accumulation (MFD)](xrspatial/hydro/flow_accumulation_mfd.py) | Accumulates upstream area through all MFD flow paths weighted by directional fractions | Qin et al. 2007 | ✅ | 🔼 | 🔼 | 🔼 |
394
- | [Flow Length (D8)](xrspatial/hydro/flow_length_d8.py) | Computes D8 flow path length from each cell to outlet (downstream) or from divide (upstream) | Standard (D8 tracing) | 🔼 | 🔼 | 🔼 | 🔼 |
395
- | [Flow Length (Dinf)](xrspatial/hydro/flow_length_dinf.py) | Proportion-weighted flow path length using D-inf angle decomposition (downstream or upstream) | Tarboton 1997 | 🔼 | 🔼 | 🔼 | 🔼 |
396
- | [Flow Length (MFD)](xrspatial/hydro/flow_length_mfd.py) | Proportion-weighted flow path length using MFD fractions (downstream or upstream) | Qin et al. 2007 | 🔼 | 🔼 | 🔼 | 🔼 |
397
- | [Fill (D8)](xrspatial/hydro/fill_d8.py) | Fills depressions in a DEM using Planchon-Darboux iterative flooding | Planchon & Darboux 2002 | ✅ | 🔼 | 🔼 | 🔼 |
398
- | [Sink (D8)](xrspatial/hydro/sink_d8.py) | Identifies and labels depression cells in a D8 flow direction grid | Standard (D8 tracing) | ✅ | 🔼 | 🔼 | 🔼 |
399
- | [Watershed (D8)](xrspatial/hydro/watershed_d8.py) | Labels each cell with the pour point it drains to via D8 flow direction | Standard (D8 tracing) | ✅ | 🔼 | 🔼 | 🔼 |
400
- | [Watershed (Dinf)](xrspatial/hydro/watershed_dinf.py) | Labels each cell with the pour point it drains to via D-infinity flow direction | Tarboton 1997 | ✅ | 🔼 | 🔼 | 🔼 |
401
- | [Watershed (MFD)](xrspatial/hydro/watershed_mfd.py) | Labels each cell with the pour point it drains to via MFD fractions | Qin et al. 2007 | ✅ | 🔼 | 🔼 | 🔼 |
402
- | [Basins](xrspatial/hydro/watershed_d8.py) | Delineates drainage basins by labeling each cell with its outlet ID | Standard (D8 tracing) | ✅ | 🔼 | 🔼 | 🔼 |
403
- | [Stream Order (D8)](xrspatial/hydro/stream_order_d8.py) | Assigns Strahler or Shreve stream order to cells in a drainage network | Strahler 1957, Shreve 1966 | ✅ | 🔼 | 🔼 | 🔼 |
404
- | [Stream Order (Dinf)](xrspatial/hydro/stream_order_dinf.py) | Strahler/Shreve stream ordering on D-infinity flow direction grids | Tarboton 1997 | ✅ | 🔼 | 🔼 | 🔼 |
405
- | [Stream Order (MFD)](xrspatial/hydro/stream_order_mfd.py) | Strahler/Shreve stream ordering on MFD fraction grids | Freeman 1991 | ✅ | 🔼 | 🔼 | 🔼 |
406
- | [Stream Link (D8)](xrspatial/hydro/stream_link_d8.py) | Assigns unique IDs to each stream segment between junctions | Standard | ✅ | 🔼 | 🔼 | 🔼 |
407
- | [Stream Link (Dinf)](xrspatial/hydro/stream_link_dinf.py) | Stream link segmentation on D-infinity flow direction grids | Tarboton 1997 | ✅ | 🔼 | 🔼 | 🔼 |
408
- | [Stream Link (MFD)](xrspatial/hydro/stream_link_mfd.py) | Stream link segmentation on MFD fraction grids | Freeman 1991 | ✅ | 🔼 | 🔼 | 🔼 |
409
- | [Snap Pour Point](xrspatial/hydro/snap_pour_point_d8.py) | Snaps pour points to the highest-accumulation cell within a search radius | Custom | ✅ | 🔼 | 🔼 | 🔼 |
410
- | [Flow Path (D8)](xrspatial/hydro/flow_path_d8.py) | Traces downstream flow paths from start points through a D8 direction grid | Standard (D8 tracing) | ✅ | 🔼 | 🔼 | 🔼 |
411
- | [Flow Path (Dinf)](xrspatial/hydro/flow_path_dinf.py) | Traces downstream flow paths using D-infinity dominant neighbor | Tarboton 1997 | 🔼 | 🔼 | 🔼 | 🔼 |
412
- | [Flow Path (MFD)](xrspatial/hydro/flow_path_mfd.py) | Traces downstream flow paths through MFD fraction-weighted neighbors | Qin et al. 2007 | 🔼 | 🔼 | 🔼 | 🔼 |
413
- | [HAND (D8)](xrspatial/hydro/hand_d8.py) | Computes Height Above Nearest Drainage by tracing D8 flow to the nearest stream cell | Nobre et al. 2011 | ✅ | 🔼 | 🔼 | 🔼 |
414
- | [HAND (Dinf)](xrspatial/hydro/hand_dinf.py) | Computes Height Above Nearest Drainage using D-infinity flow direction | Nobre et al. 2011 | ✅ | 🔼 | 🔼 | 🔼 |
415
- | [HAND (MFD)](xrspatial/hydro/hand_mfd.py) | Computes Height Above Nearest Drainage using MFD fractions | Nobre et al. 2011 | ✅ | 🔼 | 🔼 | 🔼 |
416
- | [TWI](xrspatial/hydro/twi_d8.py) | Topographic Wetness Index: ln(specific catchment area / tan(slope)) | Beven & Kirkby 1979 | ✅ | 🔼 | 🔼 | 🔼 |
388
+ | [Flow Direction](xrspatial/hydro/flow_direction_d8.py) | Direction of steepest descent out of each cell (D8 · Dinf · MFD via `routing=`) | O'Callaghan & Mark 1984; Tarboton 1997; Qin et al. 2007 | ✅ | 🔼 | 🔼 | 🔼 |
389
+ | [Flow Accumulation](xrspatial/hydro/flow_accumulation_d8.py) | Upstream cells or area draining through each cell (D8 · Dinf · MFD via `routing=`) | Jenson & Domingue 1988; Tarboton 1997; Qin et al. 2007 | ✅ | 🔼 | 🔼 | 🔼 |
390
+ | [Flow Length](xrspatial/hydro/flow_length_d8.py) | Flow path length to the outlet or from the divide (D8 · Dinf · MFD via `routing=`) | Tarboton 1997; Qin et al. 2007 | 🔼 | 🔼 | 🔼 | 🔼 |
391
+ | [Flow Path](xrspatial/hydro/flow_path_d8.py) | Traces downstream flow paths from start points (D8 · Dinf · MFD via `routing=`) | Tarboton 1997; Qin et al. 2007 | 🔼 | 🔼 | 🔼 | 🔼 |
392
+ | [Watershed](xrspatial/hydro/watershed_d8.py) | Labels each cell with the pour point it drains to (D8 · Dinf · MFD via `routing=`) | Tarboton 1997; Qin et al. 2007 | ✅ | 🔼 | 🔼 | 🔼 |
393
+ | [Stream Link](xrspatial/hydro/stream_link_d8.py) | Assigns unique IDs to stream segments above a threshold (D8 · Dinf · MFD via `routing=`) | Tarboton 1997; Freeman 1991 | ✅ | 🔼 | 🔼 | 🔼 |
394
+ | [Stream Order](xrspatial/hydro/stream_order_d8.py) | Strahler or Shreve stream ordering of the network (D8 · Dinf · MFD via `routing=`) | Strahler 1957, Shreve 1966 | | 🔼 | 🔼 | 🔼 |
395
+ | [HAND](xrspatial/hydro/hand_d8.py) | Height Above Nearest Drainage (D8 · Dinf · MFD via `routing=`) | Nobre et al. 2011 | | 🔼 | 🔼 | 🔼 |
396
+ | [Fill](xrspatial/hydro/fill_d8.py) | Fills depressions in a DEM using Planchon-Darboux iterative flooding (D8) | Planchon & Darboux 2002 | | 🔼 | 🔼 | 🔼 |
397
+ | [Sink](xrspatial/hydro/sink_d8.py) | Identifies and labels depression cells (D8) | Standard (D8 tracing) | ✅ | 🔼 | 🔼 | 🔼 |
398
+ | [Basin](xrspatial/hydro/basin_d8.py) | Labels each cell with the outlet of the basin it drains to (D8) | Standard (D8 tracing) | ✅ | 🔼 | 🔼 | 🔼 |
399
+ | [Snap Pour Point](xrspatial/hydro/snap_pour_point_d8.py) | Snaps pour points to the highest-accumulation cell within a search radius (D8) | Custom | ✅ | 🔼 | 🔼 | 🔼 |
400
+ | [TWI](xrspatial/hydro/twi_d8.py) | Topographic Wetness Index: ln(specific catchment area / tan(slope)) (D8) | Beven & Kirkby 1979 | ✅ | 🔼 | 🔼 | 🔼 |
417
401
 
418
402
  -----------
419
403