WeatherNext 3 forecast data is stored as [Zarr v3](https://zarr.dev/) 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](https://zarr.dev/) 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](https://docs.xarray.dev/) 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 | - **6-hourly inits (00, 06, 12, 18 UTC):** 360-hour (15-day) horizon. - **Interim inits:** 48-hour horizon. - 2026 to present: every hour (01--05, 07--11, 13--17, 19--23 UTC) - 2024--2025: 03, 09, 15, 21 UTC only | - **6-hourly inits (00, 06, 12, 18 UTC):** 360-hour (15-day) horizon. - **Interim inits:** 48-hour horizon. - 2026 to present: every hour (01--05, 07--11, 13--17, 19--23 UTC) - 2024--2025: 03, 09, 15, 21 UTC only |
| Storage Class | Standard (auto-transitions to Nearline after 28 days using a lifecycle rule) | Standard |
| Requester Pays | **ON** --- [network egress charges](https://cloud.google.com/storage/pricing#network-egress) are billed to your Google Cloud project. Colocate compute in `us-east1` to avoid cross-region fees. | **OFF** |
|---|---|---|

### Directory structure

All data is organized into yearly archives (2024--2025) and per-init folders
(2026 to present):

    gs://weathernext3_spatial/weathernext_3_0_0/zarr/
    ├── 2024/                            # Yearly archive
    │   ├── 6hr_inits.zarr               # 00, 06, 12, 18 UTC · all variables · 360h horizon
    │   └── interim_inits.zarr           # 03, 09, 15, 21 UTC · surface only · 48h horizon
    ├── 2025/                            # Yearly archive (same layout as 2024)
    │   ├── 6hr_inits.zarr
    │   └── interim_inits.zarr
    └── 2026_to_present/                 # Real-time forecasts, all 24 inits per day
        ├── <YYYYMMDD_HHhr_XX_preds>/
        │   └── predictions.zarr         # One store per init
        └── ...

> [!NOTE]
> **2024--2025 interim inits:** The yearly
> `interim_inits.zarr` archives contain 4 interim inits per day
> (03, 09, 15, 21 UTC). From 2026 to present, all 20 interim inits are
> available, and new forecasts are published every hour.

> [!CAUTION]
> **Longitude convention:** Longitudes in raw Zarr grids use the
> **0° to 360°** coordinate system (e.g., New York City is at
> \~286.0°, London is at \~359.9°). Convert standard −180° to
> 180° values to 0° to 360° using: `lon_360 = lon % 360` (or
> convert 0° to 360° back to −180° to 180° using:
> `lon = (lon + 180) % 360 - 180`).

*** ** * ** ***

## 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:

<br />

**Python**

<br />

    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]} ...")

<br />

<br />

#### 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:

<br />

**Python**

<br />

    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]} ...")

<br />

<br />

### 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:

<br />

**Python**

<br />

    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

<br />

<br />

### 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):

<br />

**Python**

<br />

    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

<br />

<br />

### 4. Extreme risk mapping using ensemble percentiles

Identify regions where the 90th percentile temperature exceeds 35°C at 48 hours
lead time:

<br />

**Python**

<br />

    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)"})

<br />

<br />

*** ** * ** ***

## 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](https://cloud.google.com/storage/pricing#network-egress) apply and can be significant at scale. When precomputed percentiles or SQL analytics are sufficient, query the tables in [BigQuery](https://developers.google.com/weathernext/guides/bigquery) or [Earth Engine](https://developers.google.com/weathernext/guides/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](https://storage.googleapis.com/weathernext-public/colabs/WeatherNext_3_Starter_Guide_Zarr_Spatial_Full_Ensemble_on_Google_Cloud_Storage.ipynb)
- [WeatherNext 3 Starter Guide - Zarr (Spatial - Statistics) on Google Cloud Storage](https://storage.googleapis.com/weathernext-public/colabs/WeatherNext_3_Starter_Guide_Zarr_Spatial_Statistics_on_Google_Cloud_Storage.ipynb)

## 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](https://storage.googleapis.com/weathernext-public/terms-of-use.pdf).
- **Historical Data (data relating to 1 hour ago or more):** Licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).

For more details, see
[Terms of Service and Disclaimers](https://developers.google.com/weathernext/guides/disclaimers).