Is a drought quietly building across my region before the harvest fails?
Draw a rectangle, then check which collections cover it. Most NASA data is global, so a big box matches a lot.
35, 2 → 42, 8 (Horn of Africa (S. Ethiopia / N. Kenya))Soil moisture is how much water is sitting in the dirt that plant roots drink from. When it drops below normal, crops are in trouble long before the fields *look* dry from a car or a photo. They can still be green while the soil underneath has quietly run out. The trick is to catch that hidden shortfall early. In places like the Sahel and the Horn of Africa, a few weeks of warning is the difference between a managed shortfall and a famine that displaces millions.
Is a drought quietly building across my region before the harvest fails?
Soil moisture is how much water is sitting in the dirt that plant roots drink from. When it drops below normal, crops are in trouble long before the fields look dry from a car or a photo. They can still be green while the soil underneath has quietly run out. The trick is to catch that hidden shortfall early. In places like the Sahel and the Horn of Africa, a few weeks of warning is the difference between a managed shortfall and a famine that displaces millions.
Which data to use, and how
Use this dataset: SMAP soil moisture (short name SPL3SMP_E),
a NASA satellite that measures how wet the soil is every day at ~9 km resolution, from 2015 → today.
It’s the easiest one here to read as a single “how dry is it right now” number, and it updates within
about 2–3 days. Later you can add FLDAS (FLDAS_NOAH01_C_GL_MA)
to compare today against decades of history, and ECOSTRESS (ECO_L4T_ESI)
to confirm the plants themselves are stressed.
Then do four steps (the runnable code further down does all of this; this is just the idea):
- Pick your region. A small latitude/longitude box over the cropland you care about, plus the months that lead up to the harvest (the growing season).
- One number per day. Average the SMAP soil moisture inside your box for each day, giving a daily “how wet is the soil” time series for the season.
- Compare to normal. Pull FLDAS, which has soil moisture going back to the 1980s, and work out how far below the usual amount for this time of year you are (an anomaly: today minus the long-term average, in standard-deviation units, i.e. “how unusual is this?”).
- Confirm the crops feel it. Check ECOSTRESS, which measures whether plants have stopped “sweating” (transpiring). A reading near 0 means they’ve shut their pores to save water, a direct early sign of crop failure. If soil water is far below normal and falling and plants are stressed, a drought is building.
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
- Whether the soil is drier than usual for this time of year. The core famine-early-warning signal (FLDAS soil-moisture anomaly vs the long 1982–present record).
- How dry the soil is right now, day by day (SMAP, updated within ~2–3 days of the satellite pass).
- Whether the crops are already under water stress (ECOSTRESS Evaporative Stress Index; near 0 means plants have closed up and stopped transpiring, an early warning of failure).
- Whether the dryness is everywhere or just in patches. Compare the coarse model map (FLDAS, 0.1°) with the fine ECOSTRESS map (~70 m) over the same area.
- How much water the soil can even hold, the drought buffer. For US farmland, USDA SSURGO tells you the plant-available water in the root zone, so the same rainfall shortfall is far more dangerous on a thin, sandy soil than on a deep silt loam. Outside the US, ISRIC SoilGrids gives the same idea at 250 m.
- (Africa) Where the cropland actually is, and whether water bodies are shrinking. Digital Earth Africa’s crop mask focuses the drought signal on real fields, and its Water Observations from Space (WOfS) shows reservoirs and seasonal wetlands drying up year over year. Free, no login, a natural fit for the Horn-of-Africa default here.
What it can’t tell you
- The final yield in tonnes. These measure stress on the plants and soil, not the harvest itself. For an actual yield estimate you’d join a dedicated crop model (e.g. FEWS NET / WRSI).
- What caused the dryness. Failed rains, irrigation being switched off, or a heatwave all look similar here. Telling them apart needs rainfall data (IMERG) plus maps of where land is irrigated.
- A daily, sharp picture everywhere. ECOSTRESS is high-resolution but rides on the Space Station, so it passes over on an irregular schedule. You get sharp snapshots, not a smooth daily 70 m movie.
- What the rains will do next. None of these forecast the future; they describe the current and recent state. Pair them with a seasonal weather forecast for an outlook.
Gotchas to watch for
- SMAP only senses the top ~5 cm of soil. It feels the surface, not the deep layer where roots actually drink, so a dry surface isn’t always a dry root zone and SMAP on its own can over- or under-react. Use it as the fast daily pulse, then lean on FLDAS’s layered soil moisture for the deeper, root-zone picture.
- FLDAS is a model, not a direct measurement. It’s a simulation fed by rainfall estimates (CHIRPS) and weather data, so it’s only as good as those inputs, and it gets shakier in regions with few rain gauges. Trust it most where rainfall data is dense, and cross-check it against the real SMAP measurements.
- ECOSTRESS has gaps. Its revisit is irregular and clouds block its heat sensor, so you’ll miss scenes, especially during the rainy season. Remember that “no data” is not “no stress”: treat ECOSTRESS as confirmation when you happen to have a clear scene, not as continuous coverage.
- “Below normal” depends on which years you call normal. A drought anomaly is measured against a baseline period (a climatology), and a different baseline gives a different answer. It’s easy to overstate or understate a drought by quietly picking a wet or dry baseline, so always state your baseline window out loud (here, 1991–2020).
The real-data code
The Run it cell above runs the method on synthetic data with no login. Below is the whole recipe against the real archive: search SMAP + FLDAS + ECOSTRESS, open each format, build the daily soil pulse, score the anomaly against the 1991–2020 baseline, confirm with crop stress, then plot it. It needs a free Earthdata Login.
import earthaccess, numpy as np, xarray as xr, rasterio
import matplotlib.pyplot as plt
earthaccess.login(strategy="netrc")
aoi = (35.0, 2.0, 42.0, 8.0) # W, S, E, N — Horn of Africa (S. Ethiopia / N. Kenya)
season = ("2024-03-01", "2024-06-30") # long-rains growing season, up to harvest
# 1. SMAP enhanced daily soil moisture (9km HDF5) — the "how dry right now" pulse.
grans = earthaccess.search_data(short_name="SPL3SMP_E", bounding_box=aoi, temporal=season)
daily = []
for fh in earthaccess.open(grans):
g = xr.open_dataset(fh, group="Soil_Moisture_Retrieval_Data_AM", engine="h5netcdf")
sm = g["soil_moisture"].where(g["soil_moisture"] > 0) # -9999 fill -> NaN
lat, lon = g["latitude"], g["longitude"]
box = (lon >= aoi[0]) & (lon <= aoi[2]) & (lat >= aoi[1]) & (lat <= aoi[3])
daily.append(float(sm.where(box).mean()))
smap_series = np.array(daily)
print(f"SMAP top-5cm soil moisture: {np.nanmean(smap_series):.3f} m3/m3 mean over season")
# 2. FLDAS land model (monthly 0.1° netCDF): current season vs 1991-2020 same-months baseline.
def fldas_mean(temporal):
gg = earthaccess.search_data(short_name="FLDAS_NOAH01_C_GL_MA",
bounding_box=aoi, temporal=temporal)
vals = []
for fh in earthaccess.open(gg):
d = xr.open_dataset(fh)
sub = d["SoilMoi00_10cm_tavg"].sel(X=slice(aoi[0], aoi[2]), Y=slice(aoi[1], aoi[3]))
vals.append(float(sub.mean()))
return np.array(vals)
now = fldas_mean(season) # Mar-Jun 2024
clim = np.concatenate([fldas_mean((f"{y}-03-01", f"{y}-06-30")) for y in range(1991, 2021)])
anomaly = (now.mean() - clim.mean()) / clim.std() # standard-deviation units
print(f"FLDAS root-zone anomaly: {anomaly:+.2f} sigma vs 1991-2020 "
f"({'below normal' if anomaly < 0 else 'above normal'})")
# 3. ECOSTRESS ESI (~70m GeoTIFF) — confirm the plants themselves are shutting their pores.
esi_grans = earthaccess.search_data(short_name="ECO_L4T_ESI", bounding_box=aoi, temporal=season)
with rasterio.open(earthaccess.open(esi_grans[:1])[0]) as src:
esi = src.read(1).astype("float32")
esi[esi == src.nodata] = np.nan
esi_med = np.nanmedian(esi) # 0 = severe stress, ~1 = unstressed
print(f"ECOSTRESS ESI median: {esi_med:.2f} ({'crops stressed' if esi_med < 0.5 else 'crops ok'})")
# 4. Verdict: drought building if soil far below normal AND falling AND crops stressed.
slope = np.polyfit(np.arange(len(smap_series)), np.nan_to_num(smap_series, nan=np.nanmean(smap_series)), 1)[0]
building = anomaly < -1 and slope < 0 and esi_med < 0.5
print(f"VERDICT: drought {'BUILDING' if building else 'not clearly building'} "
f"(anomaly {anomaly:+.2f}sigma, SMAP slope {slope:+.1e}/day, ESI {esi_med:.2f})")
# 5. Plot the daily SMAP pulse with the FLDAS-anomaly verdict annotated.
plt.plot(smap_series, marker=".")
plt.title(f"SMAP soil moisture, Horn of Africa — FLDAS anomaly {anomaly:+.2f} sigma")
plt.ylabel("soil moisture (m3/m3)"); plt.xlabel("day of season")
plt.tight_layout(); plt.show()
# Before you trust it: re-run with a stated baseline window (here 1991-2020) — a wetter or drier
# baseline flips "below normal", and SMAP only sees the top ~5cm, so lean on FLDAS for the root zone.
Where the data comes from
This pulls from three different NASA data centers at once, but you only need one free login
(Earthdata Login, used by the earthaccess library): FLDAS comes from GES DISC, SMAP from the NSIDC
DAAC, and ECOSTRESS from the LP DAAC. The catch is the file formats differ — FLDAS and SMAP arrive as
gridded HDF5/NetCDF, while ECOSTRESS ships as tiled GeoTIFF images. Before you can compare the fine
ECOSTRESS map against the coarse FLDAS map, you have to put them on the same grid (regrid one onto the
other). The optional extras (USDA SSURGO for US soils, ISRIC SoilGrids for global soils, and Digital Earth
Africa for African cropland and water) are all free and need no login at all. See
recipes/r01-three-daac-composition.mdx for the general pattern.
Sources + further reading
- FEWS NET (Famine Early Warning Systems Network): https://fews.net/
- NASA SMAP mission + SPL3SMP_E product: https://nsidc.org/data/spl3smp_e
- FLDAS at GES DISC (FLDAS_NOAH01_C_GL_MA): https://disc.gsfc.nasa.gov/guides/FLDAS_NOAH01_C_GL_MA_001/summary
- ECOSTRESS L4 ESI (ECO_L4T_ESI): https://lpdaac.usgs.gov/products/eco_l4t_esiv002/
- FLDAS background (NASA / FEWS NET land data assimilation): https://ldas.gsfc.nasa.gov/fldas
- USDA Soil Data Access (SSURGO, free, no login): https://sdmdataaccess.sc.egov.usda.gov/
- ISRIC SoilGrids (global 250 m soil properties): https://soilgrids.org/
- Digital Earth Africa (open STAC: WOfS, crop mask): https://www.digitalearthafrica.org/ · https://explorer.digitalearth.africa/stac
Make it yours → Set the region, the months, the climatology baseline, and the anomaly/ESI thresholds, and switch SSURGO (US) for SoilGrids (global) when picking the soil-buffer layer.
A safe place to practise the method on SPL3SMP_E. 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.