Drought quickstart — US Drought Monitor weekly polygons¶
Pull the most recent US Drought Monitor (USDM) weekly drought-class polygons through the EarthLens
facade. USDM releases one composite every Thursday UTC, valid the prior Tuesday, with five
features per release — one polygon per drought class D0 (abnormally dry) through D4
(exceptional drought). The on-disk JSON / shapefile URL stem is keyed on the Tuesday valid date
(verified live; every Thursday URL returns 404).
The drought backend is a mixed-output backend: OUTPUT_KIND is set per instance from the resolved
catalog row. The USDM row is vector, so download() returns a
pyramids.feature.collection.FeatureCollection in EPSG:4326 — not a list[Path] like the SPEIbase /
EDO / GDO rasters.
Setup¶
USDM is anonymous HTTP — no token, no extra dependency. The EarthLens facade plus datetime is
everything we need.
import datetime as dt
from earthlens import EarthLens
from earthlens.drought import Catalog
Pick the request window¶
USDM is weekly, so we want a window of at least seven days to land on at least one release. Any day inside the window snaps to the most recent already-released Tuesday composite — the backend walks back one extra week when the snapped Tuesday's release Thursday has not yet passed, so a query made on the same Tuesday the composite is valid for gets the previous week's composite (this week's is not live until Thursday).
We pin a deterministic window and a today= reference so the notebook re-executes to the same release
independently of when it is run.
WINDOW_END = dt.date(2025, 6, 23) # known-valid USDM week
WINDOW_START = WINDOW_END - dt.timedelta(days=14)
TODAY = dt.date(2025, 7, 1) # well past WINDOW_END's release Thursday
print('window:', WINDOW_START.isoformat(), 'to', WINDOW_END.isoformat(), 'today:', TODAY.isoformat())
window: 2025-06-09 to 2025-06-23 today: 2025-07-01
Download the weekly polygons¶
The four drought facade keys (drought / usdm / edo / gdo) are discoverability aliases — all
resolve to the same backend and all require an explicit dataset= kwarg. We pass an explicit bbox over
the continental United States (USDM is national-extent by nature, so any sub-bbox simply clips the
polygons after the fetch). today= pins the release-window check so the snap result is deterministic.
fc = EarthLens(
data_source='usdm',
start=WINDOW_START.isoformat(),
end=WINDOW_END.isoformat(),
variables=[],
lat_lim=[24.0, 50.0],
lon_lim=[-125.0, -66.0],
dataset='usdm',
today=TODAY,
).download(progress_bar=False)
print(type(fc).__name__, 'with', len(fc), 'features in EPSG:', fc.crs.to_epsg())
2026-06-26 20:16:18.594 | INFO | earthlens.earthlens:__init__:936 - No `path` given; download() writes 'usdm' output under earthlens-data\usdm/ (load() uses a temp dir).
2026-06-26 20:16:18 | INFO | pyramids.base.config | Logging is configured.
2026-06-26 20:16:31.864 | INFO | earthlens.drought.backend:download:613 - USDM: U.S. Drought Monitor — public-domain weekly composite produced by NDMC / UNL / USDA / NOAA. Cite the National Drought Mitigation Center.
FeatureCollection with 15 features in EPSG: 4326
Inspect the result¶
Every USDM feature carries:
OBJECTID— provider-side row id.DM— drought class (0= D0 abnormally dry, …4= D4 exceptional drought).Shape_Length/Shape_Area— provider-side geometry stats in EPSG:4326 degrees.release_date— added byearthlensso a multi-week merge keeps the per-release attribution; the ISO date is the Tuesday valid date, not the Thursday release date.
Geometry is MultiPolygon.
fc.head()
| geometry | OBJECTID | DM | Shape_Length | Shape_Area | release_date | |
|---|---|---|---|---|---|---|
| 0 | MULTIPOLYGON (((-80.95223 24.75669, -80.94815 ... | 1 | 0 | 699.024697 | 173.914048 | 2025-06-03 |
| 1 | MULTIPOLYGON (((-80.38746 25.20179, -80.38735 ... | 2 | 1 | 476.079209 | 114.160415 | 2025-06-03 |
| 2 | MULTIPOLYGON (((-80.78096 24.84392, -80.78889 ... | 3 | 2 | 270.464502 | 73.203176 | 2025-06-03 |
| 3 | MULTIPOLYGON (((-81.41815 25.79217, -81.41854 ... | 4 | 3 | 121.095390 | 41.872492 | 2025-06-03 |
| 4 | MULTIPOLYGON (((-98.39617 30.06062, -98.25597 ... | 5 | 4 | 38.062709 | 11.461299 | 2025-06-03 |
summary = (
fc[['release_date', 'DM', 'Shape_Area']]
.groupby(['release_date', 'DM'])
.agg(area=('Shape_Area', 'sum'), n=('DM', 'size'))
.reset_index()
)
summary
| release_date | DM | area | n | |
|---|---|---|---|---|
| 0 | 2025-06-03 | 0 | 173.914048 | 1 |
| 1 | 2025-06-03 | 1 | 114.160415 | 1 |
| 2 | 2025-06-03 | 2 | 73.203176 | 1 |
| 3 | 2025-06-03 | 3 | 41.872492 | 1 |
| 4 | 2025-06-03 | 4 | 11.461299 | 1 |
| 5 | 2025-06-10 | 0 | 152.960287 | 1 |
| 6 | 2025-06-10 | 1 | 125.868641 | 1 |
| 7 | 2025-06-10 | 2 | 71.739281 | 1 |
| 8 | 2025-06-10 | 3 | 36.706228 | 1 |
| 9 | 2025-06-10 | 4 | 9.738253 | 1 |
| 10 | 2025-06-17 | 0 | 146.138732 | 1 |
| 11 | 2025-06-17 | 1 | 130.000618 | 1 |
| 12 | 2025-06-17 | 2 | 72.660843 | 1 |
| 13 | 2025-06-17 | 3 | 34.379394 | 1 |
| 14 | 2025-06-17 | 4 | 9.097365 | 1 |
Quick map¶
A drought-class choropleth on a USA bbox — D0 (driest sliver) through D4 (extreme drought) in classic USDM colours. We use the most recent snapped release so the map shows one consistent week.
import matplotlib.pyplot as plt
USDM_COLORS = {0: '#ffff00', 1: '#fcd37f', 2: '#ffaa00', 3: '#e60000', 4: '#730000'}
latest_release = sorted(fc['release_date'].unique())[-1]
latest = fc[fc['release_date'] == latest_release].copy()
latest['color'] = latest['DM'].map(USDM_COLORS)
ax = latest.plot(
color=latest['color'], edgecolor='#444', linewidth=0.4, figsize=(10, 5)
)
ax.set_title(f'USDM drought classes — release {latest_release}')
ax.set_xlabel('longitude'); ax.set_ylabel('latitude')
plt.tight_layout()
plt.show()
What the facade is doing under the hood¶
data_source='usdm'resolves to theearthlens.drought.Droughtbackend;dataset='usdm'selects the USDM catalog row.- The catalog row pins the transport (
usdm-geojson) and the URL template ({ymd}substituted with the Tuesday valid date). - Each snapped date triggers one HTTP fetch of the per-week JSON, parsed by
geopandasinto aGeoDataFrame, with the source CRS read from the payload'scrsmember (RFC 7946 default 4326). - The frames are merged, reprojected to EPSG:4326 when needed, clipped to the requested bbox, and
wrapped in a
FeatureCollection. - On success the backend logs the per-source attribution (
USDM: U.S. Drought Monitor — public-domain weekly composite produced by NDMC / UNL / USDA / NOAA. Cite the National Drought Mitigation Center.).
Catalog().get('usdm')
Dataset(id='usdm', source='US Drought Monitor (NDMC / University of Nebraska-Lincoln)', transport='usdm-geojson', endpoint='https://droughtmonitor.unl.edu/data/json/usdm_{ymd}.json', coverage=None, output_kind='vector', cadence='weekly', native_crs='EPSG:4326', license_note='USDM public domain — cite NDMC / UNL')
Where to go next¶
- The catalog ships 43 datasets total — USDM (1), EDO (17), GDO (19), SPEIbase (6) —
see the
catalog_explorer.ipynbnotebook for the full list. - SPEIbase (raster monthly NetCDF) is wired and returns a
list[Path]of per-month GeoTIFFs. Raster transports require an explicitpath=to avoid silently writing hundreds of MB into the current working directory. - EDO / GDO indicators (Copernicus OGC WCS) are wired in the catalog and routed by the backend, but
the fetch raises
NotImplementedErroruntil the pyramids temporalread_wcsextension (the cross-repoPY-Atask) ships. - Vector backends like USDM reject
aggregate=— drought-class polygons have no gridded reduction.