crop-anomaly

unverified
v0.1 AOI: San Joaquin Valley cropland block · bbox -120.05, 36.55, -119.85, 36.72

Per-field crop anomaly detection from a Sentinel-2 NDVI time series. For an AOI covered by Fields of the World boundaries, fit a harmonic seasonal model to each pixel's NDVI history, compute the residual between the latest observed NDVI and the model-expected value, and aggregate that anomaly residual to each field. Fields running persistently below their own fitted season are flagged as anomalous. The method follows the BFAST season-anomaly framing (Verbesselt et al. 2010): the harmonic fit captures the expected phenological trajectory, and the signed residual (observed minus fitted) is the anomaly signal. CCDC-style persistence logic (Zhu & Woodcock 2014) informs the threshold and flagging step. The per-field anomaly table is the primary product; the NDVI stack, fitted surface, and residual raster are the scrutable "show your work" layers. Swap the NDVI expression for EVI/NDRE to shift the vigor proxy, or tighten the anomaly threshold to control alert sensitivity.

Primary outputs
compute/field_flags
Show your work
compute/anomaly compute/expected compute/ndvi
Provenance
source_scene_ids
License
recipe
Apache-2.0
outputs
CC-BY-4.0
Map preview and live runs are available in the workbench.

The contract

legacy dialectcandidate

Read from this recipe's own declarations — parameters:, outputs:, requires:, and the ops its layers name. Not from its style blocks and not from any benchmark: a colour ramp is not a contract, and a measured run is evidence about one AOI rather than a domain.

Parameters

The legacy dialect has no typed parameter schema. Read the folia.yaml for what this recipe takes; nothing here can type-check it.

Outputs

outputkindlayerwhat it is
primary not declared ['compute/field_flags']
show_your_work not declared ['compute/anomaly', 'compute/expected', 'compute/ndvi']
provenance not declared ['source_scene_ids']

Capabilities it requires

The legacy dialect has no requires: block.

Where it runs

  • Browser preview does not run here compute/anomaly: layer type "raster_timeseries" is not one folia-view parses; compute/expected: carries an unsubstituted ${...} template; compute/field_flags: carries an unsubstituted ${...} template; and 2 more
  • Local CLI (folia run) runs here runs locally through the legacy (no spec_version) parser
  • Operated fleet (POST /plan, /run) runs here legacy dialect: a lock binds by the cut's layer names, not by digest
open in Workbench from registry/recipes/crop-anomaly/folia.yaml