# catalog/operations/terrain/viewshed.yaml

id: terrain_viewshed
name: Viewshed Analysis
description: >
  Calculate visible area from one or more observer points. Uses a DEM to
  determine line-of-sight visibility. If no DEM is provided, folia uses
  Copernicus 30m global DEM automatically.
version: 1.0.0
category: terrain
type: vector-raster-to-raster

inputs:
  - name: observer_points
    type: vector
    format: geojson
    geometry: Point
    description: Observer locations (points to calculate visibility from)
    required: true
  - name: dem
    type: raster
    format: cog
    description: >
      Digital Elevation Model for terrain. Optional - if not provided,
      folia will use Copernicus 30m global DEM.
    required: false

requires:
  concepts: [elevation]

outputs:
  - name: viewshed
    type: raster
    format: cog
    dtype: uint8
    concept: viewshed
    description: >
      Binary visibility raster. 1 = visible from at least one observer,
      0 = not visible, nodata = outside analysis area.

params:
  observer_height:
    type: number
    default: 1.7
    description: Height of observer above ground in meters (default 1.7m = eye level)
    min: 0
    max: 1000
  target_height:
    type: number
    default: 0
    description: Height of targets above ground in meters (default 0 = ground level)
    min: 0
    max: 1000
  max_distance:
    type: number
    default: 10000
    description: Maximum viewing distance in meters (default 10km)
    min: 100
    max: 100000
  refractivity:
    type: number
    default: 0.14286
    description: Atmospheric refraction coefficient (default 0.14286 = 1/7)
    min: 0
    max: 1

# Reference data - folia provides DEM when user doesn't supply one
references:
  - name: dem
    catalog: folia://elevation/cop30
    size_estimate_mb: 50000
    required: true
    description: >
      Copernicus 30m global DEM. Used when user doesn't provide their own
      DEM input. Higher resolution DEMs (3DEP 1m) can be provided by user
      for better accuracy in the US.

granularity:
  min_resolution_m: 1
  max_resolution_m: 90
  recommended_resolution_m: 30
  output_resolution: inherit
  suitable_aoi_km2:
    min: 0.1
    max: 5000
  realtime_max_km2: 50
  requires_continuous: true

uncertainty:
  error_model: propagated
  factors:
    - name: dem_accuracy
      description: "Line-of-sight calculations are sensitive to DEM errors"
      severity: high
      mitigations:
        - "Use high-resolution lidar DEM for accurate viewsheds"
        - "Reduce max_distance to limit error accumulation"
    - name: atmospheric_refraction
      description: "Refraction model is approximate"
      severity: low
      mitigations:
        - "Adjust refractivity coefficient for local conditions"
    - name: vegetation_and_structures
      description: "DEM does not include vegetation canopy or buildings"
      severity: high
      mitigations:
        - "Use DSM (digital surface model) instead of DTM when available"
        - "Set target_height to approximate canopy/structure height"
  limitations:
    - "Does not account for vegetation or structures unless using DSM"
    - "Binary output loses distance-to-horizon information"
    - "Computationally expensive for large areas"

execution:
  realtime_max_km2: 50
  cost_per_km2: 0.01
  time_per_km2_sec: 2.0
  memory_profile: high
  profile:
    cpu: high
    memory: high
    io: medium
  scaling:
    model: superlinear
    parallelizable: true
    min_chunk_km2: 0.5

cache_policy:
  ttl_days: 30
  invalidate_on: [source_update, dem_update]

# backends: audited 2026-08-14 (defect 50). NOTHING dispatches this op: no arm in folia-engine
# `dispatch_op`, no `registerOp`/OP_TABLE entry in packages/compute, no `_BUILTIN_OP_MAP`
# key in folia/compute.py, no backend manifest. Declared EMPTY on purpose — an absent
# block would be indistinguishable from one nobody ever wrote.
backends: {}

display_hints:
  map:
    renderer: maplibre
    palette: binary
    opacity: 0.6
  info:
    fields: [visible]
    format: "{value}"
    section: Viewshed

ui:
  icon: eye
  color: "#4169E1"

examples:
  - name: Basic viewshed
    description: Calculate visibility from a single point
    inputs:
      observer_points: |
        {"type":"FeatureCollection","features":[
          {"type":"Feature","geometry":{"type":"Point","coordinates":[-111.5,40.5]}}
        ]}
    params:
      max_distance: 5000

  - name: Multiple observers with custom DEM
    description: Use high-res DEM for better accuracy
    inputs:
      observer_points: observers.geojson
      dem: s3://my-bucket/lidar_dem.tif
    params:
      observer_height: 30
      max_distance: 20000
