q13·intermediate

Was this region flooded after a recent storm?

hydrologydisaster-responsewater-extent Datasets: 6 1–6 hours (real disaster response timing)

Radar lets you see the ground from space even through thick storm clouds. The satellite sends down a microwave pulse and listens for the echo. Smooth, flat water sends almost nothing back, so it shows up dark. Rough land (trees, buildings, dry soil) bounces a lot back and shows up bright. A flood, then, is a new dark patch that wasn't there before the storm.

Was this region flooded after a recent storm?

Radar lets you see the ground from space even through thick storm clouds. The satellite sends down a microwave pulse and listens for the echo. Smooth, flat water sends almost nothing back, so it shows up dark. Rough land (trees, buildings, dry soil) bounces a lot back and shows up bright. A flood, then, is a new dark patch that wasn’t there before the storm.

Which data to use, and how

Use this dataset: OPERA RTC-S1 (short name OPERA_L2_RTC-S1_V1). It’s pre-processed, “analysis-ready” radar from the Sentinel-1 satellite. Someone has already done the hard cleanup steps, so you can open it and use it. The raw version (Sentinel-1 GRD) needs real radar expertise to handle, so skip it as a beginner. The reason to use radar at all is that it sees through clouds, and a storm covers everything in cloud exactly when you need to look.

Then do four steps (the runnable code further down does all of this; this is just the idea):

  1. Pick your region, a latitude/longitude box around the storm-hit area, and two time windows: a few days before the storm, and the first days after it.
  2. Grab a “before” and an “after” radar image of that box from those two windows.
  3. Find the dark (water) pixels in each. Radar brightness is measured in decibels (dB), a log scale where more-negative means darker. Rule of thumb: anything darker than about −20 dB is smooth water.
  4. Subtract before from after. A pixel that is water now (after < −20 dB) but was dry before (pre > −15 dB) is new water. That’s your flood. Add up those pixels to get flooded area, and optionally overlay GPM IMERG rainfall to show how much rain fell.

That’s the method. Below, “How a scientist answers this” names the exact tools, and the Run it cell does it live on synthetic data so you can see each step work.

What you can find out

  • How far the flood water spread, the flooded extent within 24–48 hours of the storm, because the radar sees straight through the cloud cover.
  • What’s new water vs normal water. Comparing before and after separates the flood from the rivers and lakes that are always there.
  • How much rain fell during the storm, by adding in IMERG rainfall data.
  • Whether the ground was already soaked before the storm (its “antecedent saturation”), which primes a region to flood, from the LIS soil-moisture model (or SMAP).
  • Which roads and buildings got hit, by laying the flood map over OpenStreetMap (OSM) road and building outlines.

What it can’t tell you

  • How deep the water is. Radar tells you water is there, not how many feet of it. For depth you’d combine the flood map with an elevation model (a DEM, a map of ground height) and a hydraulic model (software like HEC-RAS or DELWAQ that simulates how water flows).
  • Damage to individual buildings. The radar pixels are too coarse to judge house-by-house harm. That needs sharp commercial photos (companies like Planet or Maxar).
  • Flooding you can’t see from above. Basements, tunnels, anything underground is simply invisible to a satellite.

Gotchas to watch for

  • Radar sees roughness, not depth. It flags smooth surfaces as water (dB below −20). That’s usually right, but a smooth dry surface can fool it, and a rough surface won’t darken even when it’s underwater. So treat the map as “likely water,” not gospel.
  • Cities are the hard case. Buildings act like mirrored corners that bounce radar straight back (a “corner reflection”), so a flooded street between tall buildings can still look bright. Radar alone under-counts urban flooding, so pair it with other data there.
  • The satellite only passes every 12 days. With one Sentinel-1 satellite, your “after” image might land days off the flood’s peak, so you can miss the worst moment. Either accept the timing, or add faster commercial radar (ICEYE, Capella) if you need it.
  • Always compare to a “before” image. Rivers and lakes are smooth too, so without a baseline they’ll show up as “flood.” The before/after subtraction is what removes those permanent water bodies.
  • Start with the ready-made product. OPERA RTC-S1 is the recommended entry point precisely because the raw radar (GRD) requires SAR expertise to correct.

The real-data code

The Run it cell above runs the method on synthetic data (no login). Below is the whole recipe against the real archive: search, open the before/after radar, threshold the dark water, subtract to find new water, add up the IMERG rain, and plot it. It needs a free Earthdata Login.

import earthaccess, numpy as np, rasterio, xarray as xr
import matplotlib.pyplot as plt

earthaccess.login(strategy="netrc")

# Hurricane Helene impact in Asheville, NC, late September 2024
storm_aoi = (-83, 35, -82, 36)
pre_window  = ("2024-09-15", "2024-09-25")   # before the storm
post_window = ("2024-09-27", "2024-10-05")   # right after

# 1. OPERA RTC-S1 — analysis-ready Sentinel-1 (avoids raw GRD complexity).
#    Each granule ships one GeoTIFF per polarization; grab the VV file.
def vv_db(window):
    grans = earthaccess.search_data(short_name="OPERA_L2_RTC-S1_V1",
                                    bounding_box=storm_aoi, temporal=window)
    urls = [u for g in grans for u in g.data_links() if u.endswith("_VV.tif")]
    files = earthaccess.open(urls[:1])                  # one overlapping scene is enough
    with rasterio.open(files[0]) as src:
        g0 = src.read(1).astype("float32")             # linear backscatter (gamma-0)
    g0[g0 <= 0] = np.nan                                # mask no-data before the log
    return 10.0 * np.log10(g0)                          # convert to decibels (dB)

pre, post = vv_db(pre_window), vv_db(post_window)
h = min(pre.shape[0], post.shape[0]); w = min(pre.shape[1], post.shape[1])
pre, post = pre[:h, :w], post[:h, :w]                   # align overlapping footprints

# 2. Threshold + before/after subtraction: smooth water is radar-dark.
#    New water (the flood) = dark now AND not-dark before -> removes rivers/lakes.
new_water = (post < -20.0) & (pre > -15.0)
px_km2 = (10.0 * 10.0) / 1e6                            # RTC-S1 pixels are ~10 m
flood_km2 = float(np.nansum(new_water) * px_km2)

# 3. IMERG rainfall — how much rain actually fell over the storm window.
imerg = earthaccess.search_data(short_name="GPM_3IMERGHHL", bounding_box=storm_aoi,
                                temporal=("2024-09-25", "2024-09-28"))
ds = xr.open_mfdataset(earthaccess.open(imerg), group="Grid", combine="nested",
                       concat_dim="time")
rain = ds["precipitation"].sel(lon=slice(storm_aoi[0], storm_aoi[2]),
                               lat=slice(storm_aoi[1], storm_aoi[3]))
rain_mm = rain.sum("time") * 0.5                         # half-hourly mm/hr -> mm total
max_mm = float(rain_mm.max())

# 4. Verdict + plot: flood extent next to the rain that drove it.
print(f"Flooded (new water): {flood_km2:,.0f} km2; peak rainfall {max_mm:.0f} mm")
fig, (a1, a2) = plt.subplots(1, 2, figsize=(11, 5))
a1.imshow(post, cmap="gray", vmin=-25, vmax=0)
a1.imshow(np.where(new_water, 1, np.nan), cmap="cool", alpha=0.7)
a1.set_title(f"New water (flood): {flood_km2:,.0f} km2")
rain_mm.plot(ax=a2, cmap="Blues"); a2.set_title(f"IMERG rain, peak {max_mm:.0f} mm")
plt.tight_layout(); plt.show()

# Before you trust it: confirm against a "before" baseline (done here) so permanent
# rivers/lakes aren't counted, and remember cities under-count (corner reflection — see gotchas).

Where the data comes from

Everything comes from NASA through one free login (Earthdata Login), even though the pieces live in different NASA archives: the Sentinel-1 / OPERA radar is held at ASF, the IMERG rainfall at GES DISC, the LIS soil-moisture model at GHRC, and the optional HLS optical imagery at LP DAAC. You sign in once and reach all four.

Sources

How a scientist answers this
Parameters
Sentinel-1 SAR backscatter (VV polarization, dB) from OPERA RTC-S1 (analysis-ready, 30 m) or Sentinel-1 GRD; calm open water is radar-dark, so flood water falls below a low backscatter threshold (commonly around -17 to -20 dB VV, tuned per scene). Context comes from GPM IMERG storm-total precipitation (mm) and pre-storm LIS/SMAP soil moisture, with HLS L30/S30 optical only on cloud-free dates.
Method
Pre/post differencing: classify open water in matched pre- and post-storm SAR scenes by thresholding VV backscatter (or change-detection on the pre-minus-post dB difference), and keep only newly inundated pixels (water after, not before) to separate flood from permanent rivers and lakes; overlay IMERG rainfall and antecedent soil moisture for the saturation context.
Validation
Tune the dB threshold against the scene histogram and a known-dry/known-wet area, and mask SAR layover/shadow, urban double-bounce, and wind-roughened water (which mimic or hide flooding); cross-check cloud-free HLS NDWI or report the OPERA water-classification confidence layer where available.
In plain EnglishRadar sees through clouds and night, and flat flood water looks dark to it, so we compare a before-storm and after-storm radar image and flag the dark patches that are newly wet.

Make it yours → Set the storm bounding box and the before/after date windows, and adjust the VV backscatter (dB) water threshold for your terrain.

🧪 Virtual Mock Lab stand-in data · runs in your browser · no login

A safe place to practise the method on OPERA_L2_RTC-S1_V1. The code is the real earthaccess workflow, but we feed it stand-in data shaped like the real product — so you can run it, change the numbers, and break it without an account. When you want the real data, use the recipe under “The real-data code” above, or download the notebook below.

editable · runs in your browser

Datasets used

Recipe ready · not yet run The recipe and the Virtual Mock Lab above are ready to use. We haven't yet run this question end-to-end on real data, so it has no verified answer yet — only the questions we've actually computed carry one. That's the honest line between a method and a result.