Cloud-native access to NOAA Satellite Ocean Heat Content (OHC) products using Icechunk, providing analysis-ready access to the original NOAA data without duplicating the underlying files. https://www.ospo.noaa.gov/products/ocean/ohc/
🌐 View data in browser · 💻 Data access (code) · 📦 Data access (CoastWatch)
Icechunk stores of the full NOAA CoastWatch Ocean Heat Content (OHC) Product Suite archive — upper-ocean heat content, SST, sea surface height anomaly and isotherm depths at 0.25°, daily, 2020-04-30 through 2026-08-26 — for three regions:
Each region is a separate repository because the three use different lat/lon grids and cannot share a virtual Zarr array. Each holds the same three groups — see Groups inside each repository.
These stores are virtual: they copy no science data at all. Icechunk holds Zarr
metadata and byte-range references back to the original NetCDF/HDF5 files hosted at NOAA
CoastWatch, so a store of a multi-terabyte archive is megabytes of metadata and reads are
streamed from coastwatch.noaa.gov over HTTP range requests. That costs one extra step
when opening — see How to open it — and it is why the browser viewer
needs the workaround described below.
The stores are built by a manual run, not a live feed. Until an automatic append is in place, the last time step lags the CoastWatch archive by however long has passed since the build recorded in Provenance.
⚠️ Read this first: the data will not draw unless you disable CORS
The viewer loads, lists the variables and draws the map graticule, and then stops — because the science arrays are not in the store. They are at
coastwatch.noaa.gov, which serves byte ranges happily to a script but sends noAccess-Control-Allow-Originheader, so your browser refuses to hand those bytes to the page. This is a rule browsers enforce on the page's behalf; nothing the viewer or this repository can contain will waive it.
With that in place, the viewer streams chunks straight from the store — no install, no account, no download:
Each link opens that region's repository centred on its own basin, at the first time step.
There is deliberately no link per group or per variable: the three groups (daily,
14day_v1, 14day) and the variables within each are dropdowns in the viewer, and its
dataset picker lists all three regions — so you can move anywhere in the archive without
coming back here.
Everything after # is a URL fragment, which the host never sees, so one viewer build
serves any store: the repository is the part before the first ::, and lat, lon and
alt are the camera. Drag the globe and the address bar updates — copy it to share the
exact view you are looking at.
The viewer is gridlook, a WebGL globe for
cloud-hosted Zarr and Icechunk stores, published alongside the data at
noaa-ohc/viewer/.
Give it a moment on first load: it fetches the store's metadata before drawing. It is a
browser reading a remote archive, so treat it as a look, not an analysis.
Requires icechunk >= 2.1 and xarray. The science arrays live at CoastWatch, outside
these stores, so you must authorize the virtual chunk container at open time — this is the
one non-standard step, and the price of not copying the data.
icechunk 1.x will not work.
icechunk.http_storagedoes not exist in icechunk 1.x (it arrived in 2.0), andicechunk.credentials.HttpAccessarrived in 2.1. Every icechunk 2.x release needs Python 3.12 or newer, so on an older Pythonpip install icechunkquietly installs 1.1.x, and the code below fails withAttributeError: module 'icechunk' has no attribute 'http_storage'. Check what you have:python -c "import icechunk; print(icechunk.__version__)".
CoastWatch blocks the default python-requests User-Agent, but Icechunk sends its own
(icechunk-rust-x.y.z), so no custom headers are needed for reads.
If a read raises StorageError: error fetching virtual reference ... connection closed before message completed, retry it. CoastWatch's HTTPS endpoint is slow and occasionally
drops connections; the reference is fine, and the same read succeeds on a retry.
Use chunks={} (dask-backed) as shown — with chunks=None the arrays are plain NumPy and
this concatenation eagerly materializes several GB into memory.
The Satellite Ocean Heat Content Suite (produced by USDOC/NOAA/NESDIS/OSPO with the University of Miami/Rosenstiel School) blends altimeter sea-surface-height anomalies with GeoPolar blended SST and the SMARTS climatology to estimate upper-ocean heat content and related fields. It is widely used in hurricane-intensification analysis.
crs variable)Each region covers a different domain — the North Atlantic grid is 0°–60°N, 100°W–0°, at
241 × 401. Open a repo and inspect its latitude/longitude coordinates for the exact
extent of the others.
Coordinates are time, latitude (degrees_north) and longitude (degrees_east).
Missing data is -999 in the source files. A CF missing_value attribute has been added
where the source omitted it, so xarray masks -999 to NaN automatically on read — no
manual masking needed.
Every region repo has the same three groups. They exist because the archive is not homogeneous, and a single virtual Zarr array cannot span files that differ in variable set or byte-level encoding:
daily vs. 14day* — two generations of the product (source dirs {region} and
{region}14) with different variable sets. The 14day* files add the SST
quality_flag variable, and 14day_v1 additionally carries a scalar
quality_information summary. Keeping them separate preserves each generation's
variables without forcing empty columns.14day_v1 vs. 14day — partway through 2025 the source files switched from
NetCDF-3 (big-endian) to HDF5 (little-endian). Because Icechunk stores a single
codec pipeline per array and references the original bytes, big-endian and
little-endian chunks cannot live in one virtual array. The split is at day-of-year
084/085 of 2025, verified identical for all three regions.Time steps per group, as built:
Note on the overlap and gaps.
dailyand14day_v1overlap by a few days around January 2024 (both cover 2024-01-15…2024-01-18). A handful of source files with corrupt (all-zero) coordinates and data were dropped during construction — they are bad in the source archive, not mishandled here — so a group may begin one day after its nominal boundary, and the counts differ between regions.
For a continuous single time series, open the groups separately and concatenate the shared variables at read time, as shown above.
The notebooks that built these stores live in the GitHub repository
https://github.com/ocean-icechunks/icechunks (directory coastwatch-heat-content/). Two of
them sit alongside this README on Source Cooperative:
ocean-heat-test-sc.ipynb is deliberately not published here: it needs Source Cooperative
write credentials, so it is no use to a reader who has just found the stores, and
ocean-heat-test-local.ipynb shows the same steps with none. It writes only to a scratch
repository and refuses to touch the published prefix.
Start with ocean-heat-test-local.ipynb. It is short, needs no account, and its
committed outputs show the whole pattern working: a ranged GET against CoastWatch, three
daily files virtualized and appended along time in about one second each, then the store
reopened and one ohc field read back through its virtual references.
The write notebooks import shared helpers from icechunk_utils.py (included here), and
requirements.txt (also included) lists the package floors. Python 3.12 or newer is
required — every icechunk 2.x release is published requires_python = ">=3.12".
The browser viewer is published separately by publish_viewer.py in the GitHub
repository (python publish_viewer.py --product noaa-ohc --build ~/gridlook). It is a
plain static build of gridlook and holds no data of its own.
An earlier build of the same archive lived under fish-pace/coastwatch/ocean-heat/; these
repositories replaced it and it is no longer maintained. Every code block on this page was
executed against the live stores before it was published.
Code. The notebooks and helpers are released under Apache-2.0 and are free to use, copy, adapt and redistribute, commercially or not — no attribution required.
Data. The data is not ours, and these stores contain none of it — only references to files hosted by NOAA CoastWatch. For data use and citation, follow the source product credited below.
To look at the data anyway, install a CORS-disabling browser extension (search your browser's extension store for "CORS unblock" or "Allow CORS"), enable it, and reload the viewer. Such an extension switches off a real security protection for the sites you enable it on, so turn it back off when you are done — or use a separate browser profile for it.
The code path below has no such problem: this affects browsers only.
grid-mapping container (grid_mapping_name = latitude_longitude) |
| — |
quality_information | scalar retrieval-statistics summary | — (14day_v1 only) |