GOES-R ABI — download a CONUS granule (live)¶
This notebook fetches a real GOES-East ABI granule from the anonymous
noaa-goes19 bucket via the earthlens.goes
backend, then decodes one infrared band with
pyramids and maps it.
earthlens.goes ships the raw NetCDF and does not decode it — reading and
reprojecting the geostationary grid is pyramids' job downstream. This notebook
shows that hand-off end to end.
Setup¶
Imports and a notebook-relative output directory. GOES CONUS imagery updates every 5 minutes with a short latency, so we pick a window a few hours in the past to be sure the granules are already published.
from pathlib import Path
import numpy as np
import pandas as pd
from earthlens.core import EarthLens
out_dir = Path("goes_out")
out_dir.mkdir(exist_ok=True)
now = pd.Timestamp.utcnow().tz_localize(None).floor("min")
start = now - pd.Timedelta(hours=3)
end = start + pd.Timedelta(minutes=6) # one CONUS scan (5-min cadence)
start, end
Download¶
EarthLens("goes", ...).download() lists the hour prefix, keeps the granules
whose scan-start lands in the window, and downloads them whole. It returns the
list[pathlib.Path] of raw NetCDF files.
lens = EarthLens(
"goes",
dataset="abi-l2-mcmip", # Cloud & Moisture Imagery (16 bands)
satellite="east", # -> noaa-goes19
domain="C", # CONUS
start=start.strftime("%Y-%m-%d %H:%M"),
end=end.strftime("%Y-%m-%d %H:%M"),
fmt="%Y-%m-%d %H:%M",
lat_lim=[20, 50],
lon_lim=[-130, -60],
path=out_dir,
)
paths = lens.download()
[p.name for p in paths]
The filename encodes everything: OR_ABI-L2-MCMIPC-M6_G19_s…_e…_c….nc — the
product (MCMIPC), the scan mode (M6), the satellite (G19), and the scan
start / end / created timestamps. One CONUS file (~58 MB) carries
all 16 ABI bands.
granule = paths[0]
print("granule :", granule.name)
print("size :", round(granule.stat().st_size / 1e6, 1), "MB")
Decode a band with pyramids¶
The granule is raw geostationary NetCDF. pyramids georeferences the ABI
scan-angle grid from the CF goes_imager_projection grid-mapping, so we can pull
the clean longwave IR window band (CMI_C13, 10.3 µm — the classic IR
cloud channel) and warp it to WGS84.
from pyramids.dataset import Dataset, GeoReference
from pyramids.netcdf import NetCDF
nc = NetCDF.read_file(granule)
band = nc.get_variable("CMI_C13").to_crs(4326)
image = np.asarray(band.read_array()).astype("float32")
image[image >= 65535] = np.nan # mask the fill value
image.shape
Map it¶
Colder cloud tops are bright in the inverted IR palette; the warm surface is dark. The values are the granule's packed brightness-temperature counts (lower = warmer), which is why we invert the colormap.
vmin, vmax = np.nanpercentile(image, [2, 98])
brightness = Dataset.from_array(
image,
no_data_value=np.nan,
geo_ref=GeoReference(geo=band.geotransform, epsg=band.epsg),
)
glyph = brightness.plot(
cmap="Greys",
vmin=vmin,
vmax=vmax,
title=f"GOES-19 ABI CMI_C13 (10.3 µm) — {granule.name.split('_s')[1][:11]}",
)
glyph.cbar.set_label("packed IR counts (low = warm)")
Takeaway¶
EarthLens("goes", ...).download()returns raw ABI NetCDF granule paths — no decoding, no server-side subset.- Reading / reprojecting the geostationary grid is a downstream pyramids
(or
satpy) step — hereNetCDF.read_file(...).get_variable("CMI_C13").to_crs(4326). - Swap
domain="F"for Full Disk,domain="M1"for a 1-minute mesoscale sector, ordataset="abi-l1b-rad", variables=["C02"]for a single-channel radiance file. See the catalog explorer.