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.
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
1. Authenticate GEE
from drought_monitoring.gee import authenticate
authenticate(project="my-gee-project") # opens browser on first use
2. Generate yearly drought maps (full in-memory pipeline)
from drought_monitoring.gee import yearly_drought_maps
aoi = [38.0, 3.5, 42.5, 7.0] # [lon_min, lat_min, lon_max, lat_max]
# Borena region, Southern Ethiopia
# 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=2020)
# xr.Dataset with variables: PDI, TDI, VDI, CDI
# dims: (year, latitude, longitude)
3. Plot all years
ds["CDI"].plot(col="time", col_wrap=4, cmap="RdBu", robust=True, figsize=(20, 14))
4. Export to Cloud Optimized GeoTIFFs
from drought_monitoring.io import cdi_stack_to_cog
paths = cdi_stack_to_cog(ds, output_dir="outputs/", prefix="Borena_2000_2020")
# outputs/Borena_2000_2020_PDI.tif
# outputs/Borena_2000_2020_TDI.tif
# outputs/Borena_2000_2020_VDI.tif
# outputs/Borena_2000_2020_CDI.tif
5. Visualise COGs interactively
import leafmap
m = leafmap.Map(center=[5.0, 40.0], zoom=6)
m.add_cog_layer("outputs/Borena_2000_2020_CDI.tif", name="CDI")
m
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
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 drought_monitoring-0.1.2.tar.gz.
File metadata
- Download URL: drought_monitoring-0.1.2.tar.gz
- Upload date:
- Size: 28.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fedd38db3291fa571ee9087256b52f3b562a26ae955b683ee4bcc942ded7e3a6
|
|
| MD5 |
62702a1f78b505bceb7d0a2fd0daa232
|
|
| BLAKE2b-256 |
de9adf63a20dd852e3f57c20568ad842a29d4cfd1ea37149802ced59cd50b3c5
|
File details
Details for the file drought_monitoring-0.1.2-py3-none-any.whl.
File metadata
- Download URL: drought_monitoring-0.1.2-py3-none-any.whl
- Upload date:
- Size: 26.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
93602d0890cf0bf2ca73336ea0759435e7ecd58e836d7798ea2c0921835db9cd
|
|
| MD5 |
98cb211ba28422f076c008e49cfa8c6f
|
|
| BLAKE2b-256 |
2c0d7762e2a1a15e1002b1dd1576afa385a1cdbcaed91111e882ee6eea3229c7
|