Skip to main content

drought-monitoring

A Python package for computing the Composite Drought Index (CDI) over 20–30 year monitoring periods using Google Earth Engine, with in-memory dask-parallelised spatial computation, Cloud Optimized GeoTIFF export, and publication-quality climate science visualisations.

PyPI version Python License: MIT


Installation

# pip
pip install drought-monitoring

# uv
uv add drought-monitoring

# with all optional dependencies (GEE + COG + plots)
pip install "drought-monitoring[all]"

Optional extras:

Extra Installs Use for
gee earthengine-api fetching GEE data
cog rasterio, rioxarray COG export / import
plot matplotlib visualisation
all all of the above full workflow

Package structure

drought_monitoring/
├── core.py      CDI mathematics on pd.Series
├── spatial.py   pixel-wise computation on xr.DataArray (dask-parallelised)
├── gee.py       GEE authentication + ERA5-Land / MODIS xee cube fetching
├── io.py        Cloud Optimized GeoTIFF (COG) export and import
└── plot.py      publication-quality climate science figures
tests/
└── test_core.py
pyproject.toml
README.md

Typical Jupyter notebook workflow

See workshop/drought_monitoring_TUK_workshop.ipynb for the full worked example (Turkana County, Kenya, 2000–2025).

1. Authenticate GEE

from drought_monitoring.gee import authenticate
authenticate(project="my-gee-project")   # opens browser on first use

2. Define your Area of Interest

# Format: [lon_min, lat_min, lon_max, lat_max]
aoi = [34.5, 1.5, 36.5, 5.5]   # Turkana County, Kenya

3. Fetch 25 years of monthly data

from drought_monitoring.gee import fetch_era5_precip, fetch_era5_temp, fetch_modis_ndvi

precip = fetch_era5_precip(aoi, start_year=2000, end_year=2025)
temp   = fetch_era5_temp(aoi,   start_year=2000, end_year=2025)
ndvi   = fetch_modis_ndvi(aoi,  start_year=2000, end_year=2025)
# Each returns a pd.Series with a monthly DatetimeIndex (312 months)

4. Compute the full CDI

from drought_monitoring import compute_all

df = compute_all(precip, temp, ndvi, window=3, weights=(0.50, 0.25, 0.25))
# pd.DataFrame with columns: PDI, TDI, VDI, CDI

5. Classify drought severity

from drought_monitoring.plot import classify_cdi

df["severity"] = classify_cdi(df["CDI"])
print(df["severity"].value_counts(normalize=True).mul(100).round(1))

6. Publication-quality plots

from drought_monitoring.plot import plot_timeseries, plot_anomaly_bars, plot_seasonal_cycle
import matplotlib.pyplot as plt

fig = plot_timeseries(
    df,
    title    = "Composite Drought Index — Turkana County, Kenya",
    subtitle = "ERA5-Land + MODIS MOD13A3  |  2000–2025",
    show_components   = True,
    show_severity_bar = True,
)
fig.savefig("CDI_timeseries_Turkana.png", dpi=150, bbox_inches="tight")

fig2 = plot_anomaly_bars(df, column="CDI", freq="Y",
                         title="Annual Mean CDI Anomaly — Turkana County")
fig2.savefig("CDI_annual_Turkana.png", dpi=150, bbox_inches="tight")

fig3 = plot_seasonal_cycle(df, title="Seasonal Cycle — Turkana 2000–2025")
fig3.savefig("CDI_seasonal_Turkana.png", dpi=150, bbox_inches="tight")

7. Generate annual spatial CDI maps

from drought_monitoring.gee import yearly_drought_maps

# Streams ERA5 + MODIS via xee, computes CDI pixel-wise with dask,
# resamples to annual means — nothing written to disk
ds = yearly_drought_maps(aoi, start_year=2000, end_year=2025)
# xr.Dataset with variables: PDI, TDI, VDI, CDI  |  dims: (time, lat, lon)

ds["CDI"].plot(col="time", col_wrap=4, cmap="RdBu", robust=True, figsize=(18, 14))

8. Export to Cloud Optimized GeoTIFFs

import os
from drought_monitoring.io import cdi_stack_to_cog

os.makedirs("outputs", exist_ok=True)
paths = cdi_stack_to_cog(ds, output_dir="outputs/", prefix="Turkana_2000_2025")
# outputs/Turkana_2000_2025_PDI.tif  (26 bands, one per year)
# outputs/Turkana_2000_2025_TDI.tif
# outputs/Turkana_2000_2025_VDI.tif
# outputs/Turkana_2000_2025_CDI.tif

9. Visualise COGs interactively

import leafmap

m = leafmap.Map(center=[3.5, 35.5], zoom=7)
m.add_cog_layer("outputs/Turkana_2000_2025_CDI.tif", name="CDI")
m

10. Run a 6-month drought forecast

from drought_monitoring.forecast import forecast_all_statistical
from drought_monitoring.plot import plot_forecast

fc = forecast_all_statistical(
    precip, temp, ndvi,
    n_months = 6,
    weights  = (0.50, 0.25, 0.25),
    ci_level = 0.90,
)
# pd.DataFrame with columns: PDI, TDI, VDI, CDI, CDI_lower, CDI_upper, lead

fig_fc = plot_forecast(
    history   = df,
    forecast  = fc,
    n_history = 36,
    title     = "Turkana CDI — 6-month Drought Outlook",
    subtitle  = "STL + SARIMA statistical forecast  |  90% confidence band",
    show_components = True,
)
fig_fc.savefig("CDI_forecast_Turkana.png", dpi=150, bbox_inches="tight")

Area-averaged time series workflow

For a single-pixel or spatially-averaged CDI time series:

from drought_monitoring.gee import fetch_era5_precip, fetch_era5_temp, fetch_modis_ndvi
from drought_monitoring import compute_all
from drought_monitoring.plot import plot_timeseries, plot_anomaly_bars

precip = fetch_era5_precip(aoi, start_year=2000, end_year=2020)
temp   = fetch_era5_temp(aoi,   start_year=2000, end_year=2020)
ndvi   = fetch_modis_ndvi(aoi,  start_year=2000, end_year=2020)

df = compute_all(precip, temp, ndvi, window=3)
# pd.DataFrame with columns: PDI, TDI, VDI, CDI

fig = plot_timeseries(df, title="CDI — Borena Region",
                      subtitle="ERA5-Land + MODIS MOD13A3  |  2000–2020")
fig.savefig("CDI_timeseries.png", dpi=300, bbox_inches="tight")

fig2 = plot_anomaly_bars(df, title="Annual Mean CDI Anomaly")
fig2.savefig("CDI_annual.png", dpi=300, bbox_inches="tight")

Data sources

Variable GEE collection Band Units
Precipitation ECMWF/ERA5_LAND/MONTHLY_AGGR total_precipitation_sum mm month⁻¹
Temperature ECMWF/ERA5_LAND/MONTHLY_AGGR temperature_2m °C
NDVI MODIS/061/MOD13A3 NDVI [−1, 1]

CDI formula

Each sub-index follows Burchard-Levine et al. (2024):

DI = (μ_IP / μ_LTM) × sqrt(RL_IP / RL_LTM)

CDI = 0.50 × PDI + 0.25 × TDI + 0.25 × VDI
Symbol Meaning
μ_IP Trailing rolling mean over the interest period (default 3 months)
μ_LTM Long-term mean of μ_IP for that calendar month
RL_IP Count of months inside the IP where the anomaly condition holds
RL_LTM Long-term mean of RL_IP for that calendar month

PDI & VDI use deficit streaks (raw < monthly LTM). TDI uses excess streaks (raw > monthly LTM).


CDI severity classes

CDI value Class
< 0.50 Extreme drought
0.50 – 0.65 Severe drought
0.65 – 0.80 Moderate drought
0.80 – 0.90 Mild drought
0.90 – 1.10 Near normal
1.10 – 1.20 Mild wet
1.20 – 1.30 Moderately wet
> 1.30 Very wet

Values < 1 indicate drought; values ≈ 1 are near-normal; values > 1 are wetter than normal.


Monitoring period

Default Minimum Maximum
5 years 5 years 30 years

An error is raised if the requested period is outside this range.


COG structure

Each output GeoTIFF contains one band per timestep. Band descriptions are ISO-8601 date strings (YYYY-MM or YYYY), so every file is self-documenting and can be range-requested from cloud storage (S3, GCS, Azure Blob) by leafmap, QGIS, or any GDAL tool.


Running tests

pytest tests/ -v

Reference

Based on: Burchard-Levine, V. et al. (2024). pyCDI: a Python implementation of the composite drought index. EO-Africa R&D, ESA. https://github.com/VicenteBurchard/pyCDI

Metadata

Release files for drought-monitoring 0.1.8

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for drought-monitoring 0.1.8
File Size Uploaded Provenance Actions
drought_monitoring-0.1.8.tar.gz 36.2 kB Without provenance Details for drought_monitoring-0.1.8.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for drought-monitoring 0.1.8
File Interpreter ABI Platform Actions
drought_monitoring-0.1.8-py3-none-any.whl Python 3 none any Details for drought_monitoring-0.1.8-py3-none-any.whl

Total release size: 70.7 kB

Release files / drought_monitoring-0.1.8.tar.gz

Download URL drought_monitoring-0.1.8.tar.gz
Size 36.2 kB
Tags Source
SHA-256 checksum
How to use checksums
57c1003538ec51504a7076d8aafe51c4b7d41be87dd5f4e2cad12418f9a5c669
BLAKE2b-256 checksum
How to use checksums
1cc9836150f727afed4b7fac501e2ba5dc58d7305c46d1c0cc3b643fca29b51c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.23 {"installer":{"name":"uv","version":"0.11.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / drought_monitoring-0.1.8-py3-none-any.whl

Download URL drought_monitoring-0.1.8-py3-none-any.whl
Size 34.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
83b378beafd8626b2e8b484e2e9d9eda87d1e14950e1f20fa7fd1775d51be3f2
BLAKE2b-256 checksum
How to use checksums
1b108354f6cc992473274e6f02e50971bb16bca5358da10331f7f346a3b59c3e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.23 {"installer":{"name":"uv","version":"0.11.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for drought-monitoring 0.1.8

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page