Inspiration
In August 2026 the Trishuli River in Nepal flooded, and debris and floodwater destroyed roads and bridges. When that happens, ground sensors fail and rescue teams have to ask three questions fast: where did it hit, what was damaged, and who is cut off?
Satellites keep working when the ground infrastructure doesn't. Radar sees through monsoon clouds and works at night, but turning raw SAR scenes into something a rescuer can act on is hard. We wanted a pipeline that takes an area and a date and returns those three answers, one that works for any flood a judge picks, not just Trishuli.
What it does
Given a bounding box and a flood date, the system:
- Finds a compatible Sentinel-1 before/after pair automatically (same relative orbit, mode and polarization).
- Detects flood/debris-affected change and filters out terrain false positives using the Copernicus DEM.
- Overlays a pre-event OpenStreetMap snapshot (via the ohsome API) to estimate affected buildings, road length and bridges.
- Builds a road graph and removes affected segments to find settlements that lose their mapped road route to the nearest hospital or town.
- Communicates the verified results through a map dashboard, a one-page situation report and a grounded AI copilot that answers rescuers' questions in English and Nepali.
How we built it
The design principle is that deterministic geospatial code is the source of truth, and the LLM only explains it.
AOI + date → Sentinel-1 pair → SAR change detection → terrain filter
→ flood polygons → OSM exposure → road graph → verified JSON
→ dashboard + AI report
Change detection. For backscatter $\sigma^0$ before and after the event, we compute the log-ratio
$$\Delta = 10\log_{10}\sigma^0_{\text{after}} - 10\log_{10}\sigma^0_{\text{before}}$$
A strongly negative $\Delta$ means a surface that turned dark (newly smooth or inundated), which makes it a flood candidate.
Automatic thresholding. We choose the threshold $t^*$ with Otsu's method, which maximizes the between-class variance
$$t^* = \arg\max_t \; \omega_0(t)\,\omega_1(t)\,\big[\mu_0(t)-\mu_1(t)\big]^2$$
so nothing is hand-tuned to the Trishuli scene.
Terrain filtering. Dark pixels aren't automatically water. We use DEM-derived slope and elevation plausibility [and HAND, if implemented] to remove radar shadow and steep-slope false positives.
Exposure. We reproject to a projected CRS before measuring anything, then intersect flood polygons with OSM buildings, roads and bridges. For a road edge $e$ we compute
$$r_e = \frac{\text{length}(e \cap F)}{\text{length}(e)}$$
where $F$ is the flood polygon. The edge counts as blocked when $r_e \ge \tau$, with $\tau$ configurable.
Connectivity. Each damaged edge is removed from the networkx graph. A settlement is reported road-isolated if a path to its destination existed before and does not exist after.
Grounded AI. The LLM receives only the verified result JSON. It is instructed never to produce a number that isn't in that JSON, and to refuse questions the data can't answer, such as "how many people are trapped?" It also generates the one-page report.
Stack: Python, rasterio, numpy, scipy, geopandas, shapely, pyproj, networkx, Copernicus Data Space APIs, the ohsome API, [LLM provider], and a [React + Leaflet / Streamlit] dashboard.
Results
- Trishuli case study: [flood/debris area ≈ X km², N buildings, Y km of road, Z bridges, K settlements cut off].
- Comparison with EMSR927 (used only for validation, never as an input): precision [ ], recall [ ], IoU [ ]. [Describe honestly where we disagree and why.]
- Tested on a second area not used in development: [area, outcome, failures].
What we learned
- Pair selection matters as much as thresholding. Different orbit tracks view the Himalayas from different angles, so comparing them pixel by pixel creates fake change.
- Mountain SAR is hard. Shadow, layover and steep slopes produce dark pixels that look like water, so terrain-aware filtering is essential.
- Grounding an LLM is an architecture decision, not a prompt trick: structured JSON in, prose out.
- Choosing precise language is part of the work. At roughly 10 m resolution we can say a building intersects the detected affected area, but not that it collapsed.
- [Add what your team actually learned: CRS bugs, Copernicus API quirks, etc.]
Challenges we faced
- Finding and aligning compatible Sentinel-1 scenes automatically for an arbitrary AOI and date.
- Separating water from debris. Both change radar backscatter, but not in the same way, so we label the output "flood/debris-affected area" rather than claiming perfect water classification.
- Avoiding overfitting to Trishuli while the judges choose the area live.
- Incomplete OSM data. A missing road can look like a cut-off village, so "cut off" strictly means no remaining mapped road path.
- [Add real blockers you hit: download size, processing time, API limits, etc.]
Limitations
This is an educational prototype, not an operational rescue tool. Sentinel revisit is measured in days, so it is not an early-warning system. SAR has shadow and layover errors, and smooth non-water surfaces can mimic floods. OSM may be incomplete or outdated. "Cut off" means a loss of mapped road connectivity, not total physical isolation.
What's next
[Sentinel-2 confirmation, HAND-based filtering, the upstream flood-path bonus, a trained segmentation model, etc.]
Attribution
Contains modified Copernicus Sentinel data 2026. Produced using Copernicus WorldDEM-30 © DLR e.V. 2010–2014 and © Airbus Defence and Space GmbH 2014–2018 provided under COPERNICUS by the European Union and ESA; all rights reserved. © OpenStreetMap contributors. [Cite any training dataset you actually used, e.g. Kuro Siwo or Sen1Floods11.]
Built With
- copernicus-data-space
- copernicus-dem
- gdal
- geojson
- geopandas
- leaflet.js
- networkx
- numpy
- ohsome-api
- openstreetmap
- pyproj
- python
- rasterio
- react
- scipy
- sentinel-1
- shadcn-ui
- shapely
- tailwindcss
- tanstack-start
- typescript
- vite
- vitest
Log in or sign up for Devpost to join the conversation.