name: borneo-peatland-fires
version: "0.1"
category: events-hazards
themes: [hazards, vegetation]
family: fire-burn-severity-dnbr-recovery
answers_verb: track                       # tracks burn severity + vegetation recovery over time (GQA demand verb)
cites_papers: ["key-benson-2006"]         # dNBR / MTBS severity method
produces_concepts: [burn-severity]        # result/dnbr-severity layer is MTBS burn severity
description: >
  Fire scar mapping and peatland degradation in Central Kalimantan, Indonesia.
  Drainage canals for palm oil plantations expose deep carbon-rich peat to fire;
  the 2015 Indonesia fires released more CO2 in 6 weeks than Germany emits in a
  year, and the haze reached Singapore, Malaysia, and Thailand. dNBR maps burn
  severity per event; annual NDVI tracks recovery — or its absence.

settings:
  default_bbox: [113.5, 1.0, 115.5, 2.8]   # Central Kalimantan peatland belt
  default_crs: EPSG:32750                   # UTM 50S, metric
  cloud_cover_max_pct: 60
  prefire_months: [4, 5, 6]                 # Apr-Jun: dry enough, before fire season
  postfire_months: [10, 11, 12]             # Oct-Dec: after main fire season
  dnbr_thresholds:                          # USGS severity classes
    low: 0.10
    moderate_low: 0.27
    moderate_high: 0.44
    high: 0.66
  fire_events:
    - { label: "2015", prefire: "2015-04-01/2015-06-30", postfire: "2015-10-01/2015-12-31" }
    - { label: "2019", prefire: "2019-04-01/2019-06-30", postfire: "2019-10-01/2019-12-31" }

inputs:
  aoi:
    bbox: [113.5, 1.0, 115.5, 2.8]
    crs_target: EPSG:32750
    notes: Central Kalimantan peat dome — historically up to 10m deep peat

  date_range:
    start: "2015-07-01"
    end: "2026-06-01"

  collection:
    uri: "@sentinel-2-l2a"
    stac_endpoint: "https://earth-search.aws.element84.com/v1"

layers:

  # ── SOURCE ──────────────────────────────────────────────────────────────────

  source/s2-prefire:
    type: raster
    uri: "@sentinel-2-l2a"
    description: >
      Pre-fire season composite (Apr-Jun). Median over the window.
      Parameterized per fire event; executed per entry in fire_events.
    params:
      bbox: [113.5, 1.0, 115.5, 2.8]   # COG spatial window
      assets:
        - B04    # Red — for NDVI; also RGB context
        - B08    # NIR — NBR numerator; bright vegetation
        - B12    # SWIR2 — NBR denominator; absorbs in healthy veg, reflects in char
        - SCL    # Scene classification cloud mask
      query:
        "eo:cloud_cover": { lt: 60 }
      aggregate: median

  source/s2-postfire:
    type: raster
    uri: "@sentinel-2-l2a"
    description: Post-fire season composite (Oct-Dec). Same bands as prefire.
    params:
      bbox: [113.5, 1.0, 115.5, 2.8]
      assets: [B04, B08, B12, SCL]
      query:
        "eo:cloud_cover": { lt: 60 }
      aggregate: median

  # ── COMPUTE ─────────────────────────────────────────────────────────────────

  compute/nbr-prefire:
    type: raster
    description: NBR from the pre-fire composite.
    compute:
      op: raster_calc
      inputs:
        data: { layer: source/s2-prefire }
      params:
        expression: "(B08 - B12) / (B08 + B12)"
        output_band: nbr_pre

  compute/nbr-postfire:
    type: raster
    description: NBR from the post-fire composite.
    compute:
      op: raster_calc
      inputs:
        data: { layer: source/s2-postfire }
      params:
        expression: "(B08 - B12) / (B08 + B12)"
        output_band: nbr_post

  compute/dnbr-stack:
    type: raster
    # raster_calc reads ONE raster and binds bands positionally (b1..bN), so two-layer
    # arithmetic composes through stack_concat: stack the pair, difference the bands.
    compute:
      op: stack_concat
      inputs:
        rasters:
          - { layer: compute/nbr-prefire }   # band 1: prefire NBR
          - { layer: compute/nbr-postfire }   # band 2: postfire NBR

  compute/dnbr:
    type: raster
    description: >
      dNBR = NBR_prefire - NBR_postfire. Positive = burn. Classified by
      USGS severity thresholds: unburned (<0.1), low, mod-low, mod-high, high (>0.66).
    compute:
      op: raster_calc
      inputs:
        data: { layer: compute/dnbr-stack }
      params:
        expression: "b1 - b2"   # NBR_prefire - NBR_postfire
        output_band: dnbr

  compute/recovery-ndvi:
    type: raster_timeseries
    description: >
      Annual NDVI over the burn scar area to track vegetation recovery.
      Recovery stalls over deep peat — the substrate is gone, not just scorched.
    compute:
      op: raster_calc
      inputs:
        data: { layer: source/s2-postfire }
      params:
        expression: "(B08 - B04) / (B08 + B04)"
        output_band: ndvi
        aggregate: median
        group_by: year

  # ── RESULTS ─────────────────────────────────────────────────────────────────

  result/burn-scar-map:
    type: vector
    description: >
      Burn scar polygons per fire event, classified by dNBR severity.
      Attributes: event_year, severity_class, area_ha, dnbr_mean.
    compute:
      op: raster_classify_to_vector
      inputs:
        raster: { layer: compute/dnbr }
      params:
        thresholds: "${settings.dnbr_thresholds}"
        min_area_ha: 1.0

  result/dnbr-severity:
    type: raster
    description: dNBR severity COG per fire event.
    compute:
      op: raster_identity
      inputs:
        data: { layer: compute/dnbr }
      params:
        format: cog
        dtype: float32

  result/recovery-trajectory:
    type: table
    description: >
      Annual mean NDVI over the burn scar footprint, years 1-N after fire.
      One row per year: (year, years_since_fire, mean_ndvi_burn_scar).
    compute:
      op: analysis_zonal_stats_temporal
      inputs:
        raster: { layer: compute/recovery-ndvi }
        zones: { layer: result/burn-scar-map }
      params:
        stats: [mean]
        time_axis: true

outputs:
  primary:
    - result/burn-scar-map         # GeoParquet — severity-classified polygons
    - result/dnbr-severity         # COG — continuous dNBR
    - result/recovery-trajectory   # Parquet — annual NDVI over scar
  show_your_work:
    - compute/nbr-prefire          # COG — pre-fire baseline
    - compute/nbr-postfire         # COG — post-fire state

cache:
  scope:
    - inputs.aoi.bbox
    - inputs.date_range
    - settings.dnbr_thresholds
    - settings.fire_events
  storage: ./cache/
  reuse_policy: hash_inputs

license:
  recipe: Apache-2.0
  output_dataset: CC-BY-4.0
  upstream_data:
    - { source: sentinel-2-l2a, license: copernicus-open }
