---
name: eocube-meteo-cube
description: "Read the anonymous daily gridded meteorological cube for Romania (s3://meteo/cube/open-v1, Zarr, from 1961): open it with xarray and s3fs, pick max_temp, min_temp and precipitation, slice time to the last_*_time coordinate because future dates read as zero not NaN, clip to a county or locality, and scale to the whole country with Dask."
license: Apache-2.0
compatibility: "Any Python environment with xarray, zarr, s3fs, rioxarray and geopandas; anonymous read access, no account needed. Tested in EOCube.RO notebooks (notebooks.eocube.ro)."
---

## Open the cube

```python
import s3fs, xarray as xr
fs = s3fs.S3FileSystem(anon=True, client_kwargs={"endpoint_url": "https://storage.svc.uvt-01.eocube.ro"})
ds = xr.open_zarr(fs.get_mapper("meteo/cube/open-v1"), consolidated=True)
ds
```

Daily grids over Romania produced from the national meteorological service (ANM)
observations, starting in 1961. Anonymous access; path-style endpoint; no credentials.

## Variables

| Variable | Meaning | Unit |
|---|---|---|
| `max_temp` | daily maximum temperature | degrees Celsius |
| `min_temp` | daily minimum temperature | degrees Celsius |
| `precipitation` | daily precipitation | mm |
| `last_precipitation_time` and similar `last_*_time` | scalar: last day with real data for that variable | timestamp |

## The time-axis trap

The `time` axis is pre-allocated into the future. **Days after the last ingested date
read as 0, not NaN.** A naive mean or sum over the full axis is wrong. Always slice:

```python
last = ds["last_precipitation_time"].values
pr = ds["precipitation"].sel(time=slice("1961-01-01", last))
```

Do the same per variable with its own `last_*_time`. Treat a run of exact zeros at the
end of a series as missing data, never as dry days.

## Clip to a county or locality

Get the geometry with the `geo_*` MCP tools (`geo_county_by_name("Timiș")`,
`geo_admin_unit_by_name("Timișoara")`; the default result is a GeoJSON file path) or with
`eocube services geospatialorg get-county-by-name --name Timiș`.

```python
import geopandas as gpd, rioxarray  # noqa: F401 (registers .rio)
aoi = gpd.read_file(geojson_path).to_crs(4326)
pr = pr.rio.write_crs(4326, inplace=False) if pr.rio.crs is None else pr
clipped = pr.rio.clip(aoi.geometry, aoi.crs, drop=True)
monthly = clipped.resample(time="1MS").sum().mean(["y", "x"])
```

Check the cube's dimension names (`x`/`y` or `lon`/`lat`) with `ds.dims` and adjust.
For a single locality, pick the nearest cell instead: `pr.sel(x=lon, y=lat, method="nearest")`.

## Scale

A county or locality needs no cluster; `.compute()` directly. Whole-country statistics
over decades are Dask-sized: open with explicit `chunks={"time": 365}` and use the
personal cluster from `eocube-dask`. The cube is anonymous, so workers can open it
directly with the same `s3fs` arguments; no signing is involved.

## Tutorials

- Clima localității mele: https://docs.eocube.ro/ (notebook "meteo-localitate")
- Clima României pe hartă: https://docs.eocube.ro/ (notebook "meteo-romania")

## Pitfalls

- Summing precipitation past `last_precipitation_time` adds zeros silently.
- Comparing years: use complete years only; the current year is partial.
- Temperature extremes: `max_temp.max("time")` is fine, but `min_temp.min("time")` over
  the padded tail returns 0; slice first.
- Large `.plot()` calls on the full cube pull everything into memory; aggregate first.
