# catalog/operations/terrain/aspect.yaml

id: terrain_aspect
name: Aspect
description: >
  Calculate terrain aspect (compass direction of slope) from DEM.
  Output values are degrees clockwise from north (0-360).
  Flat areas (slope below flat_threshold) are nodata.
version: 1.0.0
category: terrain
type: raster-to-raster
default_implementation: native

inputs:
  - name: dem
    type: raster
    format: cog
    description: Input Digital Elevation Model (GeoTIFF or COG)
    required: true

requires:
  concepts: [elevation]

outputs:
  - name: aspect
    type: raster
    format: cog
    dtype: float32
    unit: degrees
    concept: aspect
    description: Aspect raster in degrees clockwise from north (0-360)

params:
  flat_threshold:
    type: number
    default: 1.0
    description: Slope threshold below which aspect is undefined (degrees)
    min: 0.0
    max: 10.0
  smooth:
    type: boolean
    default: true
    description: Apply Gaussian smoothing before calculation
  smooth_sigma:
    type: number
    default: 1.5
    description: Gaussian sigma for smoothing
    min: 0.1
    max: 10.0

granularity:
  min_resolution_m: 1
  max_resolution_m: 90
  recommended_resolution_m: 10
  output_resolution: inherit
  suitable_aoi_km2:
    min: 0.01
    max: 50000
  realtime_max_km2: 200
  requires_continuous: true

uncertainty:
  error_model: propagated
  factors:
    - name: dem_vertical_accuracy
      description: "Aspect is highly sensitive to DEM noise on gentle slopes"
      severity: medium
      mitigations:
        - "Apply smoothing for noisy DEMs"
        - "Use flat_threshold to mask unreliable values"
    - name: flat_area_ambiguity
      description: "Aspect is undefined on flat terrain"
      severity: low
      mitigations:
        - "Increase flat_threshold parameter"
  limitations:
    - "Undefined for perfectly flat cells"
    - "Discontinuity at 0/360 degree boundary"

execution:
  realtime_max_km2: 200
  cost_per_km2: 0.001
  time_per_km2_sec: 0.4
  memory_profile: low
  profile:
    cpu: low
    memory: low
    io: medium
  scaling:
    model: linear
    parallelizable: true
    min_chunk_km2: 1

default_input:
  connector: usgs_3dep
  params:
    resolution: 1m

# backends: audited 2026-08-14 (defect 50). A key means a runtime that DISPATCHES this op —
# folia-engine `dispatch_op` (products/sdk/folia-engine/src/lib.rs), a `registerOp`/OP_TABLE
# entry in packages/compute, `_BUILTIN_OP_MAP` in folia/compute.py, or a backend manifest
# (folia/backends/*/backend.yaml).
backends:
  rust-wasm:
    function: terrain_ops::terrain_aspect
    handle_function: h_terrain_aspect
  js:
    function: terrain_aspect
  python:
    function: geo.terrain.aspect
    dispatch: folia/compute.py _BUILTIN_OP_MAP
  gee:
    function: folia.backends.gee.ops.aspect
    dispatch: folia/backends/gee/backend.yaml

display_hints:
  map:
    renderer: maplibre
    palette: aspect
    opacity: 0.8
    rescale: "0,360"
    colormap: twilight
  info:
    fields: [aspect_degrees]
    format: "{value}\u00b0"
    section: Terrain

cache_policy:
  regional_precompute: [ski_areas]
  ttl_days: 90
  invalidate_on: [source_update]
