q32·advanced

After the storm, which neighborhoods lost power — and who lives there?

disasterspublic-healthhuman-dimensions Datasets: 3 30–60 min
Find the data for your area

Draw a rectangle to pick your area of interest, then see what NASA data covers it (live, here in your browser) or download a ready-to-run notebook with your AOI pre-filled. The notebook runs in any Python environment — it needs a free Earthdata Login to fetch the data.

Current AOI: -82.8, 27.6 → -82.2, 28.1 (Tampa Bay, Florida (Hurricane Milton, Oct 2024))

When a hurricane knocks out the grid, the city goes dark — and from orbit you can actually see it. NASA's **Black Marble** product (`VNP46A2`) turns the VIIRS day/night band into a daily, gap-filled, moonlight-corrected map of how much light each square kilometre is throwing off at night. Compare a clear night *before* landfall to a night *after*, and the blackout shows up as a plunge in [radiance](/glossary/radiance/). Lay a free population grid on top, and you can start to ask the human question: not just *where* the lights went out, but roughly *how many people* were sitting in the dark. **Verified locally.** Over Tampa Bay (27.6–28.1 °N), the `VNP46A2` field `Gap_Filled_DNB_BRDF-Corrected_NTL` averaged **19.18 nW/cm²/sr** on the clear night of 8 Oct 2024 — the eve of Hurricane Milton's landfall — and fell to **9.35 nW/cm²/sr** on the night of 11 Oct, a **51% drop** in mean radiance across the AOI. The lights then crept back (13.8 on the 12th, 15.3 on the 13th) as crews restored power. That recovery curve is the grid coming back online, written in light.

After the storm, which neighborhoods lost power — and who lives there?

When a hurricane knocks out the grid, the city goes dark — and from orbit you can actually see it. NASA’s Black Marble product (VNP46A2) turns the VIIRS day/night band into a daily, gap-filled, moonlight-corrected map of how much light each square kilometre is throwing off at night. Compare a clear night before landfall to a night after, and the blackout shows up as a plunge in radiance. Lay a free population grid on top, and you can start to ask the human question: not just where the lights went out, but roughly how many people were sitting in the dark.

Verified locally. Over Tampa Bay (27.6–28.1 °N), the VNP46A2 field Gap_Filled_DNB_BRDF-Corrected_NTL averaged 19.18 nW/cm²/sr on the clear night of 8 Oct 2024 — the eve of Hurricane Milton’s landfall — and fell to 9.35 nW/cm²/sr on the night of 11 Oct, a 51% drop in mean radiance across the AOI. The lights then crept back (13.8 on the 12th, 15.3 on the 13th) as crews restored power. That recovery curve is the grid coming back online, written in light.

What you can answer

  • How much darker a city got after the storm — compare mean Gap_Filled_DNB_BRDF-Corrected_NTL over your bounding box, a clear night before vs. a night after (units: nW/cm²/sr)
  • Which neighborhoods went dark — the grid is ~500 m, so you can map the per-pixel drop and see which districts lost the most light
  • How fast the grid recovered — track the AOI mean night by night and watch the radiance climb back toward its pre-storm baseline
  • Roughly how many people were in the dark zone — overlay WorldPop 1 km population on the pixels that lost light, and sum the population underneath
  • Which counties to name — clip your AOI against geoBoundaries ADM2 so you can say “Pinellas and Hillsborough,” not just “a bright blob”
  • A before/after story for any storm since 2012 — VIIRS has flown nightly since then, so the same recipe works for past hurricanes, ice storms, or wildfire evacuations

What you can NOT answer with these datasets alone

  • Confirmed power outages — a darker pixel is consistent with a blackout, but clouds, smoke, storm surge, and evacuations also dim a city. Cross-check utility outage maps before claiming a grid failure.
  • Who, exactly, lost power — WorldPop is a modeled ~1 km population estimate, not a customer list; it tells you the order of magnitude of people affected, not addresses or households
  • The cause of any one dark patch — the gap-filled product blends nights to fill clouds, so a single dark pixel can reflect missing data, not a real outage. Check the quality flags.
  • Economic damage or injuries — radiance is light, not dollars or lives; impact assessment needs ground reporting, insurance data, and damage surveys
  • Indoor vs. outdoor light — VIIRS sees streetlights, signage, and spill from windows together; it can’t separate “houses dark” from “streetlights off”
  • Demographic equity on its own — WorldPop gives counts, not income, age, or vulnerability; pair it with census data to ask who was least able to recover

Code template (Python, cloud-direct)

Verified locally. VNP46A2 is a daily HDF5 tile on a 2400×2400 grid. The brightness lives at HDFEOS/GRIDS/VIIRS_Grid_DNB_2d/Data Fields/Gap_Filled_DNB_BRDF-Corrected_NTL, with lat/lon arrays in the same group so you can subset to your bounding box before averaging. Read the real _FillValue and scale_factor from the dataset attributes — on the granules I checked the fill was -999.9 and the scale was 1.0, with units nW/cm²/sr.

import os, re, warnings, collections
warnings.filterwarnings("ignore")
import earthaccess, h5py, numpy as np

# load Earthdata creds from .env without `source` (passwords can break the shell)
for line in open(".env"):
    m = re.match(r'\s*(?:export\s+)?([A-Z0-9_]+)\s*=\s*(.*)\s*$', line)
    if m: os.environ.setdefault(m.group(1), m.group(2).strip().strip('"').strip("'"))
earthaccess.login(strategy="environment")   # free Earthdata Login

W, S, E, N = -82.8, 27.6, -82.2, 28.1       # Tampa Bay (Hurricane Milton)
GRP  = 'HDFEOS/GRIDS/VIIRS_Grid_DNB_2d/Data Fields'
NTL  = f'{GRP}/Gap_Filled_DNB_BRDF-Corrected_NTL'

def aoi_mean(granule):
    f  = earthaccess.open([granule])[0]
    h  = h5py.File(f, 'r')
    ds = h[NTL]
    lat, lon = h[f'{GRP}/lat'][:], h[f'{GRP}/lon'][:]
    fill  = float(np.array(ds.attrs.get('_FillValue', -999.9)).ravel()[0])
    scale = float(np.array(ds.attrs.get('scale_factor', 1.0)).ravel()[0])
    iy = np.where((lat >= S) & (lat <= N))[0]
    ix = np.where((lon >= W) & (lon <= E))[0]
    sub = ds[iy.min():iy.max()+1, ix.min():ix.max()+1].astype('float64')
    sub[sub == fill] = np.nan
    return np.nanmean(sub * scale)

res = earthaccess.search_data(short_name="VNP46A2",
        temporal=("2024-10-05", "2024-10-13"),
        bounding_box=(W, S, E, N))

by_date = collections.OrderedDict()
for g in res:
    d = g['umm']['TemporalExtent']['RangeDateTime']['BeginningDateTime'][:10]
    by_date.setdefault(d, []).append(g)

for d, gs in by_date.items():
    print(d, round(aoi_mean(gs[0]), 3), "nW/cm2/sr")
# 2024-10-08 -> 19.18  (clear night before landfall)
# 2024-10-11 ->  9.35  (after Milton)  == ~51% drop in mean radiance

# next: keep the dark pixels (big per-pixel drop), reproject WorldPop wpic1km
# onto them, and sum population to estimate how many people went dark.
How a scientist answers this
Parameters
Daily gap-filled, BRDF-corrected VIIRS nighttime radiance from Black Marble (VNP46A2 `Gap_Filled_DNB_BRDF-Corrected_NTL`, ~500 m, nW/cm²/sr), with the per-pixel quality/mandatory-flag layer, plus WorldPop 1 km population and geoBoundaries ADM2 to name counties. Compare a clear pre-landfall night to clear post-landfall nights.
Method
Composite clear-sky, moonlight-corrected radiance over the AOI for a before night and one or more after nights, compute the per-pixel and AOI-mean percent drop in radiance as the blackout signal, then sum WorldPop population within pixels that went dark to estimate people affected; track recovery by repeating across subsequent nights.
Validation
Use the QA/cloud flags to exclude cloud- and snow-contaminated pixels (a major confound after storms), pick truly clear nights to avoid attributing cloud cover to outages, and cross-check the outage footprint against utility/EAGLE-I or news reports where available.
In plain EnglishCompare how bright a city was on a clear night before the storm to clear nights after; the drop in light marks the blackout, and a population grid says roughly how many people sat in the dark.

Make it yours → Set the bounding box, the before/after night dates, and the radiance-drop threshold in the notebook to match your storm.

Run the core method · no login

The Before-After-Control-Impact vs a noise floor at the heart of this question — runnable on synthetic data, right here. The full earthaccess code template further down does it on real NASA data (needs an Earthdata login).

editable · runs in your browser