xarray-spatial 0.9.3__tar.gz → 0.9.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (297) hide show
  1. xarray_spatial-0.9.5/.claude/commands/sweep-performance.md +494 -0
  2. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/CHANGELOG.md +36 -0
  3. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/PKG-INFO +8 -1
  4. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/README.md +7 -0
  5. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xarray_spatial.egg-info/PKG-INFO +8 -1
  6. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xarray_spatial.egg-info/SOURCES.txt +7 -0
  7. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/_version.py +3 -3
  8. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/aspect.py +12 -4
  9. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/balanced_allocation.py +34 -6
  10. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/bump.py +3 -3
  11. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/classify.py +4 -4
  12. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/convolution.py +15 -7
  13. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/cost_distance.py +33 -6
  14. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/dasymetric.py +16 -0
  15. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/diffusion.py +90 -28
  16. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/erosion.py +22 -0
  17. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/flood.py +4 -4
  18. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/focal.py +30 -23
  19. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/__init__.py +418 -37
  20. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/_compression.py +2 -1
  21. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/_geotags.py +43 -1
  22. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/_vrt.py +2 -0
  23. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/_writer.py +405 -13
  24. xarray_spatial-0.9.5/xrspatial/geotiff/tests/test_accuracy_1081.py +292 -0
  25. xarray_spatial-0.9.5/xrspatial/geotiff/tests/test_compression_level.py +171 -0
  26. xarray_spatial-0.9.5/xrspatial/geotiff/tests/test_dtype_read.py +117 -0
  27. xarray_spatial-0.9.5/xrspatial/geotiff/tests/test_streaming_write.py +263 -0
  28. xarray_spatial-0.9.5/xrspatial/geotiff/tests/test_vrt_write.py +111 -0
  29. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/glcm.py +15 -0
  30. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/multispectral.py +5 -7
  31. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/normalize.py +22 -33
  32. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/__init__.py +13 -0
  33. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/_interpolate.py +26 -32
  34. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/_projections.py +5 -10
  35. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/_projections_cuda.py +16 -7
  36. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/surface_distance.py +14 -0
  37. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_balanced_allocation.py +32 -0
  38. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_bump.py +43 -0
  39. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_classify.py +33 -0
  40. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_flood.py +26 -0
  41. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_focal.py +158 -0
  42. xarray_spatial-0.9.5/xrspatial/tests/test_glcm_metric_order.py +162 -0
  43. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_multispectral.py +56 -12
  44. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_reproject.py +126 -0
  45. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_viewshed.py +55 -0
  46. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_zonal.py +99 -20
  47. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/viewshed.py +8 -3
  48. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/zonal.py +128 -36
  49. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.claude/commands/backend-parity.md +0 -0
  50. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.claude/commands/bench.md +0 -0
  51. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.claude/commands/dask-notebook.md +0 -0
  52. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.claude/commands/efficiency-audit.md +0 -0
  53. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.claude/commands/new-issues.md +0 -0
  54. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.claude/commands/release-major.md +0 -0
  55. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.claude/commands/release-minor.md +0 -0
  56. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.claude/commands/release-patch.md +0 -0
  57. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.claude/commands/review-pr.md +0 -0
  58. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.claude/commands/rockout.md +0 -0
  59. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.claude/commands/user-guide-notebook.md +0 -0
  60. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.claude/commands/validate.md +0 -0
  61. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.gitattributes +0 -0
  62. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  63. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.github/ISSUE_TEMPLATE/feature-proposal.md +0 -0
  64. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.github/labeler.yml +0 -0
  65. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.github/pull_request_template.md +0 -0
  66. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.github/workflows/benchmarks.yml +0 -0
  67. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.github/workflows/labeler.yml +0 -0
  68. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.github/workflows/pypi-publish.yml +0 -0
  69. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.github/workflows/test.yml +0 -0
  70. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.gitignore +0 -0
  71. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/.readthedocs.yml +0 -0
  72. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/CODE_OF_CONDUCT.md +0 -0
  73. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/CONTRIBUTING.md +0 -0
  74. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/Citation-styles.md +0 -0
  75. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/LICENSE.txt +0 -0
  76. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/MANIFEST.in +0 -0
  77. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/RELEASE.md +0 -0
  78. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/codecov.yml +0 -0
  79. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/pyproject.toml +0 -0
  80. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/setup.cfg +0 -0
  81. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/setup.py +0 -0
  82. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xarray_spatial.egg-info/dependency_links.txt +0 -0
  83. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xarray_spatial.egg-info/entry_points.txt +0 -0
  84. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xarray_spatial.egg-info/not-zip-safe +0 -0
  85. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xarray_spatial.egg-info/requires.txt +0 -0
  86. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xarray_spatial.egg-info/top_level.txt +0 -0
  87. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/__init__.py +0 -0
  88. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/__main__.py +0 -0
  89. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/accessor.py +0 -0
  90. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/analytics.py +0 -0
  91. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/bilateral.py +0 -0
  92. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/contour.py +0 -0
  93. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/corridor.py +0 -0
  94. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/curvature.py +0 -0
  95. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/dataset_support.py +0 -0
  96. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/datasets/__init__.py +0 -0
  97. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/datasets/sentinel-2/blue_band.nc +0 -0
  98. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/datasets/sentinel-2/green_band.nc +0 -0
  99. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/datasets/sentinel-2/nir_band.nc +0 -0
  100. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/datasets/sentinel-2/red_band.nc +0 -0
  101. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/datasets/sentinel-2/swir1_band.nc +0 -0
  102. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/datasets/sentinel-2/swir2_band.nc +0 -0
  103. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/diagnostics.py +0 -0
  104. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/edge_detection.py +0 -0
  105. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/emerging_hotspots.py +0 -0
  106. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/experimental/__init__.py +0 -0
  107. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/experimental/min_observable_height.py +0 -0
  108. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/fire.py +0 -0
  109. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geodesic.py +0 -0
  110. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/_dtypes.py +0 -0
  111. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/_gpu_decode.py +0 -0
  112. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/_header.py +0 -0
  113. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/_reader.py +0 -0
  114. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/tests/__init__.py +0 -0
  115. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/tests/bench_vs_rioxarray.py +0 -0
  116. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/tests/conftest.py +0 -0
  117. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/tests/test_accessor_io.py +0 -0
  118. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/tests/test_cog.py +0 -0
  119. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/tests/test_compression.py +0 -0
  120. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/tests/test_edge_cases.py +0 -0
  121. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/tests/test_features.py +0 -0
  122. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/tests/test_geotags.py +0 -0
  123. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/tests/test_header.py +0 -0
  124. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/tests/test_jpeg.py +0 -0
  125. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/tests/test_jpeg2000.py +0 -0
  126. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/tests/test_lerc.py +0 -0
  127. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/tests/test_lz4.py +0 -0
  128. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/tests/test_reader.py +0 -0
  129. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/geotiff/tests/test_writer.py +0 -0
  130. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/gpu_rtx/__init__.py +0 -0
  131. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/gpu_rtx/cuda_utils.py +0 -0
  132. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/gpu_rtx/hillshade.py +0 -0
  133. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/gpu_rtx/mesh_utils.py +0 -0
  134. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/gpu_rtx/viewshed.py +0 -0
  135. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hillshade.py +0 -0
  136. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/__init__.py +0 -0
  137. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/_boundary_store.py +0 -0
  138. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/basin_d8.py +0 -0
  139. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/fill_d8.py +0 -0
  140. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/flow_accumulation_d8.py +0 -0
  141. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/flow_accumulation_dinf.py +0 -0
  142. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/flow_accumulation_mfd.py +0 -0
  143. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/flow_direction_d8.py +0 -0
  144. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/flow_direction_dinf.py +0 -0
  145. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/flow_direction_mfd.py +0 -0
  146. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/flow_length_d8.py +0 -0
  147. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/flow_length_dinf.py +0 -0
  148. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/flow_length_mfd.py +0 -0
  149. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/flow_path_d8.py +0 -0
  150. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/flow_path_dinf.py +0 -0
  151. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/flow_path_mfd.py +0 -0
  152. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/hand_d8.py +0 -0
  153. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/hand_dinf.py +0 -0
  154. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/hand_mfd.py +0 -0
  155. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/sink_d8.py +0 -0
  156. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/snap_pour_point_d8.py +0 -0
  157. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/stream_link_d8.py +0 -0
  158. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/stream_link_dinf.py +0 -0
  159. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/stream_link_mfd.py +0 -0
  160. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/stream_order_d8.py +0 -0
  161. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/stream_order_dinf.py +0 -0
  162. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/stream_order_mfd.py +0 -0
  163. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/__init__.py +0 -0
  164. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/conftest.py +0 -0
  165. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_basin_d8.py +0 -0
  166. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_fill_d8.py +0 -0
  167. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_flow_accumulation_d8.py +0 -0
  168. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_flow_accumulation_dinf.py +0 -0
  169. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_flow_accumulation_mfd.py +0 -0
  170. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_flow_direction_d8.py +0 -0
  171. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_flow_direction_dinf.py +0 -0
  172. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_flow_direction_mfd.py +0 -0
  173. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_flow_length_d8.py +0 -0
  174. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_flow_length_dinf.py +0 -0
  175. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_flow_length_mfd.py +0 -0
  176. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_flow_path_d8.py +0 -0
  177. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_flow_path_dinf.py +0 -0
  178. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_flow_path_mfd.py +0 -0
  179. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_hand_d8.py +0 -0
  180. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_hand_dinf.py +0 -0
  181. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_hand_mfd.py +0 -0
  182. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_sink_d8.py +0 -0
  183. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_snap_pour_point_d8.py +0 -0
  184. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_stream_link_d8.py +0 -0
  185. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_stream_link_dinf.py +0 -0
  186. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_stream_link_mfd.py +0 -0
  187. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_stream_order_d8.py +0 -0
  188. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_stream_order_dinf.py +0 -0
  189. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_stream_order_mfd.py +0 -0
  190. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_twi_d8.py +0 -0
  191. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_watershed_d8.py +0 -0
  192. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_watershed_dinf.py +0 -0
  193. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/tests/test_watershed_mfd.py +0 -0
  194. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/twi_d8.py +0 -0
  195. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/watershed_d8.py +0 -0
  196. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/watershed_dinf.py +0 -0
  197. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/hydro/watershed_mfd.py +0 -0
  198. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/interpolate/__init__.py +0 -0
  199. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/interpolate/_idw.py +0 -0
  200. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/interpolate/_kriging.py +0 -0
  201. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/interpolate/_spline.py +0 -0
  202. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/interpolate/_validation.py +0 -0
  203. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/mahalanobis.py +0 -0
  204. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/mcda/__init__.py +0 -0
  205. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/mcda/combine.py +0 -0
  206. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/mcda/constrain.py +0 -0
  207. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/mcda/sensitivity.py +0 -0
  208. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/mcda/standardize.py +0 -0
  209. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/mcda/weights.py +0 -0
  210. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/morphology.py +0 -0
  211. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/pathfinding.py +0 -0
  212. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/perlin.py +0 -0
  213. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/polygonize.py +0 -0
  214. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/preview.py +0 -0
  215. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/proximity.py +0 -0
  216. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/rasterize.py +0 -0
  217. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/_crs_utils.py +0 -0
  218. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/_datum_grids.py +0 -0
  219. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/_grid.py +0 -0
  220. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/_itrf.py +0 -0
  221. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/_lite_crs.py +0 -0
  222. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/_merge.py +0 -0
  223. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/_transform.py +0 -0
  224. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/_vertical.py +0 -0
  225. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/grids/at_bev_AT_GIS_GRID.tif +0 -0
  226. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/grids/au_icsm_A66_National_13_09_01.tif +0 -0
  227. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/grids/be_ign_bd72lb72_etrs89lb08.tif +0 -0
  228. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/grids/ch_swisstopo_CHENyx06_ETRS.tif +0 -0
  229. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/grids/de_adv_BETA2007.tif +0 -0
  230. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/grids/es_ign_SPED2ETV2.tif +0 -0
  231. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/grids/nl_nsgi_rdcorr2018.tif +0 -0
  232. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/grids/pt_dgt_D73_ETRS89_geo.tif +0 -0
  233. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/grids/uk_os_OSTN15_NTv2_OSGBtoETRS.tif +0 -0
  234. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/grids/us_nga_egm96_15.tif +0 -0
  235. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/grids/us_noaa_alaska.tif +0 -0
  236. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/grids/us_noaa_conus.tif +0 -0
  237. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/grids/us_noaa_hawaii.tif +0 -0
  238. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/grids/us_noaa_nadcon5_nad27_nad83_1986_conus.tif +0 -0
  239. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/reproject/grids/us_noaa_prvi.tif +0 -0
  240. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/sky_view_factor.py +0 -0
  241. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/slope.py +0 -0
  242. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/terrain.py +0 -0
  243. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/terrain_metrics.py +0 -0
  244. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/__init__.py +0 -0
  245. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/bench_reproject_vs_rioxarray.py +0 -0
  246. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/conftest.py +0 -0
  247. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/general_checks.py +0 -0
  248. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_accessor.py +0 -0
  249. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_analytics.py +0 -0
  250. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_aspect.py +0 -0
  251. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_bilateral.py +0 -0
  252. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_contour.py +0 -0
  253. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_corridor.py +0 -0
  254. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_cost_distance.py +0 -0
  255. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_curvature.py +0 -0
  256. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_dask_cupy_gaps.py +0 -0
  257. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_dasymetric.py +0 -0
  258. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_dataset_support.py +0 -0
  259. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_datasets.py +0 -0
  260. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_diagnostics.py +0 -0
  261. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_diffusion.py +0 -0
  262. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_edge_detection.py +0 -0
  263. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_emerging_hotspots.py +0 -0
  264. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_erosion.py +0 -0
  265. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_fire.py +0 -0
  266. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_fused_overlap.py +0 -0
  267. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_geodesic_aspect.py +0 -0
  268. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_geodesic_slope.py +0 -0
  269. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_glcm.py +0 -0
  270. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_hillshade.py +0 -0
  271. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_interpolation.py +0 -0
  272. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_lite_crs.py +0 -0
  273. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_mahalanobis.py +0 -0
  274. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_mcda.py +0 -0
  275. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_min_observable_height.py +0 -0
  276. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_morphology.py +0 -0
  277. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_morphology_derived.py +0 -0
  278. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_multi_overlap.py +0 -0
  279. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_normalize.py +0 -0
  280. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_northness_eastness.py +0 -0
  281. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_pathfinding.py +0 -0
  282. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_perlin.py +0 -0
  283. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_polygonize.py +0 -0
  284. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_preview.py +0 -0
  285. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_proximity.py +0 -0
  286. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_rasterize.py +0 -0
  287. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_rasterize_accuracy.py +0 -0
  288. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_rechunk_no_shuffle.py +0 -0
  289. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_sky_view_factor.py +0 -0
  290. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_slope.py +0 -0
  291. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_surface_distance.py +0 -0
  292. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_terrain.py +0 -0
  293. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_terrain_metrics.py +0 -0
  294. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_utils.py +0 -0
  295. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/tests/test_validation.py +0 -0
  296. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/utils.py +0 -0
  297. {xarray_spatial-0.9.3 → xarray_spatial-0.9.5}/xrspatial/worley.py +0 -0
@@ -0,0 +1,494 @@
1
+ # Performance Sweep: Parallel Triage and Fix Workflow
2
+
3
+ Audit xrspatial modules for performance bottlenecks, OOM risk under 30TB dask
4
+ workloads, and backend-specific anti-patterns. Dispatches parallel subagents
5
+ for fast triage, then generates a ralph-loop to benchmark and fix HIGH-severity
6
+ issues.
7
+
8
+ Optional arguments: $ARGUMENTS
9
+ (e.g. `--top 5`, `--exclude slope,aspect`, `--only-io`, `--reset-state`)
10
+
11
+ ---
12
+
13
+ ## Step 0 -- Determine mode and parse arguments
14
+
15
+ Parse $ARGUMENTS for these flags (multiple may combine):
16
+
17
+ | Flag | Effect |
18
+ |------|--------|
19
+ | `--top N` | Limit Phase 1 to the top N scored modules (default: all) |
20
+ | `--exclude mod1,mod2` | Remove named modules from scope |
21
+ | `--only-terrain` | Restrict to: slope, aspect, curvature, terrain, terrain_metrics, hillshade, sky_view_factor |
22
+ | `--only-focal` | Restrict to: focal, convolution, morphology, bilateral, edge_detection, glcm |
23
+ | `--only-hydro` | Restrict to: flood, cost_distance, geodesic, surface_distance, viewshed, erosion, diffusion |
24
+ | `--only-io` | Restrict to: geotiff, reproject, rasterize, polygonize |
25
+ | `--reset-state` | Delete `.claude/performance-sweep-state.json` and treat all modules as never-inspected |
26
+ | `--skip-phase1` | Skip triage; reuse last state file; go straight to ralph-loop generation for unresolved HIGH items |
27
+ | `--report-only` | Run Phase 1 triage but do not generate a ralph-loop command |
28
+ | `--size small` | Phase 2 benchmarks use 128x128 arrays |
29
+ | `--size large` | Phase 2 benchmarks use 2048x2048 arrays |
30
+ | `--high-only` | Only report HIGH severity findings in the triage output |
31
+
32
+ If `--skip-phase1` is set, jump to Step 6 (ralph-loop generation).
33
+ Otherwise proceed to Step 1.
34
+
35
+ ## Step 1 -- Discover modules in scope
36
+
37
+ Enumerate all candidate modules. For each, record its file path(s):
38
+
39
+ **Single-file modules:** Every `.py` file directly under `xrspatial/`, excluding
40
+ `__init__.py`, `_version.py`, `__main__.py`, `utils.py`, `accessor.py`,
41
+ `preview.py`, `dataset_support.py`, `diagnostics.py`, `analytics.py`.
42
+
43
+ **Subpackage modules:** The `geotiff/` and `reproject/` directories under
44
+ `xrspatial/`. Treat each subpackage as a single audit unit. List all `.py`
45
+ files within each (excluding `__init__.py`).
46
+
47
+ Apply `--only-*` and `--exclude` filters from Step 0 to narrow the list.
48
+
49
+ Store the filtered module list in memory (do NOT write intermediate files).
50
+
51
+ ## Step 2 -- Gather metadata and score each module
52
+
53
+ For every module in scope, collect:
54
+
55
+ | Field | How |
56
+ |-------|-----|
57
+ | **last_modified** | `git log -1 --format=%aI -- <path>` (for subpackages, use the most recent file) |
58
+ | **total_commits** | `git log --oneline -- <path> \| wc -l` |
59
+ | **loc** | `wc -l < <path>` (for subpackages, sum all files) |
60
+ | **has_dask_backend** | grep the file(s) for `_run_dask`, `map_overlap`, `map_blocks` |
61
+ | **has_cuda_backend** | grep the file(s) for `@cuda.jit`, `import cupy` |
62
+ | **is_io_module** | module is geotiff or reproject |
63
+ | **has_existing_bench** | a file matching the module name exists in `benchmarks/benchmarks/` |
64
+
65
+ ### Load inspection state
66
+
67
+ Read `.claude/performance-sweep-state.json`. If it does not exist, treat every
68
+ module as never-inspected. If `--reset-state` was set, delete the file first.
69
+
70
+ State file schema:
71
+
72
+ ```json
73
+ {
74
+ "last_triage": "ISO-DATE",
75
+ "modules": {
76
+ "slope": {
77
+ "last_inspected": "ISO-DATE",
78
+ "oom_verdict": "SAFE",
79
+ "bottleneck": "compute-bound",
80
+ "high_count": 0,
81
+ "issue": null
82
+ }
83
+ }
84
+ }
85
+ ```
86
+
87
+ ### Compute scores
88
+
89
+ ```
90
+ days_since_inspected = (today - last_perf_inspected).days # 9999 if never
91
+ days_since_modified = (today - last_modified).days
92
+
93
+ score = (days_since_inspected * 3)
94
+ + (loc * 0.1)
95
+ + (total_commits * 0.5)
96
+ + (has_dask_backend * 200)
97
+ + (has_cuda_backend * 150)
98
+ + (is_io_module * 300)
99
+ - (days_since_modified * 0.2)
100
+ - (has_existing_bench * 100)
101
+ ```
102
+
103
+ Sort modules by score descending. If `--top N` is set, keep only the top N.
104
+
105
+ ## Step 3 -- Dispatch parallel subagents for static triage
106
+
107
+ For each module in the scored list, dispatch a subagent using the Agent tool.
108
+ Launch ALL subagents in a single message (parallel dispatch). Each subagent
109
+ receives the prompt below, with `MODULE_NAME` and `MODULE_FILES` substituted.
110
+
111
+ **Subagent prompt template:**
112
+
113
+ ~~~
114
+ You are auditing the xrspatial module "MODULE_NAME" for performance issues.
115
+
116
+ Read these files: MODULE_FILES
117
+
118
+ Perform ALL of the following analyses and return your findings as a single
119
+ JSON object. Do NOT modify any files. This is read-only analysis.
120
+
121
+ ### 1. Dask Path Analysis
122
+
123
+ Trace every dask code path (_run_dask, _run_dask_cupy, or any function that
124
+ receives dask-backed DataArrays). Flag these patterns with severity:
125
+
126
+ - HIGH: `.values` on a dask-backed DataArray or CuPy array (premature materialization)
127
+ - HIGH: `.compute()` inside a loop (materializes full graph each iteration)
128
+ - HIGH: `np.array()` or `np.asarray()` wrapping a dask or CuPy array
129
+ - MEDIUM: `da.stack()` without a following `.rechunk()`
130
+ - MEDIUM: `map_overlap` with depth >= chunk_size / 4
131
+ - MEDIUM: Missing `boundary` argument in `map_overlap`
132
+ - MEDIUM: Same function called twice on same input without caching
133
+ - MEDIUM: Python `for` loop iterating over dask chunks (serializes the graph)
134
+
135
+ If the module has NO dask code path, note "no dask backend" and skip.
136
+
137
+ ### 2. 30TB / 16GB OOM Verdict
138
+
139
+ For each dask code path found in section 1:
140
+
141
+ **Part A — Static trace:** Follow the code end-to-end. Answer: does peak
142
+ memory scale with total array size, or with chunk size? If any operation
143
+ forces full materialization, the verdict is WILL OOM.
144
+
145
+ **Part B — Task graph simulation:** Write and run a Python script (in /tmp/
146
+ with a unique name including "MODULE_NAME") that:
147
+
148
+ ```python
149
+ import dask.array as da
150
+ import xarray as xr
151
+ import json, sys
152
+
153
+ arr = da.zeros((2560, 2560), chunks=(256, 256), dtype='float64')
154
+ raster = xr.DataArray(arr, dims=['y', 'x'])
155
+
156
+ # Add coords if the function needs them (geodesic, slope with CRS, etc.)
157
+ # raster = raster.assign_coords(x=np.linspace(-180, 180, 2560),
158
+ # y=np.linspace(-90, 90, 2560))
159
+
160
+ try:
161
+ result = MODULE_FUNCTION(raster, **DEFAULT_ARGS)
162
+ graph = result.__dask_graph__()
163
+ task_count = len(graph)
164
+ tasks_per_chunk = task_count / 100.0
165
+
166
+ # Check for fan-out: any task key that depends on more than 4 other tasks
167
+ deps = dict(graph)
168
+ max_fan_in = 0
169
+ for key, val in deps.items():
170
+ if hasattr(val, '__dask_graph__'):
171
+ sub = val.__dask_graph__()
172
+ max_fan_in = max(max_fan_in, len(sub))
173
+
174
+ print(json.dumps({
175
+ "success": True,
176
+ "task_count": task_count,
177
+ "tasks_per_chunk": round(tasks_per_chunk, 2),
178
+ "max_fan_in": max_fan_in,
179
+ "extrapolation_30tb": "~{} tasks at 57M chunks".format(
180
+ int(tasks_per_chunk * 57_000_000))
181
+ }))
182
+ except Exception as e:
183
+ print(json.dumps({"success": False, "error": str(e)}))
184
+ ```
185
+
186
+ Adapt the function call and imports for the specific module. Run the script
187
+ and capture its JSON output. If it errors, record the error and rely on
188
+ Part A alone.
189
+
190
+ **Verdict:** One of:
191
+ - `SAFE` — memory bounded by chunk size, graph scales linearly
192
+ - `RISKY` — bounded but tight (e.g. large overlap depth, 3D intermediates)
193
+ - `WILL OOM` — forces full materialization or unbounded memory growth
194
+
195
+ ### 3. GPU Transfer Analysis
196
+
197
+ Scan for CuPy/CUDA code paths. Flag:
198
+
199
+ - HIGH: `.data.get()` followed by CuPy operations (GPU-CPU-GPU round-trip)
200
+ - HIGH: `cupy.asarray()` inside a loop (repeated CPU-GPU transfers)
201
+ - MEDIUM: Mixing NumPy and CuPy ops in same function without clear reason
202
+ - MEDIUM: Register pressure — count float64 local variables in `@cuda.jit`
203
+ kernels; flag if >20
204
+ - MEDIUM: Thread blocks >16x16 on kernels with >20 float64 locals
205
+
206
+ If the module has NO GPU code path, note "no GPU backend" and skip.
207
+
208
+ ### 4. Memory Allocation Patterns
209
+
210
+ - MEDIUM: Unnecessary `.copy()` on arrays never mutated downstream
211
+ - MEDIUM: Large temporary arrays that could be fused into the kernel
212
+ - LOW: `np.zeros_like()` + fill loop where `np.empty()` would suffice
213
+
214
+ ### 5. Numba Anti-Patterns
215
+
216
+ - MEDIUM: Missing `@ngjit` on nested for-loops over `.data` arrays
217
+ - MEDIUM: `@jit` without `nopython=True` (object-mode fallback risk)
218
+ - LOW: Type instability — initializing with int then assigning float
219
+ - LOW: Column-major iteration on row-major arrays (inner loop should be last axis)
220
+
221
+ ### 6. Bottleneck Classification
222
+
223
+ Based on your analysis, classify the module as ONE of:
224
+ - `IO-bound` — dominated by disk reads/writes or serialization
225
+ - `memory-bound` — peak allocation is the limiting factor
226
+ - `compute-bound` — CPU/GPU time dominates, memory is fine
227
+ - `graph-bound` — dask task graph overhead dominates
228
+
229
+ ### Output Format
230
+
231
+ Return EXACTLY this JSON structure (no extra text before or after):
232
+
233
+ ```json
234
+ {
235
+ "module": "MODULE_NAME",
236
+ "files_read": ["list of files you read"],
237
+ "findings": [
238
+ {
239
+ "severity": "HIGH|MEDIUM|LOW",
240
+ "category": "dask_materialization|dask_chunking|gpu_transfer|register_pressure|memory_allocation|numba_antipattern",
241
+ "file": "filename.py",
242
+ "line": 123,
243
+ "description": "what the issue is",
244
+ "fix": "how to fix it",
245
+ "backends_affected": ["dask+numpy", "dask+cupy", "cupy", "numpy"]
246
+ }
247
+ ],
248
+ "oom_verdict": {
249
+ "dask_numpy": "SAFE|RISKY|WILL OOM",
250
+ "dask_cupy": "SAFE|RISKY|WILL OOM",
251
+ "reasoning": "one-sentence explanation",
252
+ "estimated_peak_per_chunk_mb": 0.5,
253
+ "task_count": 3721,
254
+ "tasks_per_chunk": 37.21,
255
+ "graph_simulation_ran": true
256
+ },
257
+ "bottleneck": "compute-bound|memory-bound|IO-bound|graph-bound",
258
+ "bottleneck_reasoning": "one-sentence explanation"
259
+ }
260
+ ```
261
+
262
+ IMPORTANT: Only flag patterns that are ACTUALLY present in the code. Do not
263
+ report hypothetical issues. False positives are worse than missed issues.
264
+ If a pattern like `.values` is used on a known-numpy-only code path, do not
265
+ flag it.
266
+ ~~~
267
+
268
+ Wait for all subagents to return before proceeding to Step 4.
269
+
270
+ ## Step 4 -- Merge results and print the triage report
271
+
272
+ Parse the JSON returned by each subagent. If a subagent returned malformed
273
+ output, record the module as "audit failed" with a note.
274
+
275
+ ### 4a. Print the Module Risk Ranking Table
276
+
277
+ Sort modules by score descending. Print:
278
+
279
+ ```
280
+ ## Performance Sweep — Static Triage Report
281
+
282
+ ### Module Risk Ranking
283
+ | Rank | Module | Score | OOM Verdict | Bottleneck | HIGH | MED | LOW |
284
+ |------|-----------------|--------|-----------------|---------------|------|-----|-----|
285
+ | 1 | geotiff | 31200 | WILL OOM (d+np) | IO-bound | 3 | 1 | 0 |
286
+ | 2 | viewshed | 30050 | RISKY (d+np) | memory-bound | 2 | 2 | 1 |
287
+ | ... | ... | ... | ... | ... | ... | ... | ... |
288
+ ```
289
+
290
+ If `--high-only` is set, only count HIGH findings and omit modules with zero HIGH.
291
+
292
+ ### 4b. Print the 30TB / 16GB Verdict Summary
293
+
294
+ Group modules by OOM verdict:
295
+
296
+ ```
297
+ ### 30TB on Disk / 16GB RAM — Out-of-Memory Analysis
298
+
299
+ #### WILL OOM (fix required)
300
+ - **module_name**: reasoning from subagent
301
+
302
+ #### RISKY (bounded but tight)
303
+ - **module_name**: reasoning from subagent
304
+
305
+ #### SAFE (memory bounded by chunk size)
306
+ - module_name, module_name, module_name, ...
307
+ ```
308
+
309
+ ### 4c. Print Detailed Findings
310
+
311
+ For each module that has findings, print a severity-grouped table:
312
+
313
+ ```
314
+ ### module_name (bottleneck: compute-bound, OOM: SAFE)
315
+
316
+ | # | Severity | File:Line | Category | Description | Fix |
317
+ |---|----------|----------------|-------------------------|------------------------------|-------------------------------|
318
+ | 1 | HIGH | slope.py:142 | dask_materialization | .values on dask input | Use .data or stay lazy |
319
+ | 2 | MEDIUM | slope.py:88 | dask_chunking | map_overlap depth too large | Reduce depth or warn users |
320
+ ```
321
+
322
+ ### 4d. Print Actionable Rockout Commands
323
+
324
+ For each HIGH-severity finding, print a ready-to-paste `/rockout` command:
325
+
326
+ ```
327
+ ### Ready-to-Run Fixes (HIGH severity only)
328
+
329
+ 1. **geotiff** — eager .values materialization (WILL OOM)
330
+ /rockout "Fix eager .values materialization in geotiff reader.
331
+ The dask read path at reader.py:87 calls .values which forces
332
+ the full array into memory. For 30TB inputs this will OOM on
333
+ a 16GB machine. Must stay lazy through the entire read path."
334
+
335
+ 2. **cost_distance** — iterative solver unbounded memory (WILL OOM)
336
+ /rockout "Fix cost_distance iterative solver to work within
337
+ bounded memory. Currently materializes the full distance matrix
338
+ each iteration. Must use chunked iteration for 30TB dask inputs."
339
+ ```
340
+
341
+ Construct each `/rockout` command from the finding's description and fix fields.
342
+ Include the OOM verdict and bottleneck classification in the prompt text so
343
+ rockout has full context.
344
+
345
+ ## Step 5 -- Update state file
346
+
347
+ Write `.claude/performance-sweep-state.json` with the triage results:
348
+
349
+ ```json
350
+ {
351
+ "last_triage": "<current ISO datetime>",
352
+ "modules": {
353
+ "<module_name>": {
354
+ "last_inspected": "<current ISO datetime>",
355
+ "oom_verdict": "<SAFE|RISKY|WILL OOM>",
356
+ "bottleneck": "<IO-bound|memory-bound|compute-bound|graph-bound>",
357
+ "high_count": "<number of HIGH findings>",
358
+ "issue": null
359
+ }
360
+ }
361
+ }
362
+ ```
363
+
364
+ If the file already exists, merge — update entries for modules that were
365
+ just audited, keep entries for modules not in this run's scope.
366
+
367
+ If `--report-only` is set, stop here. Do not proceed to Step 6.
368
+
369
+ ## Step 6 -- Generate the ralph-loop command
370
+
371
+ Collect all modules from Step 4 (or from the state file if `--skip-phase1`)
372
+ that have at least one HIGH-severity finding and no `issue` recorded in the
373
+ state file (i.e. not yet fixed).
374
+
375
+ Sort them by: WILL OOM first, then RISKY, then by HIGH count descending.
376
+
377
+ Determine the benchmark array size from arguments:
378
+ - `--size small` → 128x128
379
+ - `--size large` → 2048x2048
380
+ - default → 512x512
381
+
382
+ ### 6a. Print the ranked target list
383
+
384
+ ```
385
+ ### Phase 2 Targets (HIGH severity, unfixed)
386
+ | # | Module | HIGH Count | OOM Verdict | Bottleneck |
387
+ |---|---------------|------------|-------------|--------------|
388
+ | 1 | geotiff | 3 | WILL OOM | IO-bound |
389
+ | 2 | cost_distance | 1 | WILL OOM | memory-bound |
390
+ | 3 | viewshed | 2 | RISKY | memory-bound |
391
+ ```
392
+
393
+ If no modules qualify, print:
394
+ "No HIGH-severity findings to fix. Run `/sweep-performance` without
395
+ `--skip-phase1` to refresh the triage."
396
+ Then stop.
397
+
398
+ ### 6b. Print the ralph-loop command
399
+
400
+ Using the target list, generate and print:
401
+
402
+ ````
403
+ /ralph-loop "Performance sweep Phase 2: benchmark and fix HIGH-severity findings.
404
+
405
+ **Target modules in priority order:**
406
+ 1. <module> (<N> HIGH findings, <OOM verdict>) -- <one-line summary of worst finding>
407
+ 2. <module> ...
408
+ ...
409
+
410
+ **For each module, in order:**
411
+
412
+ 1. Write a benchmark script at /tmp/perf_sweep_bench_<module>.py that:
413
+ - Imports the module's public functions
414
+ - Creates a test array (<SIZE>x<SIZE>, float64)
415
+ - For EACH available backend (numpy, dask+numpy; cupy and dask+cupy only if available):
416
+ a. Wrap the array in the appropriate DataArray type
417
+ b. Measure wall time: timeit.repeat(number=1, repeat=3), take median
418
+ c. Measure Python memory: tracemalloc.start() / tracemalloc.get_traced_memory()[1] for peak
419
+ d. Measure process memory: resource.getrusage(RUSAGE_SELF).ru_maxrss before and after
420
+ e. For CuPy backends: cupy.get_default_memory_pool().used_bytes() before and after
421
+ - Print results as JSON to stdout
422
+
423
+ 2. Run the benchmark script and capture results.
424
+
425
+ 3. Confirm the HIGH finding from Phase 1:
426
+ - If the dask backend uses significantly more memory than expected for
427
+ the chunk size, or wall time shows a materialization stall: CONFIRMED.
428
+ - If the benchmark shows no anomaly: downgrade to MEDIUM in state file,
429
+ print 'False positive — skipping' and move to the next module.
430
+
431
+ 4. If confirmed: run /rockout to fix the issue end-to-end (issue, worktree,
432
+ implementation, tests, docs). Include the benchmark numbers in the
433
+ issue body for context.
434
+
435
+ 5. After rockout completes: rerun the same benchmark script. Print a
436
+ before/after comparison:
437
+ | Backend | Metric | Before | After | Ratio | Verdict |
438
+ |------------|-------------|--------|--------|-------|------------|
439
+ | numpy | wall_ms | 45.2 | 12.1 | 0.27x | IMPROVED |
440
+ | dask+numpy | peak_rss_mb | 892 | 34 | 0.04x | IMPROVED |
441
+ Thresholds: IMPROVED < 0.8x, REGRESSION > 1.2x, else UNCHANGED.
442
+
443
+ 6. Update .claude/performance-sweep-state.json with the issue number.
444
+
445
+ 7. Output <promise>ITERATION DONE</promise>
446
+
447
+ If all targets have been addressed or confirmed as false positives:
448
+ <promise>ALL PERFORMANCE ISSUES FIXED</promise>." --max-iterations <N+2> --completion-promise "ALL PERFORMANCE ISSUES FIXED"
449
+ ````
450
+
451
+ Set `--max-iterations` to the number of target modules plus 2 (buffer for
452
+ retries).
453
+
454
+ ### 6c. Print reminder text
455
+
456
+ ```
457
+ Phase 1 triage complete. To proceed with fixes:
458
+ Copy the ralph-loop command above and paste it.
459
+
460
+ Other options:
461
+ Fix one manually: copy any /rockout command from the report above
462
+ Rerun triage only: /sweep-performance --report-only
463
+ Skip Phase 1: /sweep-performance --skip-phase1 (reuses last triage)
464
+ Reset all tracking: /sweep-performance --reset-state
465
+ ```
466
+
467
+ ---
468
+
469
+ ## General Rules
470
+
471
+ - Phase 1 subagents do NOT modify any source, test, or benchmark files.
472
+ Read-only analysis only.
473
+ - Phase 2 ralph-loop modifies code only through `/rockout`.
474
+ - Temporary benchmark scripts and graph simulation scripts go in `/tmp/`
475
+ with unique names including the module name (e.g. `/tmp/perf_sweep_bench_slope.py`,
476
+ `/tmp/perf_sweep_graph_slope.py`). Clean them up after capturing results.
477
+ - Only flag patterns that are ACTUALLY present in the code. Do not report
478
+ hypothetical issues or patterns that "could" occur.
479
+ - Include the exact file path and line number for every finding so the user
480
+ can navigate directly to the issue.
481
+ - False positives are worse than missed issues. If you are not confident a
482
+ pattern is actually harmful in context (e.g. `.values` used intentionally
483
+ on a known-numpy array), do not flag it.
484
+ - The 30TB simulation constructs the dask task graph only; it NEVER calls
485
+ `.compute()`.
486
+ - State file (`.claude/performance-sweep-state.json`) is gitignored by
487
+ convention — do not add it to git.
488
+ - If $ARGUMENTS is empty, use defaults: audit all modules, benchmark at
489
+ 512x512, generate ralph-loop for HIGH items.
490
+ - For subpackage modules (geotiff, reproject), the subagent should read ALL
491
+ `.py` files in the subpackage directory, not just `__init__.py`.
492
+ - When generating `/rockout` commands, include the OOM verdict, bottleneck
493
+ classification, and affected backends in the prompt text so rockout has
494
+ full performance context.
@@ -2,6 +2,42 @@
2
2
  -----------
3
3
 
4
4
 
5
+ ### Version 0.9.5 - 2026-03-31
6
+
7
+ #### Bug fixes and improvements
8
+ - Add GPU memory guard to reproject dask+cupy path (#1131)
9
+ - Add memory guard to surface_distance geodesic dd_grid (#1129)
10
+ - Add memory guard to dasymetric validate_disaggregation (#1127)
11
+ - Replace boolean indexing with lazy reductions in normalize dask paths (#1125)
12
+ - Keep northness/eastness lazy on dask arrays (#1123)
13
+ - Add memory guard to erosion dask paths (#1121)
14
+ - Add memory guard to cost_distance iterative Dijkstra and da.block assembly (#1119)
15
+ - Fix diffusion dask OOM by passing scalar diffusivity directly to chunks (#1117)
16
+ - Fix balanced_allocation OOM with lazy source extraction and memory guard (#1115)
17
+ - Fix zonal dask memory guards and stats filtering (#1112)
18
+
19
+
20
+ ### Version 0.9.4 - 2026-03-30
21
+
22
+ #### New features
23
+ - Streaming TIFF write for dask inputs (#1084) (#1108)
24
+ - Add dtype, compression_level, and VRT output to geotiff I/O (#1083) (#1085)
25
+
26
+ #### Bug fixes and improvements
27
+ - Use float64 in Jenks natural breaks internals (#1100) (#1101)
28
+ - Fix multi-metric output mislabeling in glcm_texture (#1106) (#1107)
29
+ - Fix inverted decay and off-by-one in bump spread (#1102) (#1103)
30
+ - Propagate NaN from curve_number in curve_number_runoff (#1104) (#1105)
31
+ - Fix target_elev contaminating horizon in dask viewshed distance sweep (#1098) (#1099)
32
+ - Preserve float64 precision in convolve_2d (#1096) (#1097)
33
+ - Fix SAVI formula: (1+L) was in denominator instead of numerator (#1094) (#1095)
34
+ - Fix NaN handling in focal_stats CUDA kernels (#1092) (#1093)
35
+ - Fix three accuracy bugs in zonal stats dask backend (#1090) (#1091)
36
+ - Add longitude normalization to CUDA inverse projection kernels (#1089)
37
+ - Fix three accuracy bugs in reproject resampling kernels (#1087)
38
+ - Fix three accuracy bugs in open_geotiff/to_geotiff (#1081) (#1082)
39
+
40
+
5
41
  ### Version 0.9.3 - 2026-03-27
6
42
 
7
43
  #### New features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xarray-spatial
3
- Version: 0.9.3
3
+ Version: 0.9.5
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
@@ -231,6 +231,13 @@ to_geotiff(data, 'out.tif', gpu=True) # force GPU compress
231
231
  to_geotiff(data, 'ortho.tif', compression='jpeg') # JPEG for orthophotos
232
232
  write_vrt('mosaic.vrt', ['tile1.tif', 'tile2.tif']) # generate VRT
233
233
 
234
+ open_geotiff('dem.tif', dtype='float32') # half memory
235
+ open_geotiff('dem.tif', dtype='float32', chunks=512) # Dask + half memory
236
+ to_geotiff(data, 'out.tif', compression_level=1) # fast scratch write
237
+ to_geotiff(data, 'out.tif', compression_level=22) # max compression
238
+ to_geotiff(dask_da, 'out.tif') # stream Dask to single TIFF
239
+ to_geotiff(dask_da, 'mosaic.vrt') # stream Dask to VRT
240
+
234
241
  # Accessor methods
235
242
  da.xrs.to_geotiff('out.tif', compression='lzw') # write from DataArray
236
243
  ds.xrs.open_geotiff('large_dem.tif') # read windowed to Dataset extent
@@ -165,6 +165,13 @@ to_geotiff(data, 'out.tif', gpu=True) # force GPU compress
165
165
  to_geotiff(data, 'ortho.tif', compression='jpeg') # JPEG for orthophotos
166
166
  write_vrt('mosaic.vrt', ['tile1.tif', 'tile2.tif']) # generate VRT
167
167
 
168
+ open_geotiff('dem.tif', dtype='float32') # half memory
169
+ open_geotiff('dem.tif', dtype='float32', chunks=512) # Dask + half memory
170
+ to_geotiff(data, 'out.tif', compression_level=1) # fast scratch write
171
+ to_geotiff(data, 'out.tif', compression_level=22) # max compression
172
+ to_geotiff(dask_da, 'out.tif') # stream Dask to single TIFF
173
+ to_geotiff(dask_da, 'mosaic.vrt') # stream Dask to VRT
174
+
168
175
  # Accessor methods
169
176
  da.xrs.to_geotiff('out.tif', compression='lzw') # write from DataArray
170
177
  ds.xrs.open_geotiff('large_dem.tif') # read windowed to Dataset extent
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xarray-spatial
3
- Version: 0.9.3
3
+ Version: 0.9.5
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
@@ -231,6 +231,13 @@ to_geotiff(data, 'out.tif', gpu=True) # force GPU compress
231
231
  to_geotiff(data, 'ortho.tif', compression='jpeg') # JPEG for orthophotos
232
232
  write_vrt('mosaic.vrt', ['tile1.tif', 'tile2.tif']) # generate VRT
233
233
 
234
+ open_geotiff('dem.tif', dtype='float32') # half memory
235
+ open_geotiff('dem.tif', dtype='float32', chunks=512) # Dask + half memory
236
+ to_geotiff(data, 'out.tif', compression_level=1) # fast scratch write
237
+ to_geotiff(data, 'out.tif', compression_level=22) # max compression
238
+ to_geotiff(dask_da, 'out.tif') # stream Dask to single TIFF
239
+ to_geotiff(dask_da, 'mosaic.vrt') # stream Dask to VRT
240
+
234
241
  # Accessor methods
235
242
  da.xrs.to_geotiff('out.tif', compression='lzw') # write from DataArray
236
243
  ds.xrs.open_geotiff('large_dem.tif') # read windowed to Dataset extent
@@ -23,6 +23,7 @@ setup.py
23
23
  .claude/commands/release-patch.md
24
24
  .claude/commands/review-pr.md
25
25
  .claude/commands/rockout.md
26
+ .claude/commands/sweep-performance.md
26
27
  .claude/commands/user-guide-notebook.md
27
28
  .claude/commands/validate.md
28
29
  .github/labeler.yml
@@ -109,8 +110,11 @@ xrspatial/geotiff/tests/__init__.py
109
110
  xrspatial/geotiff/tests/bench_vs_rioxarray.py
110
111
  xrspatial/geotiff/tests/conftest.py
111
112
  xrspatial/geotiff/tests/test_accessor_io.py
113
+ xrspatial/geotiff/tests/test_accuracy_1081.py
112
114
  xrspatial/geotiff/tests/test_cog.py
113
115
  xrspatial/geotiff/tests/test_compression.py
116
+ xrspatial/geotiff/tests/test_compression_level.py
117
+ xrspatial/geotiff/tests/test_dtype_read.py
114
118
  xrspatial/geotiff/tests/test_edge_cases.py
115
119
  xrspatial/geotiff/tests/test_features.py
116
120
  xrspatial/geotiff/tests/test_geotags.py
@@ -120,6 +124,8 @@ xrspatial/geotiff/tests/test_jpeg2000.py
120
124
  xrspatial/geotiff/tests/test_lerc.py
121
125
  xrspatial/geotiff/tests/test_lz4.py
122
126
  xrspatial/geotiff/tests/test_reader.py
127
+ xrspatial/geotiff/tests/test_streaming_write.py
128
+ xrspatial/geotiff/tests/test_vrt_write.py
123
129
  xrspatial/geotiff/tests/test_writer.py
124
130
  xrspatial/gpu_rtx/__init__.py
125
131
  xrspatial/gpu_rtx/cuda_utils.py
@@ -257,6 +263,7 @@ xrspatial/tests/test_fused_overlap.py
257
263
  xrspatial/tests/test_geodesic_aspect.py
258
264
  xrspatial/tests/test_geodesic_slope.py
259
265
  xrspatial/tests/test_glcm.py
266
+ xrspatial/tests/test_glcm_metric_order.py
260
267
  xrspatial/tests/test_hillshade.py
261
268
  xrspatial/tests/test_interpolation.py
262
269
  xrspatial/tests/test_lite_crs.py
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.9.3'
22
- __version_tuple__ = version_tuple = (0, 9, 3)
21
+ __version__ = version = '0.9.5'
22
+ __version_tuple__ = version_tuple = (0, 9, 5)
23
23
 
24
- __commit_id__ = commit_id = 'gb6a15e5b6'
24
+ __commit_id__ = commit_id = 'g45c714e12'
@@ -506,8 +506,12 @@ def northness(agg: xr.DataArray,
506
506
  asp = aspect(agg, name='_aspect', method=method, z_unit=z_unit,
507
507
  boundary=boundary)
508
508
  asp_data = asp.data
509
- trig = np.cos(np.deg2rad(asp_data))
510
- out = np.where(asp_data == -1, np.nan, trig)
509
+ if da is not None and isinstance(asp_data, da.Array):
510
+ trig = da.cos(da.deg2rad(asp_data))
511
+ out = da.where(asp_data == -1, np.nan, trig)
512
+ else:
513
+ trig = np.cos(np.deg2rad(asp_data))
514
+ out = np.where(asp_data == -1, np.nan, trig)
511
515
  return xr.DataArray(out,
512
516
  name=name,
513
517
  coords=agg.coords,
@@ -582,8 +586,12 @@ def eastness(agg: xr.DataArray,
582
586
  asp = aspect(agg, name='_aspect', method=method, z_unit=z_unit,
583
587
  boundary=boundary)
584
588
  asp_data = asp.data
585
- trig = np.sin(np.deg2rad(asp_data))
586
- out = np.where(asp_data == -1, np.nan, trig)
589
+ if da is not None and isinstance(asp_data, da.Array):
590
+ trig = da.sin(da.deg2rad(asp_data))
591
+ out = da.where(asp_data == -1, np.nan, trig)
592
+ else:
593
+ trig = np.sin(np.deg2rad(asp_data))
594
+ out = np.where(asp_data == -1, np.nan, trig)
587
595
  return xr.DataArray(out,
588
596
  name=name,
589
597
  coords=agg.coords,