Plan: Add Avalanche Runout Zone Avoidance
Compute runout zones eagerly when aspects are calculated, using the 10° beta point (gradient ≈ 0.176 rise/run) from avalanche literature. Runout zones will be avoided during pathfinding and visualized with a distinct amber hue.
Algorithm Overview
Runout Zone Computation (in Rust):
- For each pixel where
gradient >= 0.176 AND aspect ∈ excluded_aspects: mark as source zone
- For each source zone pixel, perform steepest-descent flow to propagate danger downhill:
- Look at all 8 neighbors, find the one with lowest elevation
- If that neighbor's gradient is still ≥ 0.176 (10°), mark it as runout and continue flowing from there
- Stop when slope drops below 10° (the beta point where debris typically stops)
- Output: binary raster where
1 = runout zone, 0 = safe
Steps
-
Add compute_runout_zones function in azimuth.rs: Accept excluded_aspects: Vec<String>, elevations: &Vec<Vec<f64>>, azimuths: &Vec<Vec<f64>>, gradients: &Vec<Vec<f64>>. Iterate all pixels; for each source cell (steep + excluded aspect), do BFS/flood-fill following steepest descent until gradient < 0.176. Return Vec<Vec<u8>> runout mask.
-
Extend AzimuthResult and compute_azimuths in azimuth.rs: Add excluded_aspects: &[String] parameter to compute_azimuths. Call compute_runout_zones after computing azimuths/gradients. Add runout_zones: Vec<u8> field to AzimuthResult (serialized as GeoTIFF).
-
Update TypeScript WASM wrapper in pathfinder-wasm.ts: Pass excludedAspects array to compute_azimuths, receive and return runout_zones buffer.
-
Thread runout through find-path-button.tsx: Pass excludedAspects when calling aspect computation, forward runout_zones to handleSetAspectRaster callback.
-
Add runout exclusion in find_path.rs: Accept runout_zones buffer, parse as raster, add check in successors closure: if runout_zones[ny][nx] > 0 { break 'neighbors; } alongside existing aspect check.
-
Update handleSetAspectRaster in page.tsx: Accept third runout_zones: Uint8Array parameter, parse with parseGeoraster, merge as third band into aspectRaster state.
-
Add runout visualization in leaflet-raster-layer.tsx: Read third band (values[2]) from aspectRaster. For pixels where runout > 0, render with amber/orange color (rgba(245, 158, 11, 0.5)) distinct from the red excluded-aspect shading.
Further Considerations
-
Caching invalidation: Runout depends on excludedAspects, so changing aspects requires recomputing runout. Should we cache multiple runout configurations, or always recompute? Recommend: always recompute—it's fast and aspects rarely change mid-session
-
Steep terrain without excluded aspects: Currently runout only flows from excluded-aspect slopes. Should all steep terrain generate runout regardless of aspect selection? Recommend: only excluded aspects, since user explicitly chose what to avoid
-
Flow algorithm choice: BFS flood-fill vs. following single steepest-descent path. BFS captures wider runout fans but is slower. Recommend: start with single steepest-descent path per source cell for performance, can expand later
Plan: Add Avalanche Runout Zone Avoidance
Compute runout zones eagerly when aspects are calculated, using the 10° beta point (gradient ≈ 0.176 rise/run) from avalanche literature. Runout zones will be avoided during pathfinding and visualized with a distinct amber hue.
Algorithm Overview
Runout Zone Computation (in Rust):
gradient >= 0.176ANDaspect ∈ excluded_aspects: mark as source zone1= runout zone,0= safeSteps
Add
compute_runout_zonesfunction in azimuth.rs: Acceptexcluded_aspects: Vec<String>,elevations: &Vec<Vec<f64>>,azimuths: &Vec<Vec<f64>>,gradients: &Vec<Vec<f64>>. Iterate all pixels; for each source cell (steep + excluded aspect), do BFS/flood-fill following steepest descent until gradient < 0.176. ReturnVec<Vec<u8>>runout mask.Extend
AzimuthResultandcompute_azimuthsin azimuth.rs: Addexcluded_aspects: &[String]parameter tocompute_azimuths. Callcompute_runout_zonesafter computing azimuths/gradients. Addrunout_zones: Vec<u8>field toAzimuthResult(serialized as GeoTIFF).Update TypeScript WASM wrapper in pathfinder-wasm.ts: Pass
excludedAspectsarray tocompute_azimuths, receive and returnrunout_zonesbuffer.Thread runout through find-path-button.tsx: Pass
excludedAspectswhen calling aspect computation, forwardrunout_zonestohandleSetAspectRastercallback.Add runout exclusion in find_path.rs: Accept
runout_zonesbuffer, parse as raster, add check in successors closure:if runout_zones[ny][nx] > 0 { break 'neighbors; }alongside existing aspect check.Update
handleSetAspectRasterin page.tsx: Accept thirdrunout_zones: Uint8Arrayparameter, parse withparseGeoraster, merge as third band intoaspectRasterstate.Add runout visualization in leaflet-raster-layer.tsx: Read third band (
values[2]) fromaspectRaster. For pixels whererunout > 0, render with amber/orange color (rgba(245, 158, 11, 0.5)) distinct from the red excluded-aspect shading.Further Considerations
Caching invalidation: Runout depends on
excludedAspects, so changing aspects requires recomputing runout. Should we cache multiple runout configurations, or always recompute? Recommend: always recompute—it's fast and aspects rarely change mid-sessionSteep terrain without excluded aspects: Currently runout only flows from excluded-aspect slopes. Should all steep terrain generate runout regardless of aspect selection? Recommend: only excluded aspects, since user explicitly chose what to avoid
Flow algorithm choice: BFS flood-fill vs. following single steepest-descent path. BFS captures wider runout fans but is slower. Recommend: start with single steepest-descent path per source cell for performance, can expand later