Skip to main content

Composite Drought Index via GEE + COG output + climate-science plots

Project description

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
20 years 20 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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

drought_monitoring-0.1.7.tar.gz (36.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

drought_monitoring-0.1.7-py3-none-any.whl (34.9 kB view details)

Uploaded Python 3

File details

Details for the file drought_monitoring-0.1.7.tar.gz.

File metadata

  • Download URL: drought_monitoring-0.1.7.tar.gz
  • Upload date:
  • Size: 36.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for drought_monitoring-0.1.7.tar.gz
Algorithm Hash digest
SHA256 2369040a5ae6aa057a918f32fd39b515f59994bef0198746dfaefed6300683e4
MD5 b194da50c7e8f7acd904e053802f99b2
BLAKE2b-256 6d3adc5af9b87dd3699d7e504ff4618997dffb5ba0e5e78e83a5efff2e560368

See more details on using hashes here.

File details

Details for the file drought_monitoring-0.1.7-py3-none-any.whl.

File metadata

File hashes

Hashes for drought_monitoring-0.1.7-py3-none-any.whl
Algorithm Hash digest
SHA256 ff1d18e63244f3b4333cd0014af2d7a68c47f4878242990955db77abcef6d485
MD5 025005968acf25c3c96af834a027ceef
BLAKE2b-256 b9d942ec0d8f6f5c6944fdd474c1aca46d30d0f297e81a1da387ce3369bb6c5e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page