crop-anomaly
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.
- recipe
- Apache-2.0
- outputs
- CC-BY-4.0
The contract
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
| output | kind | layer | what 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
registry/recipes/crop-anomaly/folia.yaml