San Juan Watershed Riparian Watch

Basin-scale riparian monitoring from satellite data: a STAC ETL over Sentinel and Landsat, geospatial ML, and a map-based decision-support UI.

PythonSTACGeospatial ML.NET 10PostGISReact 18MapLibre GLAspire

Try it liveSource on GitHub
Ask the map: the agent renders Random Forest (green) and OlmoEarth (teal) riparian extent over the San Juan corridor near Farmington and Bloomfield, with the two models tracing visibly different vegetation boundaries

What it is

A basin-scale monitoring system for riparian ecosystems in the San Juan River watershed (Colorado + New Mexico): satellite imagery in, health-graded map intelligence out. It learns where riparian vegetation actually is from satellite time series — instead of assuming a fixed distance from a stream — then scores its condition (including invasive tamarisk and Russian-olive cover) and tracks change across the Earth-observation record. It’s where my two careers meet: a decade of GIS engineering and an environmental-studies background on one side, modern data engineering and ML on the other.

The live site is an interactive story map of the corridor, a Random Forest vs. Ai2 OlmoEarth foundation-model field test, and a grounded RAG agent that streams cited answers about the watershed.

The pipeline

Why it’s interesting engineering

Most portfolio ML projects stop at a notebook. This one is a system: reproducible ETL over public satellite archives, models validated the way spatial data demands (spatial CV, not random splits), a typed API layer, and a UI a watershed manager could actually use. It’s deployed with Docker and PostgreSQL, guarded in production by rate limits, input shields, and Cloudflare Turnstile.

And it holds itself to scientific standards: the repo carries a public engineering & methodology walkthrough inviting scrutiny of the weak points, and a RETRACTIONS.md. When a result’s interpretation didn’t survive re-verification, the correction was published, not buried. The headline finding stands either way: on an out-of-distribution reach, the pixel-wise Random Forest collapses to near-chance while the OlmoEarth foundation model holds, a measured demonstration of classical-ML brittleness to distribution shift.

Ask the agent: the document agent answers "How does OlmoEarth compare to the Random Forest?" with a generated comparison table across speed, accuracy, out-of-distribution robustness and spatial context, each row carrying numbered citations back into the watershed corpus

That finding is also what the document agent answers from. It runs hybrid RAG over the watershed corpus plus the experiment results, so asking it to compare the two models returns a table with every row cited back to a source you can open. The model selector is honest about what’s actually serving, too: OLMo is listed as disabled while no provider serves it, rather than quietly substituting another model.