Skip to content

About

Subpixel fire temperature from Tanager hyperspectral imagery — without a thermal band. Planck fitting across 154 SWIR bands, validated against VIIRS 2.5 min apart.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Subpixel fire temperature from Tanager — without a thermal band

Measuring the flaming-combustion structure that operational fire satellites average away.

Ren Dodge · Johns Hopkins University · Planet Tanager Open Data Competition, 2026

Headline result

Full write-up — results first, with method, uncertainty budget and limits in the appendices. The same document in three forms:

Tanager_Subpixel_Fire_Lightning_Case_Study_Ren_Dodge.pdf start here — figures embedded and captioned, previews in the browser
paper/Lightning_Case_Study.md the source, and it renders inline on GitHub with every figure — no download needed
paper/Lightning_Case_Study.docx Word version, rebuilt by reproduce.sh. GitHub cannot preview .docx; download it to read

The PDF is exported from the Word build rather than generated by reproduce.sh, so after editing the markdown, re-run the build and re-export it.


The result

Tanager-1 carries no thermal channel. It stops at 2.5 µm. Yet flaming combustion radiates enough at those wavelengths that a Planck curve can be fitted across 154 usable bands, recovering the temperature and fractional area of fires far smaller than a pixel.

On the Monroe Canyon Fire (Utah, 2025-07-24), that yields 148 subpixel fire detections at 845 ± 40 K, validated against a VIIRS overpass 2.5 minutes later.

The point is not that Tanager detects fires — VIIRS and GOES do that better, more often, and operationally. The point is what happens inside one of their pixels:

Within a single 639 × 783 m VIIRS footprint reporting one temperature and one fire radiative power, Tanager resolves up to 36 separate flaming fronts spanning 586 K and a 403× range in fractional area.

That is a measurement no operational thermal sensor can make at its own resolution, and it is the quantity fire-behavior and emissions models actually need.

Why it works

Temperature changes the shape of the emitted spectrum, not just its level. Between 600 K and 1200 K the ratio B(2.4 µm)/B(1.6 µm) swings from 19.5 to 1.6. Classic two-band methods (Dozier 1981) have exactly two equations for two unknowns and no residual left to check. With 154 bands the system is heavily overdetermined, so fit quality itself becomes evidence — and a physical discriminator separates real thermal emission from bright-surface false positives:

ΔL(2400)/ΔL(1600)
thermal emission 3.7 – 9.9 (matches Planck for 700–900 K)
reflected sunlight 0.46 – 0.48 (declining spectrum)

0 of 300 randomly sampled non-fire pixels survive the acceptance test.

Validation

check result
VIIRS/SNPP fire pixels in footprint 6 of 6 contain Tanager detections
Tanager detections within 600 m of a VIIRS pixel (either overpass) 85%
Detections strictly inside an SNPP footprint 45% (stricter test, one sensor)
Orphan detections statistically different? No — same shape ratio (p=0.83) and temperature (p=0.89)
FRP, Tanager vs VIIRS/SNPP 115 MW vs 181 MW, same order

The 85% and 45% figures answer different questions — proximity to either sensor versus strict containment in one — and are not in conflict.

GOES-18, scanning every 5 minutes, shows why the comparison is favorable here: at Tanager's moment the fire was detected but not quantifiable by GOES, which reported no FRP at all until 19:41 UTC, then ramped to 1022 MW by 20:16. Tanager caught it ~30 minutes before the afternoon intensification.

What this method cannot do

Stated plainly, because it matters more than the result:

  • Blind to smoldering. Smoldering is flameless combustion, ~500–700 K, in duff and organic soil; it persists for weeks and produces the CO, CH₄ and PM₂.₅ that dominate smoke-health impact. A 600 K source needs to occupy ~10⁻¹ of a pixel before a second thermal component becomes recoverable, versus ~10⁻³ for flaming. Synthetic recovery testing (fire_two_component.py) confirms the split is not retrievable at this noise level — the F-test "detects" a second component in noise-only data. This is the quantitative case for VSWIR–thermal fusion.
  • Emission-weighted. A one-component fit returns an effective temperature biased toward the hottest material present. There is no single "true" temperature.
  • One instant, one scene. 8,201 m² of flaming area in a fire that reached 298 km² — a thin active perimeter, not a characterization of the fire.
  • A rejected hypothesis is included, not hidden. smoke_bias_*.py document a smoke/cloud-mask investigation that was tested and failed; the scene's aerosol field has no local variance, making the comparison unidentifiable.

Quickstart

git clone https://github.com/rendodge/tanager-subpixel-fire.git
cd tanager-subpixel-fire
python3 -m venv .venv && .venv/bin/python -m pip install -r requirements.txt
.venv/bin/python src/fetch_data.py     # ~5.4 GB; see note on Earthdata below
bash reproduce.sh

requirements.txt is the direct dependency list; requirements-lock.txt holds the exact 64-package resolution of the environment these figures were produced in (macOS x86_64, CPython 3.10.8) — install from it to reproduce byte-for-byte. matplotlib must stay ≥3.9 — older builds were compiled against the NumPy 1.x ABI and fail under NumPy 2.x. Do not work around it by pinning numpy back.

VIIRS needs a free NASA Earthdata Login. Register at https://urs.earthdata.nasa.gov/users/new, then create ~/.netrc:

machine urs.earthdata.nasa.gov login YOUR_USERNAME password YOUR_PASSWORD

and chmod 600 ~/.netrc. Tanager and GOES need no credentials.

Pipeline

script does key output
fetch_data.py downloads Tanager, GOES-18, VIIRS data/
fire_qc.py cloud / smoke / active-fire triage *_fireqc.png
fire_temperature.py the retrieval — Planck fit, 154 bands *_firetemp.csv
fire_two_component.py can flaming and smoldering be separated? (no) two_component_test.csv
uncertainty_budget.py error budget by re-fitting under perturbations uncertainty_budget.csv
goes_fire_timeseries.py GOES-18 FRP context, 5-min cadence goes18_*_frp.csv
viirs_tanager_comparison.py subpixel structure inside true VIIRS footprints headline figure
make_fire_figures.py scene context, method, validation figures figures/
build_case_study_docx.py renders the case study to Word with figures embedded paper/*.docx
burn_severity.py NBR + hyperspectral char/ash classes, absolute thresholds *_burn_severity.csv
smoke_bias_spectrum.py superseded — documented negative result —
smoke_bias_radiance.py the corrected version of the same test; also negative —

Every figure is regenerable from the script named in its docstring.

Post-fire: what a two-band burn index misses

Forty days later the fire is out, and the same pipeline maps the scar (20250902_190116_02, Sevier frame). A second September frame over Piute County tiles the eastern part of the scar but is not reported: it overlaps Sevier by ~45 km² so the two cannot be summed, 32% of it is cloud, and it returns the same answers.

Burned area, from basic_sr with per-pixel ground areas computed from the granule's own geolocation:

frame day valid (non-cloud) burned (NBR < 0) char ash
07-24 (active fire) 11 359.5 km² 84.0 km² (23.4%) 0.63 km² (0.17%) 65.4 km²
09-02 (near-final scar) 51 452.4 km² 118.8 km² (26.3%) 23.44 km² (5.18%) 60.0 km²

Char is time-dependent. Dark char is essentially absent while the fire still burns (0.17% of valid) and abundant forty days later (5.18%) — a factor of 30. At day 11 the entire SWIR-brightness distribution of burned pixels sits above the char threshold (p10 = 0.147, only 0.8% below); by day 51, 20.1% falls below. Smoke contributes (median SWIR brightness rises 0.190 → 0.226 across the AOD range, r = 0.165) but that ~0.036 shift cannot account for a ~0.10 gap. The straightforward reading is that fresh burn is dominated by pale ash and that ash is stripped over the following weeks, exposing char — an observation from two epochs of one fire, not a demonstrated mechanism.

Footprints differ and 22% of the September frame is cloud, so the two epochs are not directly comparable and cannot be read as scar growth without co-registration. Against the fire's official 298 km² final extent the totals are the right order.

The hyperspectral point. Char and ash have nearly identical NDVI (0.236 vs 0.236 on the Sevier frame) and NBR only 0.056 apart — but their SWIR brightness differs 5× (0.044 vs 0.218). Char is dark and spectrally flat; ash is the brightest material in the scene beyond 1.5 µm, brighter than green vegetation. A two-band index cannot separate them; 368 bands separate them trivially — NDVI and NBR are carried here as the control, to show what the standard products would report. The distinction matters because char and ash record different combustion completeness, carbon loss and erosion behavior, and because combustion completeness is what physics-based fire models predict per cell and currently cannot be scored against.

Two methodological guards, enforced in burn_severity.py:

  • dNBR severity thresholds are NOT applied. The familiar 0.10/0.27/0.44/0.66 breaks are calibrated for the differenced index. No pre-fire Tanager scene of this fire exists — verified across all 276 STAC items — so dNBR is impossible, and applying its thresholds to single-date NBR would be wrong.
  • Classes use absolute, physical thresholds, not percentiles. An earlier version binned by NBR percentile; that is self-fulfilling — every scene returns ~30/40/30 regardless of what burned, and the "areas" are not measurements. Burned is now NBR < 0 (R2200 > R860, the post-fire spectral reversal), with char and ash split on absolute SWIR brightness. The two epochs now return genuinely different numbers — 23.4% vs 26.3% burned, char fractions a factor of 30 apart — instead of the same ~30/40/30 split a percentile binning returns for anything.
  • Areas come from basic_sr, never ortho. The ortho grid holds 842,764 cells for 427,328 real samples on the Sevier frame — 1.97× duplication, versus 1.31× on the 07-24 frame.

No active fire remains — the real negative control. 0 of 300 random non-fire pixels is a weak test; random pixels are dark vegetation and bare soil that nothing would confuse with fire. The hard test is a whole scene of exactly what should fool a brightness threshold: bright, SWIR-dominant burn-scar pixels on ground that burned six weeks earlier. Running the retrieval on the 09-02 Sevier frame accepts 0 of 55 candidates; every one sits in the reflected-solar regime (shape ratio ≤ 0.76, never crossing the 1.5 threshold). Two VIIRS overpasses that day independently report zero fire pixels inside the frame while detecting 302 and 204 fires elsewhere in the same granules.

Notes on the data

Tanager ships basic (swath) and ortho (gridded) products. Ortho products are nearest-neighbor resampled — measured here at 1.31 ortho cells per real swath sample — which duplicates and drops spectra and breaks the sample statistics this analysis depends on. All measurements use the basic radiance swath. Ortho is used only to draw basemaps.

Bands flagged good_wavelengths == 0 (the 1342–1438 and 1783–1967 nm water-vapor blocks, 58 of 426) are excluded throughout.

Data sources and attribution

  • Tanager-1 — Planet Open Data STAC, CC-BY-4.0. Redistribution of Tanager data or derived material must carry: "Tanager STAC Data, available at www.planet.com/data/stac © 2025 Planet Labs PBC. All Rights Reserved" — prefaced with "Adapted from…" for adapted material.
  • VIIRS VNP14IMG / VJ114IMG / VNP21 — NASA LP DAAC.
  • GOES-18 ABI L2 FDCC — NOAA, public S3.

Related

The Fiji/ImageJ plugin toolkit for opening and exploring Tanager scenes lives in a separate repository: tanager-hyperspectral-fiji.

License

Code: PolyForm Noncommercial 1.0.0 (SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0, see LICENSE).

Free to use, modify and redistribute for any noncommercial purpose — the license names research, education, public research organizations, environmental protection organizations and government institutions explicitly. Commercial use, including sale, requires a separate license: rendodge@gmail.com.

Data is not covered by that license. Tanager imagery remains CC-BY-4.0 from Planet Labs PBC and is unrestricted by these terms — see LICENSE for the required attribution string.

About

Subpixel fire temperature from Tanager hyperspectral imagery — without a thermal band. Planck fitting across 154 SWIR bands, validated against VIIRS 2.5 min apart.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages