Observed since 21 Oct, 10:56 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Global large-sample hydrology — CAMELS-like catchment attributes for any watershed (recipe layer over aihydro-data + aihydro-watershed)
Global large-sample hydrology — CAMELS-like catchment attributes for globally routed watersheds, with source-specific caveats.
A recipe layer over aihydro-data and aihydro-watershed that computes 101 static scalar attributes (8 families) plus daily meteorological forcing timeseries and temporal-tier extensions (trends, annual dynamics, drought/flood events) for basin geometries through region-aware product routing. The camels-attrs CONUS reference implementation is used to validate recipes against USGS gauge data; use aihydro_lsh.reliability.reliability_matrix() for the family/region caveats that qualify global interpretation.
import geopandas as gpd
from shapely.geometry import box
import aihydro_lsh
# Any watershed polygon (EPSG:4326)
geom = gpd.GeoDataFrame(geometry=[box(-79.5, 38.5, -77.5, 39.5)], crs="EPSG:4326")
result = aihydro_lsh.attributes(geom, "1990-01-01", "2020-12-31")
print(result.attrs["p_mean"]) # mean daily precip (mm/day)
print(result.attrs["elev_mean"]) # mean elevation (m)
print(result.attrs["geol_porosity"]) # GLHYMPS porosity
df = result.to_dataframe() # single-row DataFrame, 101 columns
# Full provenance report (agent-relay verbatim — package is the authority)
print(result.to_report())
# Fully JSON-serializable dict (NaN → null)
d = result.to_json()
print(d["meta"]["content_hash"]) # SHA-256 fingerprint of attrs
# USGS gauge — auto-delineate then extract (outlet coords wired automatically)
from aihydro_watershed.delineation.router import delineate_from_point
ws = delineate_from_point(39.27, -77.54) # Potomac Point of Rocks
result = aihydro_lsh.attributes(ws, "1990-01-01", "2020-12-31",
gauge_id="01638500")
# ws is a HydroResult — outlet_lat/outlet_lon extracted automatically for geomorphic attrs
# Daily forcing timeseries (for ML workflows — LSTM, HBV training)
df = aihydro_lsh.forcing(geom, "1990-01-01", "2020-12-31")
print(df.columns.tolist())
# ['prcp_mm', 'tmin_C', 'tmax_C', 'tavg_C', 'pet_mm', 'srad_Wm2', 'wind_ms', 'sph_kgkg']
# Combined: scalar attrs + daily forcing in one call
result = aihydro_lsh.attributes(geom, "1990-01-01", "2020-12-31", include_forcing=True)
print(result.forcing) # pd.DataFrame with DatetimeIndex
# Temporal extensions: Mann-Kendall trend scalars (T2.5)
result = aihydro_lsh.attributes(geom, "1990-01-01", "2020-12-31", include_trends=True)
print(result.trends) # pd.DataFrame — 16 slope/p-value scalars
# Temporal extensions: annual dynamic panels (T3)
result = aihydro_lsh.attributes(geom, "1990-01-01", "2020-12-31", include_dynamic=True)
print(result.dynamic) # pd.DataFrame (year × 22 variables)
# Temporal extensions: drought and flood event inventory (T3)
result = aihydro_lsh.attributes(geom, "1990-01-01", "2020-12-31", include_events=True)
print(result.events["drought"]) # DataFrame of SPI-drought episodes
print(result.events["flood"]) # DataFrame of POT-flood exceedances
# Everything at once
result = aihydro_lsh.attributes(
geom, "1990-01-01", "2020-12-31",
include_forcing=True,
include_trends=True,
include_dynamic=True,
include_events=True,
)
# Batch extraction (list of pour-points → single DataFrame)
df, errors = aihydro_lsh.batch_attributes(
[{"id": "01638500", "lat": 39.27, "lon": -77.54},
{"id": "02108000", "lat": 35.53, "lon": -79.09}],
start="1990-01-01", end="2020-12-31",
)
df.to_csv("camels_global.csv", index=False)
pip install -e . # editable (ecosystem tree)
pip install -e .[validate] # + camels-attrs CONUS reference
pip install -e .[mcp] # + MCP server tools
| Family | Attrs | Data source | Notes |
|---|---|---|---|
| watershed | 5 | GDF geometry | area, perimeter, compactness, elongation, form_factor |
| topography | 7 | GLO30 / DEM3DEP / SRTM | elev stats, slope stats, area |
| climate | 14 | GRIDMET / ERA5-Land / CHIRPS | aridity, seasonality, snow frac, high/low precip stats |
| soil | 9 | POLARIS / SoilGrids | texture, Ksat; porosity/AWC/FC from Saxton-Rawls PTF |
| geomorphic | 28 | 3DEP/GLO30 + NLCD/ESA WorldCover | shape, relief, stream network, CN, sinks, land cover % |
| vegetation | 13 | MODIS LAI/NDVI + ESA WorldCover | LAI/GVF seasonal stats, LC fractions, root depth |
| geology | 9 | pygeoglim (GLiM + GLHYMPS) | lithology classes, carbonate frac, permeability, porosity; global path is permission/tile-gated |
| hydrology | 17 | NWIS (CONUS) / GEOGLOWS (global) | q_mean, BFI, FDC slope, half-flow date, … |
| forcing | timeseries | GRIDMET (CONUS) / ERA5-Land (global) | Daily: prcp_mm, tmin_C, tmax_C, tavg_C, pet_mm [+ srad, wind, sph] |
geomorphic runs after soil so clay_frac is available for NRCS Curve Number soil group estimation.
forcing is opt-in (not in the default RECIPE_ORDER) — request via include_forcing=True or families=["forcing"].
The static families above are T1 (permanent) and T2 (window-mean over the caller's date range). Three optional tiers extend the attribute landscape:
| Tier | Flag | Output | Description |
|---|---|---|---|
| T2.5 — Trends | include_trends=True | result.trends — up to 16 slope/p-value scalars | Mann–Kendall p-value and Sen slope for eight derived series: precipitation mean, temperature mean, PET mean, snow fraction, LAI maximum, GVF mean, discharge mean and runoff ratio. At least ten valid annual values are required. P-values do not yet adjust for serial dependence or multiple comparisons. |
| T3 — Dynamic | include_dynamic=True | result.dynamic — year × 22-column schema | Annual climate, vegetation and streamflow summaries. Three annual land-cover columns are currently unavailable and filled with NaN. Other columns can also be missing by source/year; this is a schema, not 22 measured values in every year. Annual q5 is high flow (5% exceedance) and q95 is low flow. |
| T3 — Events | include_events=True | result.events["drought"] + result.events["flood"] | SPI-based drought episodes (McKee et al. 1993) and peaks-over-threshold flood exceedances (Coles 2001). Each row is a discrete event with start/end date, duration, severity/magnitude. |
The temporal extensions are opt-in so standard attributes() calls remain fast. All three can be combined with include_forcing=True in a single call.
attributes() accepts any of:
HydroResult from delineate_from_point() / delineate_watershed() — outlet coords auto-wiredGeoDataFrame (EPSG:4326, single or multi-row)(lat, lon) tuple — wrapped as 0.001° buffer (use full delineation for accurate basin-mean attrs)(minx, miny, maxx, maxy) bbox tupleEvery AttributeResult carries:
.provenance — per-family AttrProvenance (source, product, region, resolution_m, is_observed).meta — ResultMeta with package versions, bbox, timing, and a SHA-256 content hash.to_report() — human-readable report; agents relay this verbatim (package is the authority).to_json() — fully JSON-serializable dict (NaN → null)The is_observed flag distinguishes gauge data (NWIS, is_observed=True) from modelled or
reanalysis products (GEOGLOWS, ERA5-Land, is_observed=False) — critical for uncertainty-aware
interpretation by agents. For hydrology specifically, gauge-less/global streamflow signatures
carry quality_flags including modelled_streamflow; AttributeResult.to_report() emits a
mandatory MODELLED STREAMFLOW CAVEAT so BFI/FDC/flood-frequency/timing metrics are not
mistaken for observed catchment properties.
aihydro-core ← aihydro-data ← aihydro-lsh
aihydro-watershed ↗
aihydro-lsh may depend DOWN on core/data/watershed, never UP on aihydro-tools.
Enforced by the AST guard in tests/test_layering.py and import-linter.
When aihydro-lsh[mcp] is installed alongside aihydro-tools, ten tools are
auto-registered on the shared server:
| Tool | Description |
|---|---|
lsh_attributes | All 101 static scalar attrs for a polygon or pour-point |
lsh_forcing | Daily basin-averaged meteorological timeseries (LSTM/HBV) |
lsh_dynamic_attributes | Annual dynamic panels (T3) — year × 22 variables |
lsh_events | Drought (SPI) and flood (POT) event inventories (T3) |
lsh_families | List families + attribute keys |
lsh_attribute_info | Unit, source, family, formula, CAMELS equivalent, and caveat for one attribute |
lsh_batch | Multi-basin extraction from JSON array |
lsh_compare_to_ref | lsh vs camels-attrs side-by-side (CONUS) |
lsh_recipe_status | Check which recipes can run (auth/deps) |
lsh_reliability_matrix | Family/region availability status and interpretation caveats |
For comparative studies, keep product chains explicit. The helper
aihydro_lsh.batch.summarize_product_homogeneity(results) reports whether each
attribute family used one product across a result set or mixed products such as
CHIRPS and ERA5-Land. Mixed products are valid but should be reported as a
source of uncertainty rather than interpreted as purely hydrologic contrast.
aihydro_lsh.reliability.reliability_matrix() returns a code-readable status
for each attribute family by region (reliable, available_with_caveats, or
not_available). It is intentionally static and import-light: it does not prove
live network/data availability, but it prevents binary “global” claims from
dropping caveats such as modelled streamflow, product-switching uncertainty, and
permission/tile availability for geology. Geology provenance includes
global_tiles_status=... from pygeoglim.global_tiles_status() where available.
# CI gate — offline, reads the pre-generated fixture, no network required
pytest tests/test_conus_parity.py -k Offline --tb=short
# Regenerate the fixture (network + GEE + pygeoglim HF token required;
# several minutes per gauge)
python scripts/gen_conus_parity_fixture.py
# Live spot-check against a fresh live run instead of the cached fixture
pytest tests/test_conus_parity.py -m live --tb=short
The parity suite is parameterized by tests/fixtures/conus_parity_panel.json
— inspect that file for the current basin panel and how each gauge was
selected (stratified by aridity x snow fraction x relief using
pygeohydro.get_camels()'s real 671-station CAMELS metadata). It checks:
What this does and does not establish: this validates that aihydro-lsh
reproduces camels-attrs on CONUS — it does not validate the global attribute
claim (no non-CONUS ground truth exists in this chain, and gauge-less
hydrology signatures are computed from modelled streamflow with no observed
reference to check against anywhere). See papers/DECISIONS.md PD-4 for the
claim-scoping decision this follows, and
scripts/gen_parity_tolerance_table.py for the per-attribute divergence
table generated from the fixture.
Attribute-level citations are returned in AttributeResult.citations.
Key references:
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.2.1 | 20 Oct 2026 | 18 |
This describes our collected data; it is not a security assessment.
Observed since 21 Oct, 10:56 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:5916002dddab35ed17d77dc6db53ea2ada4bda5f60b3c1d293aa8a590189c4bf