q16·intermediate

Where is surface water, and how is it changing over time?

hydrologywater-extentclimate Datasets: 5 20–60 min

Surface water is the open water you could see from a plane: lakes, rivers, reservoirs, flooded fields. A satellite can map which patches of ground are covered by water and which aren't. Do that over and over for years and you can watch a lake dry out, a reservoir fill, or a wetland swell and shrink with the seasons. This is the slow water-extent question, not the sudden storm-flood question (that's q13).

Where is surface water, and how is it changing over time?

Surface water is the open water you could see from a plane: lakes, rivers, reservoirs, flooded fields. A satellite can map which patches of ground are covered by water and which aren’t. Do that over and over for years and you can watch a lake dry out, a reservoir fill, or a wetland swell and shrink with the seasons. This is the slow water-extent question, not the sudden storm-flood question (that’s q13).

Which data to use, and how

Use this dataset: OPERA DSWx-S1 (short name OPERA_L3_DSWX-S1_V1), a ready-made surface-water map at 30 m made from radar. Radar is the easy starting point because it sees through clouds and works at night, so you get gap-free coverage and never have to wait for a clear sky. It runs from about 2023 onward. There’s also an optical version, OPERA DSWx-HLS, built from camera-like imagery. It’s useful as a second opinion, but it goes blind under clouds, so start with the radar one.

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

  1. Pick your water body. A small latitude/longitude box around the lake or basin, plus the years you care about.
  2. Turn each scene into a yes/no water map. The product hands you a band where each 30 m pixel is already labelled (1 = open water, 0 = not water, plus some “couldn’t tell” flags). Keep the water pixels.
  3. Count the water as area. Each water pixel is 30 m × 30 m, so add them up and convert to km². That’s the water extent for that date.
  4. Stack the dates into a time series and look at the line. A downward line means the water body is shrinking, upward means filling, and a repeating wave means seasonal wetland pulsing.

That’s the whole 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

  • Where the open water is right now. A 30 m, all-weather water map with no cloud gaps (from OPERA DSWx-S1, the radar product).
  • A cloud-aware optical version. Open water plus “partially wet” ground, from OPERA DSWx-HLS, good as a cross-check on clear days.
  • Multi-year change. An area-over-time chart that flags shrinking lakes or filling reservoirs.
  • Seasonal pulsing. Wet-season vs dry-season extent for wetlands and floodplains, thanks to the frequent radar revisits.
  • How today compares to “normal.” Join the long-term JRC/Pekel water-occurrence map (1984–present) to see what’s gained or lost against the historical baseline.
  • Radar vs optical agreement. Compare DSWx-S1 and DSWx-HLS on cloud-free dates to check they tell the same story.

What it can’t tell you

  • How much water there is (volume or depth). Extent is a flat 2-D footprint. It tells you the surface area, not how deep the water goes. A lake can keep the same area while quietly losing depth. For volume you need the area times a depth model, or a height-measuring satellite (SWOT/ICESat-2, see q06).
  • The water level / elevation. This is a water-or-not stamp, not a height ruler.
  • Why it changed. The map shows that a lake shrank, not whether it was irrigation, drought, or a dam. Pinning down the cause needs other data (GRACE-FO gravity, IMERG rainfall, land-use maps).
  • Water quality (murkiness, algae, salt). That’s a color/reflectance problem (see q14), not a water mask.
  • Anything smaller than 30 m. Narrow streams, small ponds, and field-scale ditches fall below the pixel size and get missed.
  • Daily flood peaks. For fast storm events use q13’s near-real-time radar workflow instead.

Gotchas to watch for

  • Radar measures roughness, not water itself. Smooth water bounces the radar signal away, so it looks dark, and that’s how DSWx-S1 finds it. But wind can roughen open water until it reads as land, and smooth wet soil or wet tarmac can fake water. Respect the product’s quality flags (cloud/no-data) instead of trusting every pixel.
  • The optical version goes blind under clouds. DSWx-HLS is camera-like, so persistent cloud means sparse coverage. Lean on the radar product for the continuous record and use the optical one only as an occasional cross-check.
  • The record is short. OPERA DSWx only starts around 2023. To go further back, backfill with Sentinel-1 raw radar (from 2014) or the JRC/Pekel baseline (from 1984).
  • 30 m blurs the shoreline. Pixels right on the water’s edge are part-water, part-land, so the reported area carries some shoreline uncertainty, and tiny channels and ponds simply vanish.
  • Steep terrain confuses radar. Mountains cast radar “shadows” and pile-ups (called layover) that can corrupt water detection. DSWx flags these, but read mountain lakes with extra care.
  • The historical baseline lives elsewhere. JRC/Pekel isn’t on NASA’s archive and sits on its own map grid, so you must reproject it onto the DSWx grid before comparing pixel-to-pixel.

The real-data code

The Run it cell above runs this method on synthetic data with no login. Below is the whole recipe against the real archive: search DSWx-S1, open each scene’s water-classification band, count the water pixels as km², stack the dates into a time series, fit the trend, and plot it. It needs a free Earthdata Login.

import earthaccess, numpy as np
import rasterio
from rasterio.windows import from_bounds
from datetime import datetime
from scipy import stats
import matplotlib.pyplot as plt

earthaccess.login(strategy="netrc")

# Aral Sea — the textbook shrinking-lake case, eastern basin nearly gone.
aoi = (60.5, 44.0, 62.5, 46.5)            # (W, S, E, N)
window = ("2023-04-01", "2025-09-30")

# 1. OPERA DSWx-S1 — radar surface water, all-weather, the workhorse here.
grans = earthaccess.search_data(
    short_name="OPERA_L3_DSWX-S1_V1", bounding_box=aoi, temporal=window)

# 2. For each granule, open the WTR (water classification) band — a 30 m COG —
#    crop to the AOI, and count water pixels. DSWx convention:
#      0 = not water, 1 = open water, 252..255 = fill / cloud / no-data masks.
def scene_extent_km2(gran):
    url = next(u for u in gran.data_links() if u.endswith("_B01_WTR.tif"))
    with rasterio.open(earthaccess.open([gran])[0]) as src:
        win = from_bounds(*aoi, transform=src.transform)
        wtr = src.read(1, window=win)
    water = (wtr == 1)                     # keep only confident open water
    return water.sum() * (30 * 30) / 1e6   # 30 m × 30 m pixels → km²

dates, areas = [], []
for g in grans:
    t = g["umm"]["TemporalExtent"]["RangeDateTime"]["BeginningDateTime"]
    try:
        areas.append(scene_extent_km2(g))
        dates.append(datetime.fromisoformat(t.replace("Z", "+00:00")))
    except Exception:
        continue                           # skip granules without the WTR band / read errors

order = np.argsort(dates)
dates = np.array(dates)[order]
areas = np.array(areas)[order]

# 3. Stack the dates into a time series and fit a linear trend through the extent.
t_years = np.array([(d - dates[0]).days / 365.25 for d in dates])
slope, intercept, r, p, se = stats.linregress(t_years, areas)
verb = "shrinking" if slope < 0 else "filling"
print(f"Surface water is {verb}: {slope:+.1f} km²/yr "
      f"(p={p:.3f}, latest {areas[-1]:.0f} km² on {dates[-1].date()})")

# 4. Plot the extent-over-time line with the fitted trend.
plt.plot(dates, areas, "o-", label="DSWx-S1 water extent")
plt.plot(dates, intercept + slope * t_years, "--", c="k", label="linear trend")
plt.ylabel("water area (km²)"); plt.xlabel("date")
plt.legend(); plt.tight_layout(); plt.show()

# Before you trust it: cross-check against the JRC/Pekel baseline (the long-term "normal"
# extent), and remember wind can roughen open water so DSWx reads it as land (see gotchas).

Expected output: an extent time series (water area in km² per date, radar as the backbone with optical points overlaid on clear days), a trend summary (net area change plus seasonal swing for wetlands), a change map (water-loss in red, water-gain in blue versus the JRC/Pekel baseline), and the latest all-weather water mask for your area.

Where the data comes from

Everything NASA-side comes through one free login (Earthdata Login), even though it’s spread across two archives. The radar products (OPERA DSWx-S1, Sentinel-1, RTC-S1) live at the ASF archive, and the optical ones (OPERA DSWx-HLS, HLS) live at the LP DAAC archive. The long-term JRC/Pekel baseline is a separate, external dataset you join in yourself (see r01 for cross-source patterns).

Sources

How a scientist answers this
Parameters
Binary open-water masks at 30 m from OPERA DSWx-S1 (radar, all-weather, from Sentinel-1) and OPERA DSWx-HLS (optical, cloud-limited), aggregated to water-surface area (km2) per date; the JRC/Pekel Global Surface Water occurrence layer (1984-present) supplies the long-term 'normal' extent and permanent-vs-seasonal baseline.
Method
Convert each water mask to an area time series (count water pixels times pixel area), then fit an area-weighted Theil-Sen + Mann-Kendall trend on annual or seasonal extent to flag shrinking lakes or filling reservoirs, and compare wet- vs dry-season composites against the JRC occurrence baseline to characterize seasonal pulsing; on cloud-free dates, cross-validate DSWx-S1 against DSWx-HLS.
Validation
Report radar-optical agreement on overlapping cloud-free dates and mask known artifacts (SAR layover/shadow and wind-roughened water; optical cloud/shadow and terrain shadow), and anchor results to the JRC/Pekel permanent-water baseline; note extent is a 2-D footprint, not volume or depth.
In plain EnglishMap where open water sits each date at 30 m using cloud-piercing radar plus optical, add up the wet area over months and years, and compare it to the long-term normal to see if water bodies are growing, shrinking, or just pulsing seasonally.

Make it yours → Edit the bounding box, date range, and wet/dry-season windows, and toggle radar-only versus radar+optical to trade cloud-immunity for cross-checking.

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

A safe place to practise the method on OPERA_L3_DSWX-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
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.