Catalog PyPI
PACKAGE EXPLORERView in registry

aihydro-data

PyPI

Global hydrology dataverse — multi-source, region-aware data fetch (GEE + STAC + HyRiver) for AI agents and humans

agenticcamelsdata-fetchearth-enginegeospatialglobalhydrologymcpstac
Versions collected1
Direct dependencies41From release 0.2.4
Last observed21 Oct, 10:56 UTC

README

Version 0.2.4 documentation

aihydro-data

Global hydrology dataverse — fetch any variable, anywhere, from the best available source.

aihydro-data is a variable-centric, multi-source, region-aware Python library that unifies Google Earth Engine (GEE), HyRiver, and direct HTTP APIs behind a single fetch() call. It is the data backbone of the AI-Hydro toolchain.

from aihydro_data import fetch

# Auto mode — router picks the best product for the geometry's region
result = fetch(
    variable="precipitation",
    geometry=watershed_gdf,       # GeoDataFrame, GeoJSON, shapely, (lat, lon), or bbox
    start="2010-01-01",
    end="2020-12-31",
)
print(result.product)    # "CHIRPS"  (auto-selected)
print(result.source)     # "gee"
print(result.citation)   # full bibliographic reference
result.data              # pd.DataFrame or xr.DataArray
result.next_steps        # agent-facing hints: what to do with this data

PyPI Python License DOI


Table of Contents


Why

Before aihydro-data, fetching hydrology data meant hard-coding a single CONUS-only source per variable (GridMET for precip, NWIS for streamflow, POLARIS for soil). Anything outside CONUS meant writing a new fetcher from scratch.

aihydro-data turns this into a single declarative routing problem: one call, every region, every variable, documented fallbacks.

VariableCONUS (primary)Global (primary)Fallback chain
PrecipitationGridMETCHIRPS (GEE)IMERG, ERA5-Land, CHIRPS-IRI*
Tmax / TminGridMET, DaymetERA5-Land—
ET (actual)MOD16MOD16TerraClimate, ERA5-Land
ET (potential)GridMETERA5-LandMOD16
Soil moistureSMAPSMAP—
Land coverNLCDESA WorldCoverDynamic World
Soil propertiesPOLARISSoilGrids—
DEM3DEP (10 m)Copernicus GLO-30SRTM, MERIT-DEM
StreamflowUSGS Water Data API (NWIS)GEOGLOWS, Open-Meteo, GloFASGEOGLOWS, Open-Meteo
NDVIMODIS (250 m)MODIS (250 m)Sentinel-2 (10 m)
LAIMODISMODIS—

* CHIRPS_IRI — auth-free OPeNDAP fallback, no GEE account required.


Install

# Full install (all backends)
pip install aihydro-data[all]

# Per-backend
pip install aihydro-data[gee]        # Google Earth Engine (23 products)
pip install aihydro-data[hyriver]    # CONUS via HyRiver (10 products)
pip install aihydro-data[stac]       # STAC catalogues (Planetary Computer)
pip install aihydro-data[opendap]    # IRI OPeNDAP (CHIRPS auth-free fallback)

Python: 3.10+  |  GEE auth: required for 23 GEE products (see Auth Setup)


Quick Start

1. Auto mode — global watershed

from aihydro_data import fetch
import geopandas as gpd

# Any geometry: GeoDataFrame, shapely Point/Polygon, (lat, lon) tuple, or bbox
gdf = gpd.read_file("ganges_basin.geojson")

result = fetch("precipitation", gdf, "2015-01-01", "2015-12-31")
print(result.product)   # "CHIRPS"  (auto-selected for South Asia)
print(result.data)
#           date  precipitation
# 0   2015-01-01           1.23
# 1   2015-01-02           0.00
# ...

2. Auto mode — CONUS watershed

from aihydro_data import fetch
from shapely.geometry import Point

# Kansas City — routes to GridMET (no GEE auth needed)
result = fetch("precipitation", Point(-94.5, 39.1), "2020-01-01", "2020-12-31")
print(result.product)   # "GRIDMET_PRECIP"
print(result.source)    # "hyriver"

3. Manual mode — pin a specific product

result = fetch(
    "et",
    gdf,
    "2010-01-01", "2020-12-31",
    mode="manual",
    product="MOD16_ET",
)
print(result.units)     # "mm/month"
print(result.citation)  # Running et al. 2019 ...
print(result.bibtex)    # @dataset{...}

4. Plot the result (v0.1.2+)

result = fetch("streamflow", "03245500", "2010-01-01", "2020-12-31")

# Auto-dispatched plot — picks line/bar/imshow based on data shape
result.plot(logy=True)

# Interactive folium map preview
result.map()

# Multi-source comparison in one call
from aihydro_data.viz import compare
fig = compare(
    ["GRIDMET_PRECIP", "CHIRPS", "ERA5L_PRECIP"],
    watershed_gdf, "2015-01-01", "2020-12-31",
    plots=["timeseries", "climatology", "scatter", "double_mass"],
)

# Research-grade hydrology plots
from aihydro_data.viz import flow_duration_curve, climatology, budyko
flow_duration_curve(streamflow_result)
budyko(precip_result, pet_result, et_result, label="My catchment")

Install with pip install aihydro-data[viz] (matplotlib + folium).

5. Discover available products

from aihydro_data import list_products, get_product

# List all precipitation products
for p in list_products(variable="precipitation"):
    print(f"{p.id:20s}  {p.coverage}  {p.resolution_m}m  {p.source}")

# Inspect one product's full spec
spec = get_product("CHIRPS")
print(spec.common_pitfalls)
print(spec.examples)

6. Validate before fetching

from aihydro_data.mcp import data_validate_request

check = data_validate_request(
    variable="et",
    geometry={"type": "Point", "coordinates": [-94.5, 39.1]},
    start="1990-01-01",
    end="2000-12-31",
)
# {"ok": False, "issues": [{"code": "DATE_OUT_OF_RANGE",
#   "product": "MOD16_ET", "message": "MOD16 starts 2000-01-01."}], ...}

Products

57 products across 19 variables (v0.2.0).

Precipitation (6 products)

IDSourceCoverageResolutionTimestepNotes
CHIRPSgeeglobal5 kmdailyGEE auth required
IMERG_PRECIPgeeglobal11 kmdailyGEE auth required
ERA5L_PRECIPgeeglobal11 kmdailyGEE auth required
GRIDMET_PRECIPhyriverCONUS4 kmdailyauth-free
DAYMET_PRECIPhyriverNORTH_AMERICA1 kmdailyauth-free
CHIRPS_IRIdirect_apiglobal5 kmdailyauth-free

Temperature — Tmax (4 products)

IDSourceCoverageResolutionTimestepNotes
GRIDMET_TMAXhyriverCONUS4 kmdailyauth-free
DAYMET_TMAXhyriverNORTH_AMERICA1 kmdailyauth-free
ERA5L_TMAXgeeglobal11 kmdailyGEE auth required
OPEN_METEO_TMAXdirect_apiglobal25 kmdailyauth-free; spatial support: point

Temperature — Tmin (4 products)

IDSourceCoverageResolutionTimestepNotes
GRIDMET_TMINhyriverCONUS4 kmdailyauth-free
DAYMET_TMINhyriverNORTH_AMERICA1 kmdailyauth-free
ERA5L_TMINgeeglobal11 kmdailyGEE auth required
OPEN_METEO_TMINdirect_apiglobal25 kmdailyauth-free; spatial support: point

Temperature — Tmean (1 product)

IDSourceCoverageResolutionTimestepNotes
ERA5L_TMEANgeeglobal11 kmdailyGEE auth required

Potential Evapotranspiration (PET) (4 products)

IDSourceCoverageResolutionTimestepNotes
MOD16_PETgeeglobal500 mmonthlyGEE auth required
ERA5L_PETgeeglobal11 kmdailyGEE auth required
GRIDMET_PEThyriverCONUS4 kmdailyauth-free
OPEN_METEO_PETdirect_apiglobal25 kmdailyauth-free; spatial support: point

Actual Evapotranspiration (ET) (3 products)

IDSourceCoverageResolutionTimestepNotes
MOD16_ETgeeglobal500 mmonthlyGEE auth required
TERRACLIMATE_AETgeeglobal4 kmmonthlyGEE auth required
OPENET_ENSEMBLEgeeCONUS30 mmonthlyGEE auth required

DEM (6 products)

IDSourceCoverageResolutionTimestepNotes
GLO30geeglobal30 mstaticGEE auth required
SRTMgeeglobal30 mstaticGEE auth required
DEM3DEP_10MhyriverCONUS10 mstaticauth-free
MERIT_DEMgeeglobal90 mstaticGEE auth required
GLO30_STACstacglobal30 mstaticauth-free
GLO30_ELEMENT84stacglobal30 mstaticauth-free

Soil Moisture (1 product)

IDSourceCoverageResolutionTimestepNotes
SMAP_SMgeeglobal9 kmdailyGEE auth required

Land Cover (4 products)

IDSourceCoverageResolutionTimestepNotes
NLCDhyriverCONUS30 mstaticauth-free
ESA_WORLDCOVERgeeglobal10 mstaticGEE auth required
DYNAMIC_WORLDgeeglobal10 mstaticGEE auth required
ESA_WORLDCOVER_STACstacglobal10 mstaticauth-free

Soil Properties (2 products)

IDSourceCoverageResolutionTimestepNotes
POLARIShyriverCONUS30 mstaticauth-free
SOILGRIDSgeeglobal250 mstaticGEE auth required

NDVI (2 products)

IDSourceCoverageResolutionTimestepNotes
MODIS_NDVIgeeglobal250 mmonthlyGEE auth required
SENTINEL2_NDVIgeeglobal10 mmonthlyGEE auth required

LAI (1 product)

IDSourceCoverageResolutionTimestepNotes
MODIS_LAIgeeglobal500 mmonthlyGEE auth required

Optical (5 products)

IDSourceCoverageResolutionTimestepNotes
SENTINEL2_SRgeeglobal10 mcompositeGEE auth required
LANDSAT9_SRgeeglobal30 mcompositeGEE auth required
LANDSAT8_SRgeeglobal30 mcompositeGEE auth required
SENTINEL2_SR_STACstacglobal10 mcompositeauth-free
LANDSAT_SR_STACstacglobal30 mcompositeauth-free

Streamflow (4 products)

IDSourceCoverageResolutionTimestepNotes
NWIS_STREAMFLOWdirect_apiCONUS0 mdailyauth-free; spatial support: gauge_point
GEOGLOWS_RETROgeoglows_retroglobal0 mdailyauth-free; spatial support: reach
OPENMETEO_FLOODopenmeteo_floodglobal5 kmdailyauth-free; spatial support: reach
GLOFAS_STREAMFLOWcds_glofasglobal5 kmdailyspatial support: reach

Bedrock Depth (1 product)

IDSourceCoverageResolutionTimestepNotes
OPENLANDMAP_BEDROCKgeeglobal250 mstaticGEE auth required

Flood Inundation (1 product)

IDSourceCoverageResolutionTimestepNotes
GFM_S1_INUNDATIONdirect_apiglobal20 meventauth-free

Geology (3 products)

IDSourceCoverageResolutionTimestepNotes
PYGEOGLIM_ALLpygeoglimglobal0 mstaticauth-free
GLIM_TILESpygeoglimglobal0 mstaticauth-free
GLHYMPS_TILESpygeoglimglobal0 mstaticauth-free

Impervious (2 products)

IDSourceCoverageResolutionTimestepNotes
NLCD_IMPERVIOUShyriverCONUS30 mstaticauth-free
GHSL_BUILT_UPgeeglobal100 mstaticGEE auth required

Population (3 products)

IDSourceCoverageResolutionTimestepNotes
GHSL_POPgeeglobal100 m5-yearly—
WORLDPOPgeeglobal100 mannual—
GPW_V411geeglobal927 m5-yearly—

Routing System

The router is a declarative policy table in routing/policy.py — no if/else chains, no source-specific logic. Adding a new product means adding one row.

fetch(variable, geometry, start, end)
        │
        ▼
detect_region(geometry)          ← CONUS? S_ASIA? EUROPE? global?
        │
        ▼
PRODUCT_POLICY[(variable, region)]   ← ordered list [primary, fallback1, fallback2, ...]
        │
        ▼
resolve_product(spec)            ← ProductSpec with backend_config
        │
        ▼
Backend.fetch_timeseries()       ← gee / hyriver / direct_api
        │
        ▼
FetchResult(data, product, source, citation, units, next_steps)

Region detection uses bounding-box math: if the geometry's centroid is inside the CONUS rectangle (−125° to −66°W, 24° to 50°N), the region is "CONUS". Otherwise a Pfafstetter level-2 table resolves to "S_ASIA", "EUROPE", "AFRICA", etc. Unknown regions fall to "global".

Fallback chain: if the primary product raises SourceUnavailable or times out, the pipeline walks down the policy list automatically.


Auth Setup

Google Earth Engine (23 products)

# 1. Install
pip install aihydro-data[gee]

# 2. Authenticate (one time — writes ~/.config/earthengine/credentials)
python -c "import ee; ee.Authenticate()"

# 3. Verify
python -c "from aihydro_data.mcp import data_doctor; print(data_doctor())"

GEE requires a Google account registered for Earth Engine. Academic / research use is free.

HyRiver (10 products)

No auth required. Just install:

pip install aihydro-data[hyriver]

Auth-free global products

No auth required:

pip install aihydro-data[opendap]    # CHIRPS_IRI — needs xarray + netCDF4
pip install aihydro-data[geoglows]   # GEOGLOWS_RETRO — AWS Zarr; needs s3fs + zarr
# NWIS_STREAMFLOW, OPEN_METEO_*, *_STAC all work with their respective extras; no auth

GloFAS (modelled global streamflow)

pip install aihydro-data[glofas]

# One-time: create a free Copernicus CDS account at cds.climate.copernicus.eu
# then add your token to ~/.cdsapirc:
# url: https://cds-beta.climate.copernicus.eu
# key: <your-api-key>

MCP Tools

aihydro-data ships 9 MCP tools that expose the full API to AI agents (Claude, etc.) via the AI-Hydro MCP server:

ToolDescription
data_fetchFetch a variable for a geometry and date range
data_batch_fetchFetch multiple geometries in one call
data_list_productsDiscover products by variable / region / source
data_describe_productFull spec for one product (citation, pitfalls, examples)
data_validate_requestDry-run validation before fetching (size estimate, date checks)
data_get_cache_statusInspect the disk cache
data_invalidate_cacheClear cached entries
data_doctorEnvironment check: auth, backends, cache, missing extras
data_helpBuilt-in onboarding guide (topics: auth, first_fetch, caching, …)

The tools are auto-registered when aihydro-data[mcp] is installed, via the aihydro.tools entry-point group.


Architecture

aihydro_data/
├── __init__.py          ← Public API: fetch(), list_products(), get_product()
├── fetch.py             ← Unified entry point
├── _pipeline.py         ← Routing → product resolution → backend dispatch → cache
├── contracts.py         ← ProductSpec, FetchRequest, FetchResult (Pydantic)
├── exceptions.py        ← Typed exceptions with agent-friendly .to_dict()
│
├── products/            ← Declarative variable registry (one file per variable)
│   ├── precipitation.py     6 products
│   ├── temperature.py       9 products (tmax/tmin/tmean + Open-Meteo)
│   ├── et.py                6 products (pet 4 + et 2)
│   ├── dem.py               5 products
│   ├── soil_moisture.py     1 product
│   ├── landcover.py         4 products
│   ├── soil.py              2 products
│   ├── vegetation.py        3 products (ndvi 2 + lai 1)
│   ├── optical.py           5 products
│   └── streamflow.py        4 products (NWIS + GEOGLOWS + Open-Meteo + GloFAS)
│
├── sources/             ← Backend adapters (lazy imports — safe without extras)
│   ├── base.py              SourceBackend ABC
│   ├── gee/                 GEE backend package (23 products)
│   │   ├── __init__.py          Backend class + fetch_timeseries/fetch_raster
│   │   ├── _download.py         raster download helpers
│   │   └── _composite.py        optical composite + spectral index helpers
│   ├── hyriver.py           HyRiver backend (10 products)
│   ├── direct_api.py        NWIS + CHIRPS IRI OPeNDAP + Open-Meteo (5 products)
│   ├── stac.py              STAC/Planetary Computer (4 products)
│   ├── geoglows_retro.py    GEOGLOWS v2 retrospective via AWS Zarr (1 product)
│   ├── openmeteo_flood.py   Open-Meteo river discharge (1 product)
│   ├── cds_glofas.py        GloFAS via Copernicus CDS (1 product)
│   ├── _common.py           require_import + assert_backend_available helpers
│   ├── _retry.py            call_with_retry for transient HTTP errors
│   └── _gee_vendored/       GEE auth + timeseries helpers
│
├── routing/
│   ├── regions.py           CONUS bbox, Pfafstetter region table
│   ├── detect.py            detect_region(geometry) → str
│   └── policy.py            PRODUCT_POLICY: (variable, region) → [product_ids]
│
├── geometry/
│   └── __init__.py          coerce_geometry() — normalises all input types
│
├── cache/
│   └── __init__.py          Disk cache at ~/.aihydro/cache/data/
│
├── mcp/
│   └── __init__.py          9 MCP tools (data_fetch, data_doctor, …)
│
└── help_topics/             Bundled markdown help (version-pinned to install)
    ├── first_fetch.md
    ├── auth.md
    ├── caching.md
    └── ...

Three orthogonal axes

AxisWhat it isWhere it lives
Variablewhat — precipitation, tmax, et, ndvi, dem, …products/<variable>.py
Source/Productwhere from — GridMET, CHIRPS, ERA5-Land, MOD16, …ProductSpec.backend_config
Regionwhere to — CONUS, S_ASIA, EUROPE, global, …routing/policy.py

A user asks for "precipitation over this watershed" — the router resolves all three axes automatically, or the user pins any/all of them manually.

Agent-friendly design

Every failure returns a structured envelope:

{
    "error": True,
    "code": "GEE_AUTH_MISSING",
    "message": "Google Earth Engine credentials not found.",
    "recovery": "Run `python -c \"import ee; ee.Authenticate()\"`",
    "next_tools": ["data_doctor", "data_help"],
    "docs_anchor": "auth#gee"
}

Every success carries citation, bibtex, units, license, and next_steps so agents can chain downstream tools without re-planning.


Contributing

Adding a new product

  1. Add a ProductSpec to the relevant products/<variable>.py (or create a new file).
  2. Add a row to routing/policy.py.
  3. If it's a new backend, add a Backend subclass to sources/.
  4. Run pytest -m "not live" — no live credentials needed for the offline suite.

Running tests

# Offline suite (no network, no auth — ~7 seconds)
pytest -m "not live"

# Live sweep — tests all 57 products against real backends (~15 minutes)
# Requires GEE auth + internet
pytest tests/test_live_sweep.py -v

Status

v0.2.1 — STAC robustness + metadata repair: retry+backoff in STAC backend; GLO30_ELEMENT84; impervious + bedrock_depth variables; and corrected aihydro-core dependency metadata so downstream aihydro-watershed/aihydro-tools resolve with aihydro-core>=0.2. Shipped with 54 products in 18 variables and 369 offline tests (current counts: see Products).

v0.2.0 — First public PyPI release. Global streamflow tri-source chain (GEOGLOWS/Open-Meteo/GloFAS); spatial-support honesty (point vs areal vs reach products declared and enforced); verify-on-read cache; region and outlet kwargs; structural refactor (gee/ package, MCP @_tool_envelope); 341 offline tests.

See examples/cookbook.ipynb for working recipes.

PhaseStatusDescription
1: Scaffold✅Package structure, contracts, registry
2: Precipitation vertical✅6 products, routing, fallback chain
3: CONUS migration✅Streamflow, temperature, landcover, soil
4: Global gap-filling✅ET, DEM, soil moisture, vegetation
5: Cache + provenance✅Disk cache, manifest, license tracking
6: Batch fetching✅Multi-geometry parallel dispatch
7: MCP tools✅9 tools, help topics, doctor
8: PyPI publish✅v0.2.0 on PyPI (pip install aihydro-data)


Citation

If you use aihydro-data in your research, please cite it:

@software{galib2025aihydrodata,
  author    = {Galib, Mohammad},
  title     = {aihydro-data: Global Hydrology Dataverse for the AI-Hydro Platform},
  year      = {2025},
  publisher = {Zenodo},
  doi       = {10.5281/zenodo.20823443},
  url       = {https://doi.org/10.5281/zenodo.20823443}
}

License

Apache-2.0. Data products carry their own licenses — always check result.license or data_describe_product(id).

IMERG daily precipitation contract

IMERG_PRECIP integrates the native half-hourly rates into complete UTC daily totals. Date-only start/end are inclusive for this product. Missing, duplicate or invalid intervals fail instead of producing partial totals. Returned interval_count, expected_interval_count and source_status columns expose temporal support and product maturity. Provisional or unknown status must not be cited as verified Final data. This check does not establish spatial completeness or observational accuracy. See the decision and limitations and implementation plan.

GFM observational failures and synthetic fixtures

fetch_gfm_extent raises on network/asset failures; it never substitutes synthetic polygons. use_fixture=True explicitly selects synthetic test data, without an observational citation. Inspect status: no acquisitions and no valid pixels are distinct from no flood detected. Per-item acquisition times and assets remain available for review. Automatic model-validation scores are withheld until a jointly valid comparison footprint is established; an extent alone is insufficient. See the flood-reference decision.

CHIRPS versions and cached identity

CHIRPS currently routes to v3 daily SAT on GEE. CHIRPS_IRI serves v2 daily-improved; fallback between them changes the scientific product. V3 SAT uses IMERG Late V07 for daily partitioning of pentad totals, so comparison with IMERG must account for shared inputs.

Inspect result.product_identity (also returned by MCP) for version, variant, derivation, dependencies and interpretation metadata. New caches retain the identity and warnings captured at fetch time. Empty legacy identity means unknown. These are configured-product declarations, not verification of upstream asset bytes or revision. See the identity decision.

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Versions 1

Collected releases
VersionPublishedDirect dependenciesOpen version
0.2.420 Oct 202641

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Repository

PENDING
ai-hydro/ai-hydro

Shared repository data · GitHub

License detected in repository
Not collected

Repository

PENDING
ai-hydro/aihydro-data

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 21 Oct, 10:56 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:eb03843d7dec54083e26bdb4b8bdb8906b3ad7ab6effa6f76f24f251d7425821