WeatherNext 3 forecast data is stored as Zarr v3 arrays on Google Cloud Storage. Google Cloud Storage is the primary surface for accessing the complete, raw model output at full resolution, multi-resolution surface grids, and 3D atmospheric pressure levels.
What is Zarr?
Zarr is an open-standard format for chunked N-dimensional arrays. Data is partitioned into compressed chunks in cloud storage, enabling direct queries in Python using Xarray without downloading entire global grids.
Available buckets
WeatherNext 3 forecast data is stored across two primary Zarr storage buckets:
| Property | Full Ensemble Bucket | Precomputed Statistics Bucket |
|---|---|---|
| GCS URI | gs://weathernext3_spatial/weathernext_3_0_0/zarr/ |
gs://weathernext3_statistics_spatial/weathernext_3_0_0_statistics/zarr/ |
| Contents | Raw 64-member ensemble, all variables including 13 atmospheric pressure levels. | Precomputed distribution statistics (_mean, _p10, _p25, _p50, _p75, _p90) for all 0.1° and 0.05° surface variables (no pressure levels). |
| Lead Time Coordinates | 6-hourly lead_time with 6 hourly offsets in lead_subtime (stacking required for hourly series). |
Pre-flattened: Single, continuous 1-hour lead_time coordinate axis (no subtime stacking needed). |
| Forecast Horizon |
|
|
| Storage Class | Standard (auto-transitions to Nearline after 28 days using a lifecycle rule) | Standard |
| Requester Pays | ON — network egress charges are billed to your Google Cloud project. Colocate compute in us-east1 to avoid cross-region fees. |
OFF |
Directory structure
All data is structured in timestamped folders and batched archives:
gs://weathernext3_spatial/weathernext_3_0_0/zarr/
├── 2024/ # Historical data (currently being backfilled)
│ ├── 6hr_inits.zarr # 6-hourly inits (00, 06, 12, 18 UTC), all vars, 360h horizon
│ └── interim_1hr_inits.zarr # Interim hourly inits, surface only, 48h horizon
├── 2025/ # Historical data (currently being backfilled)
│ ├── 6hr_inits.zarr
│ └── interim_1hr_inits.zarr
└── 2026_to_present/ # Operational real-time forecasts
├── <YYYYMMDD_HHhr_XX_preds>/
│ └── predictions.zarr # Per-init real-time forecasts
└── ...
Common query recipes
Before running these recipes, install the required Python libraries:
pip install xarray zarr obstore
1. Open a real-time forecast run
Open a real-time forecast run using Xarray and Zarr with obstore.
Option A: Full Ensemble Store (gs://weathernext3_spatial/)
When opening the full ensemble bucket, pass your billing project in the client options header:
Python
import obstore
import xarray as xr
import zarr
# Initialize GCS store with Requester Pays billing project header
gcs_store = obstore.store.GCSStore(
bucket="weathernext3_spatial",
prefix="weathernext_3_0_0/zarr/2026_to_present/20260826_00hr_01_preds/predictions.zarr",
client_options={
"default_headers": {
"x-goog-user-project": "YOUR_PROJECT_ID"
}
}
)
zstore = zarr.storage.ObjectStore(gcs_store)
# Open Zarr dataset lazily
ds_ens = xr.open_zarr(zstore, chunks={})
print(f"Loaded ensemble run initialized at: {ds_ens['init_time'].values}")
print(f"Lead times available: {ds_ens['lead_time'].values[:5]} ...")
Option B: Precomputed Statistics Store (gs://weathernext3_statistics_spatial/)
The summary statistics store provides pre-flattened continuous 1-hour
lead_time arrays (_mean, _p10, _p25, _p50, _p75, _p90) without
requiring billing headers:
Python
import obstore
import xarray as xr
import zarr
stats_store = obstore.store.GCSStore(
bucket="weathernext3_statistics_spatial",
prefix="weathernext_3_0_0_statistics/zarr/2026_to_present/20260826_00hr_01_preds/predictions.zarr"
)
zstore_stats = zarr.storage.ObjectStore(stats_store)
# Open statistics dataset lazily
ds_stats = xr.open_zarr(zstore_stats, chunks={})
print(f"Loaded statistics run initialized at: {ds_stats['init_time'].values}")
print(f"Continuous lead times: {ds_stats['lead_time'].values[:5]} ...")
2. Point forecast time series (0.05° station grid)
Extract an hourly 24-hour surface temperature forecast for a single region using the 0.05° station head:
Python
import numpy as np
import obstore
import xarray as xr
import zarr
# Initialize GCS store and open ensemble dataset
gcs_store = obstore.store.GCSStore(
bucket="weathernext3_spatial",
prefix="weathernext_3_0_0/zarr/2026_to_present/20260826_00hr_01_preds/predictions.zarr",
client_options={"default_headers": {"x-goog-user-project": "YOUR_PROJECT_ID"}}
)
ds_ens = xr.open_zarr(zarr.storage.ObjectStore(gcs_store), chunks={})
# Coordinates for New York City (longitudes in 0-360 range)
min_lon, max_lon = 285.74, 286.30
min_lat, max_lat = 40.50, 40.90
# Select 0.05° station head 2m temperature
nyc_ds = ds_ens[['station_head_temperature_2m']].sel(
lat_0p05=slice(min_lat, max_lat),
lon_0p05=slice(min_lon, max_lon)
)
# Select first 24 hours (6h, 12h, 18h, 24h lead times)
lead_times_24h = [np.timedelta64(h, 'h') for h in [6, 12, 18, 24]]
nyc_24h = nyc_ds['station_head_temperature_2m'].sel(lead_time=lead_times_24h)
# Stack lead_time and lead_subtime into continuous hourly steps
nyc_hourly = nyc_24h.stack(step=('lead_time', 'lead_subtime'), create_index=False)
valid_times = (
ds_ens['init_time'].values
+ nyc_hourly['lead_time'].values
+ nyc_hourly['lead_subtime'].values
)
nyc_hourly = nyc_hourly.assign_coords(valid_time=('step', valid_times))
# Compute ensemble mean in Celsius (°C)
nyc_mean = nyc_hourly.mean(dim=('sample', 'lat_0p05', 'lon_0p05')) - 273.15
3. Deterministic ensemble mean and regional slice
Compute the global ensemble mean across all 64 members, then crop to a regional geographic box (handling Prime Meridian 0° / 360° wrap-around):
Python
import numpy as np
import obstore
import xarray as xr
import zarr
# Initialize GCS store and open ensemble dataset
gcs_store = obstore.store.GCSStore(
bucket="weathernext3_spatial",
prefix="weathernext_3_0_0/zarr/2026_to_present/20260826_00hr_01_preds/predictions.zarr",
client_options={"default_headers": {"x-goog-user-project": "YOUR_PROJECT_ID"}}
)
ds_ens = xr.open_zarr(zarr.storage.ObjectStore(gcs_store), chunks={})
# Note: In raw ensemble stores (ds_ens), selecting lead_subtime=0h isolates the forecast
# step on the outer 6-hour interval boundary. For continuous hourly data, stack dimensions (Recipe 2).
# Alternatively, open the statistics store (gs://weathernext3_statistics_spatial/weathernext_3_0_0_statistics/zarr/) where lead_time
# is already flattened into a single, continuous 1-hour axis (e.g. ds_stats.sel(lead_time=np.timedelta64(6, 'h'))).
# Compute global ensemble mean at 6h lead time and 0h subtime offset
world_avgs = ds_ens.sel(
lead_time=np.timedelta64(6, 'h'),
lead_subtime=np.timedelta64(0, 'h')
).mean(dim='sample', skipna=True)
# Select Western Europe bounding box on 0.1° grid
# (lat: 60°N to 35°N, lon: 10°W [350°E] to 20°E)
lat_slice = slice(35.0, 60.0)
ds_west = world_avgs[['temperature_2m']].sel(lat_0p1=lat_slice, lon_0p1=slice(350.0, 360.0))
ds_east = world_avgs[['temperature_2m']].sel(lat_0p1=lat_slice, lon_0p1=slice(0.0, 20.0))
europe_temp = xr.concat([ds_west, ds_east], dim='lon_0p1') - 273.15
4. Extreme risk mapping using ensemble percentiles
Identify regions where the 90th percentile temperature exceeds 35°C at 48 hours lead time:
Python
import matplotlib.pyplot as plt
import numpy as np
import obstore
import xarray as xr
import zarr
# Initialize GCS store and open ensemble dataset
gcs_store = obstore.store.GCSStore(
bucket="weathernext3_spatial",
prefix="weathernext_3_0_0/zarr/2026_to_present/20260826_00hr_01_preds/predictions.zarr",
client_options={"default_headers": {"x-goog-user-project": "YOUR_PROJECT_ID"}}
)
ds_ens = xr.open_zarr(zarr.storage.ObjectStore(gcs_store), chunks={})
# Select 48h lead time on 6-hour boundary (or query ds_stats directly without lead_subtime)
temp_48h = ds_ens['temperature_2m'].sel(
lead_time=np.timedelta64(48, 'h'),
lead_subtime=np.timedelta64(0, 'h')
) - 273.15
# Compute 90th percentile across the 64 ensemble members
temp_p90 = temp_48h.quantile(0.90, dim='sample')
# Flag regions exceeding 35°C threshold
heat_risk = temp_p90 > 35.0
heat_risk.plot(cmap="Reds", cbar_kwargs={"label": "Heat Risk (p90 > 35°C)"})
Best practices
- Slice before loading: Filter by variable, region, or time
(
.sel(),ds_ens[['temperature_2m']]) before triggering computations or calling.values. - Avoid full
.load(): A 64-member global forecast contains hundreds of gigabytes. Let Xarray stream chunks on demand. - Minimize egress costs: The full ensemble bucket uses Requester Pays
and is located in
us-east1. Running compute in the same region avoids network transfer fees entirely. If your workloads run in a different region or outside Google Cloud, standard network egress charges apply and can be significant at scale. When precomputed percentiles or SQL analytics are sufficient, query the tables in BigQuery or Earth Engine instead.
Starter guides
For an interactive Colab environment with global mapping and advanced workflows:
- WeatherNext 3 Starter Guide - Zarr (Spatial - Full Ensemble) on Google Cloud Storage
- WeatherNext 3 Starter Guide - Zarr (Spatial - Statistics) on Google Cloud Storage
Terms of use
- Real-Time Data (data relating to less than 1 hour ago and the future): Governed by the GDM Real-Time Weather Forecasting Experimental Data Terms of Use.
- Historical Data (data relating to 1 hour ago or more): Licensed under CC BY 4.0.
For more details, see Terms of Service and Disclaimers.