← back to the map · model performance · about
Everything the interactive map shows is downloadable here for research use: the gridded 72-hour forecast fields, the site's own compact binary format, and the observation-vs-forecast verification statistics. Please see the citation and the as-is disclaimer at the bottom.
Statistics are computed by pairing AirNow observations with the archived forecast that covered each day; methodology on the verification page.
One file per pollutant per cycle. 72 bands = 72 forecast hours; each band description is the valid time in Pacific time. float32 EPSG:3857 NaN = nodata Units: µg/m³ (PM2.5), ppb (ozone).
import rasterio
ds = rasterio.open("pm25_20260712.tif")
pm = ds.read(1) # band N = forecast hour N-1
print(ds.descriptions[0]) # e.g. "2026-07-12 05:00 PDT"
print(ds.crs, ds.bounds)
The viewer's own format — much smaller than the GeoTIFFs and available for every archived cycle. Each cycle directory has a manifest.json describing the grid plus gzipped .bin files:
| File | Layout |
|---|---|
| pm25_<cycle>.bin.gz o3_<cycle>.bin.gz |
uint16 little-endian, hour-major, rows N→S, cols W→E. Value = stored/scale (per-species scale in the manifest). 65535 = nodata. |
| daily_<cycle>.bin.gz | day-major, then [24-h PM2.5 mean, ozone MDA8] per day; same scaling; day list in manifest.daily. |
import gzip, json, numpy as np, urllib.request as rq base = "https://nw-air-forecast.pages.dev/data/20260712/" man = json.load(rq.urlopen(base + "manifest.json")) H, W = man["grid"]["height"], man["grid"]["width"] sp = man["species"]["pm25"] raw = gzip.decompress(rq.urlopen(base + sp["bin"] + ".gz").read()) a = np.frombuffer(raw, "<u2").reshape(-1, H, W).astype(float) a[a == 65535] = np.nan pm = a / sp["scale"] # (72, H, W) in µg/m³; hours in man["hours"]
AIRPACT-6 air quality forecast (Northwest Air Quality Forecast visualization), Washington State University, https://nw-air-forecast.pages.dev/ (cycle YYYYMMDD, accessed YYYY-MM-DD).
This is a research forecast provided as-is, without warranty; fields are model output, not observations. Bulk or automated downloading of many cycles is fine — files are served from a CDN — but please open a GitHub issue if you need a different format or historical data beyond what is listed here.