# catalog/operations/analysis/urban_heat_index.yaml

id: analysis_urban_heat_index
name: Urban Heat Index
description: >
  Derive urban heat island (UHI) intensity in °C by subtracting a per-AOI
  rural baseline LST from each pixel's LST. The rural baseline is the mean
  LST of all pixels classified as "non-urban" by the supplied urbanisation
  mask (e.g., building-density, impervious-surface, or any continuous
  urbanisation indicator). Input LST in Kelvin is auto-detected (mean > 200)
  and converted; output is always °C.
version: 1.0.0
category: analysis
type: raster-to-raster
default_implementation: native

inputs:
  - name: lst
    type: raster
    format: geotiff
    description: Land surface temperature (Celsius or Kelvin; auto-detected)
    required: true
  - name: urbanisation_mask
    type: raster
    format: geotiff
    description: >
      Continuous or binary urbanisation indicator (1 = fully urban, 0 = fully
      rural). Pixels with values < `rural_threshold` define the rural
      baseline pool. Must share CRS and dimensions with `lst`.
    required: true

requires:
  # LST + an urbanisation indicator. The urbanisation input is most often
  # the `built-up-area` concept (impervious-surface is a reasonable
  # substitute; pick the one the dataset publishes).
  concepts: [land-surface-temperature, built-up-area]

outputs:
  - name: uhi
    type: raster
    format: cog
    dtype: float32
    # No concept slug — no `urban-heat-index` concept exists in
    # registry/concepts/ yet. Gap candidate for a future ticket.
    description: >
      UHI intensity in °C (positive = warmer than rural baseline, negative =
      cooler). NoData where either input has NoData.

params:
  rural_threshold:
    type: number
    default: 0.1
    description: >
      Pixels with urbanisation_mask < this threshold are pooled into the
      rural baseline. For walkthru building-density (0–1 scale), 0.1 is
      reasonable for European cities. Tune per AOI.
  temperature_unit:
    type: string
    default: auto
    enum: [auto, celsius, kelvin]
    description: >
      Input LST unit. "auto" detects Kelvin when mean LST > 200.

granularity:
  min_resolution_m: 10
  max_resolution_m: 5000
  recommended_resolution_m: 1000
  output_resolution: inherit
  requires_continuous: true
  suitable_aoi_km2:
    min: 1
    max: 50000
  realtime_max_km2: 5000

uncertainty:
  error_model: additive
  factors:
    - name: lst_retrieval_error
      severity: medium
      description: Satellite LST retrieval error (typically ±1–3 K for MODIS/SLSTR).
      mitigations:
        - Use 8-day or monthly LST composites to average out per-scene retrieval noise.
        - Cross-compare MOD11A2 with Sentinel-3 SLSTR for the same window.
    - name: baseline_pool_variance
      severity: medium
      description: Variance within the rural-pool; smaller AOIs have noisier baselines.
      mitigations:
        - Expand the AOI buffer so the rural pool has enough cells to stabilise the mean.
        - Increase rural_threshold to include more rural cells when AOI is small.
    - name: temporal_misalignment
      severity: low
      description: When lst and urbanisation_mask are from different epochs.
      mitigations:
        - Use a contemporaneous building / impervious mask within ±2 years of the LST epoch.
  limitations:
    - The baseline is AOI-pooled, not spatially varying — does not capture
      sub-regional gradients. Future v2 will add `spatial_baseline_radius_km`
      for a moving-window baseline.
    - Assumes both inputs share CRS and dimensions. Run raster_resample +
      raster_reproject upstream if not.
  methodology_reference: |
    Oke (1973) "City size and the urban heat island" — original UHI definition.
    Voogt & Oke (2003) "Thermal remote sensing of urban climates" — satellite-LST UHI methodology.

# 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:
  python:
    function: geo.raster.urban_heat_index
    dispatch: folia/compute.py _BUILTIN_OP_MAP

display_hints:
  map:
    type: raster
    palette: magma
    rescale: [-2, 8]
    title: Urban Heat Index (°C)
  table:
    sort_by: uhi
    order: desc
