Tethys meteoraster
meteoraster is a Python library, developed by FORESIGHT — Forecasting and
Optimization for Resilient Environmental Systems through Investigation with
Groundbreaking Hydrological Tools, for
handling distributed ensemble and probabilistic meteorological forecasts.
Its core data structure, MeteoRaster, holds a single 5-D array indexed by
[production_datetime, ensemble_member, leadtime, latitude, longitude], and
wraps it with everything you need to turn raw reanalysis/forecast files into
report-ready time series, maps and aggregated statistics:
- read ERA5, ERA5-Land, GFS, C3S (seasonal) and CORDEX files into a common model,
- crop, join, trim, resample and check the completeness of the data,
- extract point time series or catchment/zone aggregates from KML polygons (area-weighted, missing-data aware),
- persist to compressed NetCDF (
h5netcdf) and reload losslessly, - and plot means, seasonal cycles, coordinates and availability with
cartopy.
Features
| Category | What you get |
|---|---|
| Data model | MeteoRaster — 5-D [production_datetime, ensemble_member, leadtime, lat, lon]; 2-D lat/lon grids; automatic Greenwich (0–360 → −180–180) fix and latitude flip |
| Subsetting / merging | get_cropped, join, trim, adjust_leadtimes |
| Resampling | resample_timestep (pandas rules) and the standalone Resample class |
| Extraction | get_values_from_latlon, get_values_from_KML, get_quantiles_from_KML*, get_values_from_latlon_by_event |
| Spatial aggregation | area-weighted coverage matrices from KML polygons, missing-data aware |
| Quality / diagnostics | is_complete, get_complete_index, get_missing, get_completeness |
| I/O | save / load (compressed NetCDF via h5netcdf), to_xarray |
| Visualisation | plot_mean, plot_mean_projected, plot_seasonal, plot_coordinates, plot_availability, add_shapefile, add_shape |
* get_quantiles_from_KML is currently under review and not functional — see
AGENTS.md.
Installation
meteoraster relies on a geospatial stack (GDAL/osgeo, cartopy, cfgrib)
that is most reliably installed with conda. Two paths follow: install the
released package from PyPI on top of a conda environment (use the package), or
install from source (develop / run the tests).
A · Install from PyPI
Create a conda environment that provides the geospatial libraries, then install
meteoraster into it:
conda env create -f environment.yml
conda activate tethys_rasters
pip install meteoraster
Upgrading later needs no URL — pip install --upgrade meteoraster always picks up
the newest release.
The package declares its pure-Python dependencies, but GDAL is intentionally not a pip dependency (it is a system/conda package). The conda environment above provides it. If you only need the point/plotting features and already have GDAL, you can
pip install meteorasterinto any environment.
Every release is also attached to the GitHub releases page, which is useful to pin an exact build without going through PyPI:
pip install https://github.com/FORESIGHT-ULisboa/Tethys-meteoraster/releases/download/v2.5/meteoraster-2.5-py3-none-any.whl
B · Install from source (development)
1 · Create the conda environment
conda env create -f environment.yml
conda activate tethys_rasters
2 · Install the package (editable) with the dev extras
From the repository root:
pip install -e ".[dev]"
That adds pytest, pytest-cov, build, twine and jupyterlab on top of the
runtime dependencies.
Quick start
import numpy as np
import pandas as pd
from meteoraster import MeteoRaster
# data axes: [production_datetime, ensemble_member, leadtime, lat, lon]
data = np.random.rand(2, 3, 2, 4, 5)
mr = MeteoRaster(
data=data,
latitudes=np.linspace(40, 30, 4), # 1-D is auto-meshed to 2-D
longitudes=np.linspace(-10, 10, 5),
production_datetime=pd.to_datetime(["2023-01-01", "2023-01-02"]).values,
leadtimes=np.array([pd.Timedelta(days=1), pd.Timedelta(days=2)]),
units="mm",
variable="precip",
)
Read a real dataset
The readers live in meteoraster.utils and return a ready-to-use MeteoRaster:
from meteoraster.utils import readERA5Land_monthly, read_C3S
era5 = readERA5Land_monthly("era5land_tp.grib", "tp") # total precipitation
c3s = read_C3S("seasonal_t2m.grib", "t2m") # seasonal 2 m temperature
Extract a point time series
# DataFrame: index = production_datetime, columns = MultiIndex(leadtime, ensemble_member)
ts = mr.get_values_from_latlon(lat=35.0, lon=0.0)
Aggregate over KML zones (e.g. catchments)
# agg: DataFrame indexed by production date, columns MultiIndex(zone, leadtime, ensemble_member)
# centroids: DataFrame of zone centroids (x, y)
agg, centroids = mr.get_values_from_KML("zones.kml", nameField="zone_id")
Crop, save and reload
sub = mr.get_cropped(from_lat=32, to_lat=38, from_lon=-2, to_lon=7,
from_prod_date=pd.Timestamp("2023-01-02"))
sub.save("forecast.nc") # compressed NetCDF (h5netcdf)
reloaded = MeteoRaster.load("forecast.nc")
Resample in time
import numpy as np
mr.resample_timestep("MS", fun=np.nanmean) # to month start, ensemble-wise
# Or resample a production×leadtime DataFrame with the standalone helper:
from meteoraster import Resample
resampled = Resample.resample(df, timestep_frequency="1D", resamplingType="sum")
Plot
ax, cbar = mr.plot_mean(central_longitude=0, coastline=True, borders=True,
colorbar=True, cmap="viridis")
mr.add_shapefile(ax, "basin.shp")
mr.plot_seasonal(lat=35.0, lon=0.0)
mr.plot_availability()
Project structure
Tethys-meteoraster/
├── meteoraster/
│ ├── __init__.py # exports MeteoRaster, Resample
│ ├── meteoraster.py # MeteoRaster core class
│ ├── resample.py # Resample (time-step resampling of DataFrames)
│ └── utils.py # ERA5 / ERA5-Land / GFS / C3S / CORDEX readers
├── tests/ # pytest suite + fixtures (conftest.py)
├── images/foresight.png # README header
├── environment.yml # conda environment (tethys_rasters)
├── pyproject.toml # packaging + pytest configuration
├── AGENTS.md # canonical guidance for humans & AI agents
├── CLAUDE.md # → points to AGENTS.md
├── .github/copilot-instructions.md # → points to AGENTS.md
├── .github/workflows/release.yml # build + publish to PyPI on tag push
├── .github/scripts/check_version.py # release guard: version declared consistently
├── LICENSE # Apache 2.0
├── NOTICE # attribution required by Apache 2.0
└── README.md
Running the tests
conda activate tethys_rasters
pip install -e ".[dev]"
pytest -ra
The suite covers construction, cropping, point/KML extraction, NetCDF round-trips, completeness checks, resampling and the deprecated-alias warnings. Tests that need real GRIB/NetCDF inputs (the format readers) are out of scope and not included.
Building a distribution (wheel + sdist)
The package builds with the standard PEP 517 toolchain:
conda activate tethys_rasters
pip install -e ".[build]"
python -m build
This produces both artifacts in dist/:
dist/
├── meteoraster-2.5-py3-none-any.whl
└── meteoraster-2.5.tar.gz
Install the wheel anywhere (provided GDAL is available):
pip install dist/meteoraster-2.5-py3-none-any.whl
Notes:
- The version lives in pyproject.toml (
project.version) and is mirrored inMeteoRaster.VERSION— bump both together. .github/scripts/check_version.py enforces that at release time. - Building by hand is only needed to inspect an artifact locally. Actual releases are produced by CI — see Releasing below.
Releasing
Releases are automated by
.github/workflows/release.yml. To cut version
X.Y:
-
Bump the version in both pyproject.toml and
MeteoRaster.VERSION, then commit. -
Tag and push:
git tag vX.Y git push origin vX.Y
The workflow then verifies the two version declarations agree with the tag, builds
the wheel and sdist, runs twine check --strict, publishes to
PyPI, and attaches both artifacts to the
GitHub release.
To rehearse without touching PyPI, run the workflow manually from the Actions
tab and leave the target as testpypi; it builds and uploads to
TestPyPI with no tag required.
PyPI uploads are immutable — a version number can never be reused, even after deletion. Get the version right before pushing the tag.
Authentication uses PyPI Trusted Publishing (OIDC), so no API tokens are stored in the repository. The one-time setup is described in AGENTS.md.
API reference
MeteoRaster
Construction & conversion
| Member | Description |
|---|---|
MeteoRaster(data, latitudes, longitudes, production_datetime, leadtimes, units='unknown', variable='unknown', …) |
Build from a 5-D array (or a dict with the same keys). 1-D lat/lon are auto-meshed to 2-D; longitudes are wrapped to [−180, 180] and latitudes flipped if needed. |
copy() |
Deep copy. |
to_xarray() |
Convert to a labelled xarray.DataArray. |
Subsetting, merging & resampling
| Member | Description |
|---|---|
get_cropped(from_prod_date, to_prod_date, from_lat, to_lat, from_lon, to_lon, from_leadtime, to_leadtime) |
Crop in time and space; returns a new MeteoRaster. |
join(meteoRaster, strickt=False, trim=False) |
Concatenate another raster along production dates (aligns ensembles/leadtimes). |
trim() |
Drop leading/trailing all-NaN production dates. |
adjust_leadtimes(period='months') |
Align leadtimes to relative periods (experimental). |
resample_timestep(rule, fun=np.mean) |
Resample production dates with pandas rules (experimental). |
Extraction & aggregation
| Member | Description |
|---|---|
get_values_from_latlon(lat, lon) |
Time series from the nearest pixel as a DataFrame. |
get_values_from_KML(kml, nameField=None, …) |
Area-weighted aggregate over KML polygons → (agg, centroids). |
get_quantiles_from_KML(...) |
Spatial quantiles over KML zones — under review, not functional. |
get_values_from_latlon_by_event(production_date_dataframe) |
(static) Reindex a production-date frame by event date. |
Quality & diagnostics
| Member | Description |
|---|---|
is_complete(full_ensemble=True, space_completeness=False) |
Whether every production×leadtime slice has finite data. |
get_complete_index(full_ensemble=True, space_completeness=False) |
Boolean DataFrame (production × leadtime) of completeness. |
get_missing() |
Fraction of missing pixels per leadtime/ensemble member. |
get_completeness(file) |
(classmethod) Read the complete flag from a saved file. |
I/O
| Member | Description |
|---|---|
save(file, complevel=1, complete=None) |
Write compressed NetCDF via h5netcdf. |
load(file, verbose=None) |
(classmethod) Reload a NetCDF written by save. |
Visualisation
| Member | Description |
|---|---|
create_plot(central_longitude, …) |
Create a cartopy axes. |
plot_mean(ax=None, coastline=False, borders=False, colorbar=True, cmap='viridis', …) |
Map of the ensemble/temporal mean → (ax, cbar). |
plot_mean_projected, plot_coordinates, plot_seasonal, plot_availability |
Projected mean, grid coordinates, seasonal cycle, data availability. |
add_shapefile(ax, shapefile_path, …) / add_shape(ax, path, …) |
Overlay shapefile geometries/boundaries. |
Deprecated camelCase aliases (
getCropped,getValuesFromKML,getDataFromLatLon,getQuantilesFromKML,resampleTimeStep) still work but emit aDeprecationWarning; use the snake_case names above.
Resample
| Member | Description |
|---|---|
Resample.resample(data, timestep_frequency, resamplingType, date_from=None, date_to=None, print_func=None) |
(classmethod) Resample a production×leadtime DataFrame. resamplingType ∈ {'sum', 'mean', 'linear', 'max'}. |
License
Licensed under the Apache License, Version 2.0 — see LICENSE and NOTICE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file meteoraster-2.5.tar.gz.
File metadata
- Download URL: meteoraster-2.5.tar.gz
- Upload date:
- Size: 41.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
73fedd5124c8747ce1d31f7dd84535e64beefc2ea2e88a78b7a3c7d36fa6a3c7
|
|
| MD5 |
b49421d3ccd1f623f413fc5fee523c31
|
|
| BLAKE2b-256 |
95c0459b37abc1b80b7de7d83a06f935df948779553382442cc10a9e63246abd
|
Provenance
The following attestation bundles were made for meteoraster-2.5.tar.gz:
Publisher:
release.yml on FORESIGHT-ULisboa/Tethys-meteoraster
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
meteoraster-2.5.tar.gz -
Subject digest:
73fedd5124c8747ce1d31f7dd84535e64beefc2ea2e88a78b7a3c7d36fa6a3c7 - Sigstore transparency entry: 2359647325
- Sigstore integration time:
-
Permalink:
FORESIGHT-ULisboa/Tethys-meteoraster@fa04d473ec27affe0a99485d279f1b062d0cef52 -
Branch / Tag:
refs/tags/v2.5 - Owner: https://github.com/FORESIGHT-ULisboa
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@fa04d473ec27affe0a99485d279f1b062d0cef52 -
Trigger Event:
push
-
Statement type:
File details
Details for the file meteoraster-2.5-py3-none-any.whl.
File metadata
- Download URL: meteoraster-2.5-py3-none-any.whl
- Upload date:
- Size: 31.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dd9271b396fffdae4ee105756e30be2b1e31c8fe4dc29b5d57a6333b1d77ae9e
|
|
| MD5 |
6a1ed1acdc0e4485ce2a8c867d819fb0
|
|
| BLAKE2b-256 |
0083d7db7c6b77ec95039dc5ebcfebd280f757247aacf81ab6a64be0c8813489
|
Provenance
The following attestation bundles were made for meteoraster-2.5-py3-none-any.whl:
Publisher:
release.yml on FORESIGHT-ULisboa/Tethys-meteoraster
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
meteoraster-2.5-py3-none-any.whl -
Subject digest:
dd9271b396fffdae4ee105756e30be2b1e31c8fe4dc29b5d57a6333b1d77ae9e - Sigstore transparency entry: 2359647380
- Sigstore integration time:
-
Permalink:
FORESIGHT-ULisboa/Tethys-meteoraster@fa04d473ec27affe0a99485d279f1b062d0cef52 -
Branch / Tag:
refs/tags/v2.5 - Owner: https://github.com/FORESIGHT-ULisboa
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@fa04d473ec27affe0a99485d279f1b062d0cef52 -
Trigger Event:
push
-
Statement type: