GHSL — population projection to 2100 (GHS-WUP, R2025A)¶
The R2025A GHS-WUP (World Urbanization Prospects) family projects population and built-up forward to 2100. Here we pull GHS-WUP-POP at two epochs — 2025 and 2100 — over a Portugal-sized AOI and compare. WUP is whole-globe 1 km (no tiles), so each epoch downloads the global file once and crops.
Code cells are
NBVAL_SKIP(whole-globe downloads); the saved outputs are real.
Setup¶
Imports up front, the same way the other examples open. pyramids provides Dataset (reading the GeoTIFFs that
download() writes); earthlens provides the unified EarthLens entry point. tempfile gives us a scratch
directory for the downloaded files.
# NBVAL_SKIP
import tempfile
import matplotlib.pyplot as plt
from pyramids.dataset import Dataset
from earthlens.core import EarthLens
1 · Download GHS-WUP-POP at two epochs¶
GHS-WUP is whole-globe 1 km (no tiles), so each epoch downloads the global file once and crops to the AOI. We build
the request first — source, variable, the 2025 and 2100 epochs, release, and a Portugal-sized bounding box — then
download() writes one GeoTIFF per epoch into the scratch directory.
# NBVAL_SKIP
out = tempfile.mkdtemp()
wup = EarthLens(
data_source='ghsl',
variables=['GHS_WUP_POP'],
start='2025-01-01',
end='2100-12-31',
epochs=[2025, 2100],
release='R2025A',
aoi=[-9.6, 36.9, -6.0, 42.2],
path=out,
)
paths = wup.download(progress_bar=False)
The two written files — one per epoch — carry the epoch in their names:
# NBVAL_SKIP
[p.name for p in paths]
2 · Compare projected population, 2025 vs 2100¶
Read each GeoTIFF into a population array, masking the negative no-data fill to NaN, and key the arrays by epoch
so the plot can pull them by year.
# NBVAL_SKIP
# GHS-WUP-POP declares its absent-cell fill (-200) in the band, so pyramids
# masks it in the map and in the statistics alike — no sentinel comparison and
# no second copy of the array are needed.
rasters = {
('2025' if p.name.find('_E2025') >= 0 else '2100'): Dataset.read_file(p)
for p in paths
}
for year, ds in rasters.items():
print(f'{year}: declared nodata {ds.no_data_value}')
Side-by-side maps¶
Render the 2025 and 2100 population grids on a shared magma colour ramp so the spatial redistribution over the
century is directly comparable.
# NBVAL_SKIP
fig, axes = plt.subplots(1, 2, figsize=(11, 5))
for ax, year in zip(axes, ['2025', '2100']):
glyph = rasters[year].plot(
fig=fig, ax=ax, cmap='magma', title=f'GHS-WUP-POP {year} (1 km)'
)
glyph.cbar.set_label('people / cell')
plt.tight_layout()
3 · Projected change in total population¶
Summing each grid gives the AOI-wide population per epoch; the difference is the projected change across the AOI, 2025 → 2100.
# NBVAL_SKIP
tot = {year: float(ds.read_array(masked=True).sum()) for year, ds in rasters.items()}
print(f"2025: {tot['2025']:,.0f} -> 2100: {tot['2100']:,.0f}")
print(f"change: {tot['2100'] - tot['2025']:+,.0f}")
for ds in rasters.values():
ds.close()