Drought raster indicators — Copernicus EDO / GDO¶
The earthlens.drought backend is mixed-output: the US Drought Monitor route
returns vector polygons (see the quickstart), while the
Copernicus European / Global Drought Observatory route returns raster
GeoTIFFs. This notebook teaches that raster route end-to-end — download an
indicator for a date and bounding box, open the GeoTIFF, and map it.
EDO/GDO is a Copernicus REST shim, not a conformant OGC WCS server: only its
GetCoverage operation is reliable. The backend builds the documented
GetCoverage URL by hand (TIME + SELECTED_TIMESCALE + a SUBSET=Long/Lat
bbox), streams the GeoTIFF, and opens it through
pyramids — no owslib, no GDAL
WCS driver. Because the resolved dataset is raster, download() returns a
list[Path] of written GeoTIFFs (one per requested period), not an in-memory
collection.
Setup¶
Imports and a scratch output directory. The raster routes write GeoTIFFs to path/, so every raster request needs an explicit path=.
import tempfile
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
from earthlens import EarthLens
from earthlens.drought import Catalog
from pyramids.dataset import Dataset
OUT = Path(tempfile.mkdtemp(prefix="edo-"))
OUT
2026-07-01 12:40:43 | INFO | pyramids.base.config | Logging is configured.
WindowsPath('C:/Users/main/AppData/Local/Temp/edo-kruz60d9')
Quickstart — one EDO indicator, one date, one bbox¶
edo-spaST is the SPI (ERA5) short-term indicator — a standardised
precipitation index. We request a single date over a small European box. The
facade routes the drought key to the backend; dataset= picks the catalog
row, lat_lim / lon_lim are the bounding box, and path= is where the
GeoTIFF lands.
| argument | meaning | value here |
|---|---|---|
dataset |
catalog row id | edo-spaST (SPI ERA5 short-term) |
start / end |
inclusive date window | a single day, 2025-12-21 |
lat_lim / lon_lim |
bounding box (degrees) | central Europe |
path |
output directory (required for raster) | the scratch dir |
paths = EarthLens(
data_source="drought",
dataset="edo-spaST",
variables=[],
start="2025-12-21",
end="2025-12-21",
lat_lim=[40.0, 50.0],
lon_lim=[5.0, 15.0],
path=str(OUT),
).download(progress_bar=False)
paths
2026-07-01 12:40:53.942 | INFO | earthlens.drought.backend:download:719 - EDO/GDO: Copernicus European/Global Drought Observatory (EMS) — free reuse with attribution to Copernicus EMS.
[WindowsPath('C:/Users/main/AppData/Local/Temp/edo-kruz60d9/edo-spaST_20251221.tif')]
download() returned a list[Path] — one GeoTIFF for the single requested date. That list-of-paths shape is what every raster drought dataset returns.
Inspect the GeoTIFF¶
Open the written raster with pyramids.dataset.Dataset. It reports the CRS
(EPSG:4326, as the catalog row declares), the pixel grid, and the geographic
bounds — which should match the box we asked for.
ds = Dataset.read_file(str(paths[0]))
print("CRS EPSG:", ds.epsg)
print("grid (rows x cols):", ds.rows, "x", ds.columns)
print("bounds [x_min, y_min, x_max, y_max]:", ds.bbox)
CRS EPSG: 4326 grid (rows x cols): 40 x 1440 bounds [x_min, y_min, x_max, y_max]: [-180.0, 40.0, 180.0, 50.0]
Map the indicator¶
Read the single band into a NumPy array, mask the no-data fill, and draw it with its geographic extent so the axes are longitude / latitude. Diverging colours suit a standardised index (negative = drier than normal, positive = wetter).
band = ds.read_array()
nodata = ds.no_data_value[0] if ds.no_data_value else None
grid = np.ma.masked_equal(band, nodata) if nodata is not None else band
x_min, y_min, x_max, y_max = ds.bbox
fig, ax = plt.subplots(figsize=(7, 6))
im = ax.imshow(grid, extent=[x_min, x_max, y_min, y_max],
origin="upper", cmap="RdYlBu")
fig.colorbar(im, ax=ax, label="SPI (ERA5, short-term)")
ax.set_title("EDO SPI short-term — 2025-12-21")
ax.set_xlabel("longitude"); ax.set_ylabel("latitude")
plt.tight_layout()
plt.show()
GDO is the same route, on global data¶
The European (EDO) and Global (GDO) observatories share a single Copernicus
WCS map (map=DO_WCS); the coverages carry global data, so the "European vs
global" split is purely the bounding box you pass. gdo-smand is the ensemble
soil-moisture anomaly. Here we request it over the Horn of Africa — same call
shape, same list[Path] result, a different corner of the same global grid.
gdo_paths = EarthLens(
data_source="drought",
dataset="gdo-smand",
variables=[],
start="2024-06-21",
end="2024-06-21",
lat_lim=[-5.0, 15.0],
lon_lim=[30.0, 52.0],
path=str(OUT),
).download(progress_bar=False)
gdo = Dataset.read_file(str(gdo_paths[0]))
band = gdo.read_array()
nodata = gdo.no_data_value[0] if gdo.no_data_value else None
grid = np.ma.masked_equal(band, nodata) if nodata is not None else band
x_min, y_min, x_max, y_max = gdo.bbox
fig, ax = plt.subplots(figsize=(6, 6))
im = ax.imshow(grid, extent=[x_min, x_max, y_min, y_max],
origin="upper", cmap="BrBG")
fig.colorbar(im, ax=ax, label="soil-moisture anomaly")
ax.set_title("GDO ensemble soil-moisture anomaly — Horn of Africa, 2024-06-21")
ax.set_xlabel("longitude"); ax.set_ylabel("latitude")
plt.tight_layout()
plt.show()
2026-07-01 12:40:58.591 | INFO | earthlens.drought.backend:download:719 - EDO/GDO: Copernicus European/Global Drought Observatory (EMS) — free reuse with attribution to Copernicus EMS.
Discover the available indicators¶
The catalog carries every EDO and GDO indicator as its own row. Each raster row
declares transport: edo-wcs, its Copernicus coverage id, a cadence, and
the timescale (the SELECTED_TIMESCALE window). Browse them straight off the
catalog.
cat = Catalog()
edo = sorted(i for i in cat.datasets if i.startswith("edo-"))
gdo = sorted(i for i in cat.datasets if i.startswith("gdo-"))
print(f"{len(edo)} EDO + {len(gdo)} GDO indicators")
row = cat.get("edo-spaST")
row.transport, row.coverage, row.cadence, row.timescale
17 EDO + 19 GDO indicators
('edo-wcs', 'spaST', '10day', '01')
Takeaway¶
- The Copernicus EDO / GDO route is the drought backend's raster face:
download()returns alist[Path]of GeoTIFFs, one per requested period. - The backend hits Copernicus'
GetCoverageREST endpoint directly — no WCS handshake — and opens the bytes withpyramids. - EDO and GDO ride one global
DO_WCSmap; the bounding box you pass decides which region you get. - Open any output with
pyramids.dataset.Datasetto inspect or map it.
Next: the catalog explorer for the full dataset list, or the quickstart for the USDM vector route.