# MapBiomas Validation — Ji-Paraná, Rondônia
#
# Reproduce MapBiomas products (fire scars, water dynamics, simplified LULC)
# using Folia's browser-native WASM pipeline. Validate pixel-by-pixel against
# MapBiomas Collection 10, JRC Global Surface Water, and FIRMS active fire.
#
# Pipeline:
#
#   landsat-pre  ──→ NBR pre  ──┐
#   landsat-post ──→ NBR post ──┼──→ dNBR ──→ fire scars
#                               │
#   landsat ──→ MNDWI  ──────────────────────→ water mask
#   landsat ──→ NDVI  ──┐
#   landsat ──→ NDWI  ──┼──→ decision tree ──→ land cover (5 classes)
#   landsat ──→ NDBI  ──┘

name: "MapBiomas Validation — Rondônia"
description: >
  Reproduce MapBiomas products for a ~50km tile in Ji-Paraná, Rondônia, Brazil
  using Folia's browser-native WASM pipeline. Validated against MapBiomas
  Collection 10, JRC Global Surface Water, and FIRMS active fire data.

# Reproduction metadata (ADR-0032)
reproduction:
  type: reproduction
  quality_tier: human
  validation_status: validated
  last_validated: "2026-04-16T12:00:00Z"
  study_period:
    start: "2023-01-01"
    end: "2023-12-31"
  spatial_extent:
    primary_bbox: [-62.2, -11.1, -61.7, -10.6]  # Ji-Paraná tile
    tiles: 10
    states: [Rondônia, "Mato Grosso", Pará, Amazonas]
    biomes: [arc, cerrado, equatorial]
  references:
    - rel: describedby
      href: "https://mapbiomas.org/"
      title: "MapBiomas Project — Collection 10"
    - rel: describedby
      href: "doi:10.1038/nature20584"
      title: "Pekel et al. 2016 — JRC Global Surface Water"
    - rel: describedby
      href: "doi:10.1016/j.rse.2014.02.015"
      title: "Olofsson et al. 2014 — Good practices for accuracy assessment"
    - rel: validation
      href: "validation/comparison.yaml"
      title: "Comparison spec (ADR-0032 D3)"
    - rel: validation
      href: "validation/checksums.yaml"
      title: "Source data checksums"
    - rel: validation
      href: "results/olofsson_metrics.json"
      title: "Olofsson-protocol pooled metrics"
  method_ops:
    - imagery_composite
    - raster_calc
    - raster_normalized_diff
    - classify_rf

settings:
  default_bbox: [-62.2, -11.1, -61.7, -10.6]
  theme: light

layers:

  # ── Source: Landsat composites (median, band names preserved) ──────────

  # Wet season composite (Jan–May 2023)
  landsat-wet:
    type: raster
    uri: "stac://microsoft-pc/landsat-c2-l2"
    bands: [red, nir08, swir16]
    params:
      temporal: ["2023-01-01", "2023-05-31"]
      composite: median
      limit: 50
      sort: "eo:cloud_cover"
      scale:
        factor: 0.0000275
        offset: -0.2
        nodata: 0

  # Dry season composite (Jul–Nov 2023)
  landsat-dry:
    type: raster
    uri: "stac://microsoft-pc/landsat-c2-l2"
    bands: [red, nir08, swir16]
    params:
      temporal: ["2023-07-01", "2023-11-30"]
      composite: median
      limit: 50
      sort: "eo:cloud_cover"
      scale:
        factor: 0.0000275
        offset: -0.2
        nodata: 0

  # Full-year composite for indices + true color
  landsat:
    type: raster
    uri: "stac://microsoft-pc/landsat-c2-l2"
    bands: [blue, green, red, nir08, swir16]
    params:
      temporal: ["2023-01-01", "2023-12-31"]
      composite: median
      limit: 75
      sort: "eo:cloud_cover"
      scale:
        factor: 0.0000275
        offset: -0.2
        nodata: 0
    style:
      rgb: [red, green, blue]
      clim: [0, 0.3]

  # ── Product 1: Fire Scars (dNBR) ──────────────────────────────────────

  nbr-pre:
    type: raster
    visible: false
    compute:
      op: raster_calc
      inputs:
        raster: { layer: landsat-wet }
      params:
        expression: "(nir08 - swir16) / (nir08 + swir16)"

  nbr-post:
    type: raster
    visible: false
    compute:
      op: raster_calc
      inputs:
        raster: { layer: landsat-dry }
      params:
        expression: "(nir08 - swir16) / (nir08 + swir16)"

  # Wet/dry NDVI for temporal amplitude
  ndvi-wet:
    type: raster
    visible: false
    compute:
      op: raster_calc
      inputs:
        raster: { layer: landsat-wet }
      params:
        expression: "(nir08 - red) / (nir08 + red)"

  ndvi-dry:
    type: raster
    visible: false
    compute:
      op: raster_calc
      inputs:
        raster: { layer: landsat-dry }
      params:
        expression: "(nir08 - red) / (nir08 + red)"

  ndvi-amplitude:
    type: raster
    visible: false
    compute:
      op: raster_calc
      inputs:
        wet: { layer: ndvi-wet }
        dry: { layer: ndvi-dry }
      params:
        expression: "wet - dry"

  dnbr:
    type: raster
    compute:
      op: raster_calc
      inputs:
        pre: { layer: nbr-pre }
        post: { layer: nbr-post }
      params:
        expression: "pre - post"
    style:
      palette: ylorrd
      clim: [0.1, 0.66]

  fire-scars:
    type: raster
    compute:
      op: raster_calc
      inputs:
        dnbr: { layer: dnbr }
      params:
        expression: "where(dnbr > burn_thresh, 1, 0)"
        burn_thresh: 0.27
    style:
      clim: [0, 1]
      colorRamp:
        - { value: 0, color: "#00000000", label: "No burn" }
        - { value: 1, color: "#FF4500", label: "Burned" }
      form:
        burn_thresh:
          type: slider
          label: "dNBR Burn Threshold"
          min: 0.1
          max: 0.5
          step: 0.01
          default: 0.27
          description: "Pixels with dNBR above this → burned"

  # ── Product 2: Water (MNDWI) ──────────────────────────────────────────

  mndwi:
    type: raster
    compute:
      op: raster_calc
      inputs:
        raster: { layer: landsat }
      params:
        expression: "(green - swir16) / (green + swir16)"
    style:
      palette: blues
      clim: [-0.3, 0.8]

  water:
    type: raster
    compute:
      op: raster_calc
      inputs:
        mndwi: { layer: mndwi }
      params:
        expression: "where(mndwi > water_thresh, 1, 0)"
        water_thresh: -0.20
    style:
      clim: [0, 1]
      colorRamp:
        - { value: 0, color: "#00000000", label: "Land" }
        - { value: 1, color: "#2196F3", label: "Water" }
      form:
        water_thresh:
          type: slider
          label: "Water (MNDWI)"
          min: -0.2
          max: 0.5
          step: 0.05
          default: -0.20
          description: "Pixels with MNDWI above this → water"

  # ── Product 3: Simplified LULC (5-class decision tree) ────────────────

  primitive/ndvi:
    type: raster
    compute:
      op: raster_calc
      inputs:
        raster: { layer: landsat }
      params:
        expression: "(nir08 - red) / (nir08 + red)"
    style:
      palette: ylgn
      clim: [-0.2, 0.9]

  primitive/ndwi:
    type: raster
    compute:
      op: raster_calc
      inputs:
        raster: { layer: landsat }
      params:
        expression: "(green - nir08) / (green + nir08)"
    style:
      palette: blues
      clim: [-0.5, 0.5]

  primitive/ndbi:
    type: raster
    compute:
      op: raster_calc
      inputs:
        raster: { layer: landsat }
      params:
        expression: "(swir16 - nir08) / (swir16 + nir08)"
    style:
      palette: oranges
      clim: [-0.5, 0.5]

  primitive/bsi:
    type: raster
    visible: false
    compute:
      op: raster_calc
      inputs:
        raster: { layer: landsat }
      params:
        expression: "((swir16 + red) - (nir08 + blue)) / ((swir16 + red) + (nir08 + blue))"

  # Raw bands for RF features
  band/blue:
    type: raster
    visible: false
    compute:
      op: raster_calc
      inputs:
        raster: { layer: landsat }
      params:
        expression: "blue"

  band/green:
    type: raster
    visible: false
    compute:
      op: raster_calc
      inputs:
        raster: { layer: landsat }
      params:
        expression: "green"

  band/red:
    type: raster
    visible: false
    compute:
      op: raster_calc
      inputs:
        raster: { layer: landsat }
      params:
        expression: "red"

  band/nir08:
    type: raster
    visible: false
    compute:
      op: raster_calc
      inputs:
        raster: { layer: landsat }
      params:
        expression: "nir08"

  band/swir16:
    type: raster
    visible: false
    compute:
      op: raster_calc
      inputs:
        raster: { layer: landsat }
      params:
        expression: "swir16"

  land-cover:
    type: raster
    compute:
      op: raster_calc
      inputs:
        ndvi: { layer: primitive/ndvi }
        ndwi: { layer: primitive/ndwi }
        ndbi: { layer: primitive/ndbi }
      params:
        expression: "where(ndwi > water_thresh, 1, where(ndvi > forest_thresh, 2, where(ndvi > crop_thresh, 3, where(ndbi > built_thresh, 4, 5))))"
        water_thresh: -0.20
        forest_thresh: 0.78
        crop_thresh: 0.45
        built_thresh: -0.10
    style:
      clim: [1, 5]
      colorRamp:
        - { value: 1, color: "#2196F3", label: "Water" }
        - { value: 2, color: "#1B5E20", label: "Forest" }
        - { value: 3, color: "#8BC34A", label: "Pasture/Crop" }
        - { value: 4, color: "#F44336", label: "Built-up" }
        - { value: 5, color: "#D7CCC8", label: "Bare/Other" }
      form:
        water_thresh:
          type: slider
          label: "Water (NDWI)"
          min: -0.4
          max: 0.3
          step: 0.02
          default: -0.20
        forest_thresh:
          type: slider
          label: "Forest (NDVI)"
          min: 0.60
          max: 0.90
          step: 0.01
          default: 0.78
        crop_thresh:
          type: slider
          label: "Pasture/Crop (NDVI)"
          min: 0.20
          max: 0.65
          step: 0.01
          default: 0.45
        built_thresh:
          type: slider
          label: "Built-up (NDBI)"
          min: -0.3
          max: 0.1
          step: 0.02
          default: -0.10

views:
  - name: "Fire Scars"
    layers: [landsat, fire-scars]
  - name: "dNBR (continuous)"
    layers: [landsat, dnbr]
  - name: "Water"
    layers: [landsat, water]
  - name: "MNDWI (continuous)"
    layers: [landsat, mndwi]
  - name: "Land Cover"
    layers: [land-cover]
  - name: "Primitives"
    layers: [primitive/ndvi, primitive/ndwi, primitive/ndbi]
  - name: "True Color"
    layers: [landsat]
