Note
Go to the end to download the full example code.
Mountain ranges: GMBA Mountain Inventory v2#
The Global Mountain Biodiversity Assessment inventory (Snethlage et al. 2022)
names 8,327 mountain ranges and nests them in a hierarchy up to ten levels
deep. esd.boundaries.mountains.load reads it in three subsets:
subset="basic"(default) — the smallest non-overlapping units;subset="300"— 291 non-overlapping major systems;subset="all"— every range at every level, overlapping;level=picks one;
and two extents: "standard" follows the GMBA mountain definition,
"broad" reaches into the surrounding terrain. Each is one zipped shapefile
on EarthEnv, fetched once into the cache; no account is needed.
The figures show the basic units and the Cascade Range over the Natural Earth hillshade, the hierarchy above Mount Rainier, and, per range, how much of it the Wrzesien mask classifies as seasonal mountain snow.
import geopandas as gpd
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import easysnowdata as esd
aoi = (-123.0, 46.0, -120.5, 48.0) # the central Cascades, Puget Sound to Yakima
rainier = (-121.94, 46.72, -121.54, 46.99)
box = esd.parse_aoi(aoi)
grid = box.to_geobox(crs="utm", resolution=1000)
print(box)
for src in esd.catalog.get("gmba-mountains").sources:
print(f"{src.id:10} {src.title:10} {', '.join(src.requires) or 'no account'}")
AOI(bounds=(-123.0000, 46.0000, -120.5000, 48.0000), clip=True)
earthenv EarthEnv no account
The basic units intersecting the AOI, the one major system they belong to,
and the standard against the broad extent of that system. Every frame starts
with name, gmba_id, level and path.
basic_gdf = esd.boundaries.mountains.load(aoi)
major_gdf = esd.boundaries.mountains.load(aoi, subset="300")
broad_gdf = esd.boundaries.mountains.load(aoi, subset="300", extent="broad")
print(f"{len(basic_gdf)} basic units; major systems: {', '.join(major_gdf['name'])}")
print(basic_gdf[["name", "level"]].sort_values("name").to_string(index=False))
hillshade_da = esd.terrain.hillshade.load(box.buffer(40_000), style="shaded-relief")
ax = esd.plotting.map(
hillshade_da.odc.reproject(grid, resampling="bilinear"),
cmap="gray",
vmin=0,
vmax=255,
colorbar=False,
title="GMBA basic units (colour), the Cascade Range standard and broad",
figsize=(10, 8),
)
esd.plotting.add_outline(
ax,
basic_gdf,
column="name",
cmap="tab20",
alpha=0.45,
edgecolor="0.3",
linewidth=0.4,
)
esd.plotting.add_outline(ax, major_gdf, edgecolor="black", linewidth=2.0)
esd.plotting.add_outline(
ax, broad_gdf, edgecolor="black", linewidth=1.2, linestyle="--"
)
ax.figure.tight_layout()

0%| | 0.00/39.3M [00:00<?, ?B/s]
20%|███████▌ | 7.98M/39.3M [00:00<00:00, 79.8MB/s]
45%|████████████████▋ | 17.7M/39.3M [00:00<00:00, 90.3MB/s]
71%|██████████████████████████ | 27.7M/39.3M [00:00<00:00, 94.7MB/s]
97%|███████████████████████████████████▋ | 38.0M/39.3M [00:00<00:00, 97.8MB/s]
0%| | 0.00/39.3M [00:00<?, ?B/s]
100%|██████████████████████████████████████| 39.3M/39.3M [00:00<00:00, 221GB/s]
0%| | 0.00/32.1M [00:00<?, ?B/s]
14%|█████▏ | 4.45M/32.1M [00:00<00:00, 44.5MB/s]
28%|██████████▎ | 8.91M/32.1M [00:00<00:00, 40.5MB/s]
46%|█████████████████ | 14.8M/32.1M [00:00<00:00, 48.7MB/s]
73%|███████████████████████████▏ | 23.6M/32.1M [00:00<00:00, 63.6MB/s]
0%| | 0.00/32.1M [00:00<?, ?B/s]
100%|██████████████████████████████████████| 32.1M/32.1M [00:00<00:00, 154GB/s]
0%| | 0.00/22.4M [00:00<?, ?B/s]
5%|█▋ | 1.06M/22.4M [00:00<00:02, 10.6MB/s]
9%|███▍ | 2.11M/22.4M [00:00<00:01, 10.5MB/s]
49%|██████████████████ | 11.0M/22.4M [00:00<00:00, 46.1MB/s]
88%|████████████████████████████████▋ | 19.8M/22.4M [00:00<00:00, 62.8MB/s]
0%| | 0.00/22.4M [00:00<?, ?B/s]
100%|██████████████████████████████████████| 22.4M/22.4M [00:00<00:00, 116GB/s]
52 basic units; major systems: Cascade Range, Olympic Mountains, Columbia Plateau, Oregon Coast Range
name level
Capitol Hills 6
Cedar River-South Snoqualmie Pass 7
Central South Cascade Crest 7
Chikamin-Keechelus 7
Chiwaukum Mountains 7
Chiwawa Ridge 7
Columbia Gorge North 6
Constance-Buckhorn Group 6
Dakobed Range 7
Duckabush-Hamma Hamma Group 6
East Yakima Ridges 7
Goat Rocks 6
Horse Heaven Hills 7
Huckleberry-Grass 7
Index-Tolt Area 7
Issaquah Alps 7
Kachess Ridge 7
Kitsap Peninsula 6
Mission-Naneum Ridges 7
Monte Cristo Area 7
Mount Adams Area 6
Mount Aix Area 7
Mount Daniel Area 7
Mount Jupiter Area 6
Mount Rainier East Foothills 7
Mount Rainier Massif 7
Mount Rainier North Foothills 7
Mount Rainier West Foothills 7
Mount Saint Helens Area 6
Nason Ridge 7
North Chelan Mountains 7
North Entiat Mountains 7
North Wenatchee Mountains 7
North-Middle Forks Snoqualmie 7
Northeast Olympic Foothills 6
Pilchuck Area 7
Ragged Ridge 7
Seattle-Everett Area 7
Simcoe Mountains 7
Snoqualmie Pass North 7
Sourdough Mountains 7
South Chelan Mountains 7
South Entiat Mountains 7
Southwest Mount Rainier Area 7
Stuart Range 7
Tatoosh Range 7
Teanaway Area 7
Wenatchee Ridge 7
West Manastash-Umtanum Ridges 7
White Mountains 7
Wild Sky 7
Willapa Hills 6
The hierarchy above Rainier: subset="all" holds every level, so the
ranges containing the mountain run from the continent down to the massif.
level= keeps one of them; level 4 is the Cascade Range.
nested_gdf = esd.boundaries.mountains.load(rainier, subset="all")
ladder_gdf = nested_gdf[nested_gdf.contains(esd.parse_aoi(rainier).geometry.centroid)]
print(ladder_gdf.sort_values("level")[["level", "name"]].to_string(index=False))
print(esd.boundaries.mountains.load(rainier, subset="all", level=4)["name"].tolist())
0%| | 0.00/186M [00:00<?, ?B/s]
4%|█▍ | 7.33M/186M [00:00<00:02, 73.3MB/s]
8%|███▏ | 15.8M/186M [00:00<00:02, 79.8MB/s]
13%|████▉ | 24.0M/186M [00:00<00:01, 81.0MB/s]
17%|██████▌ | 32.3M/186M [00:00<00:01, 81.9MB/s]
22%|████████▎ | 40.5M/186M [00:00<00:01, 81.3MB/s]
26%|██████████ | 49.2M/186M [00:00<00:01, 83.0MB/s]
31%|███████████▊ | 57.8M/186M [00:00<00:01, 84.1MB/s]
36%|█████████████▌ | 66.5M/186M [00:00<00:01, 85.0MB/s]
41%|███████████████▍ | 75.4M/186M [00:00<00:01, 86.3MB/s]
45%|█████████████████▏ | 84.2M/186M [00:01<00:01, 86.7MB/s]
50%|██████████████████▉ | 92.8M/186M [00:01<00:01, 86.7MB/s]
55%|█████████████████████▎ | 102M/186M [00:01<00:00, 87.6MB/s]
60%|███████████████████████▎ | 111M/186M [00:01<00:00, 88.2MB/s]
64%|█████████████████████████ | 120M/186M [00:01<00:00, 85.9MB/s]
70%|███████████████████████████▏ | 130M/186M [00:01<00:00, 90.5MB/s]
75%|█████████████████████████████▍ | 140M/186M [00:01<00:00, 94.2MB/s]
81%|███████████████████████████████▌ | 150M/186M [00:01<00:00, 97.0MB/s]
87%|█████████████████████████████████▋ | 161M/186M [00:01<00:00, 99.0MB/s]
92%|████████████████████████████████████▊ | 171M/186M [00:01<00:00, 100MB/s]
98%|███████████████████████████████████████ | 181M/186M [00:02<00:00, 101MB/s]
0%| | 0.00/186M [00:00<?, ?B/s]
100%|████████████████████████████████████████| 186M/186M [00:00<00:00, 853GB/s]
level name
1 North America
2 American Cordillera (North America)
3 Pacific Coast Ranges
4 Cascade Range
5 South Washington Cascades
6 Mount Rainier Area
7 Mount Rainier Massif
['Cascade Range']
Where the snow is: the fraction of each basic unit that the Wrzesien mask classifies as seasonal mountain snow (MODIS 2000-2016). The ranges along the crest are mostly seasonal; the foothills to the west and east are not.
mask_da = esd.snow.mountain_snow_mask.load(box.buffer(5000), chunks=None)
lon, lat = np.meshgrid(mask_da["longitude"].values, mask_da["latitude"].values)
pixels_df = pd.DataFrame(
{"lon": lon.ravel(), "lat": lat.ravel(), "class": mask_da.values.ravel()}
)
pixels_gdf = gpd.GeoDataFrame(
pixels_df,
geometry=gpd.points_from_xy(pixels_df["lon"], pixels_df["lat"]),
crs="EPSG:4326",
)
joined_gdf = pixels_gdf.sjoin(basic_gdf[["name", "geometry"]], predicate="within")
seasonal_df = (
joined_gdf.groupby("name")["class"]
.apply(lambda classes: float((classes == 3).mean()))
.rename("seasonal_fraction")
.reset_index()
)
ranges_gdf = basic_gdf.merge(seasonal_df, on="name")
print(
ranges_gdf.sort_values("seasonal_fraction", ascending=False)[
["name", "seasonal_fraction"]
].to_string(index=False)
)
fig, ax = plt.subplots(figsize=(10, 8))
esd.plotting.map(
hillshade_da.odc.reproject(grid, resampling="bilinear"),
ax=ax,
cmap="gray",
vmin=0,
vmax=255,
colorbar=False,
title="Seasonal mountain snow per GMBA unit (Wrzesien et al. 2019)",
)
esd.plotting.add_outline(
ax,
ranges_gdf,
column="seasonal_fraction",
cmap="Blues",
vmin=0,
vmax=1,
alpha=0.8,
edgecolor="0.3",
linewidth=0.4,
legend=True,
legend_kwds={"label": "fraction of pixels with seasonal snow", "shrink": 0.7},
)
fig.tight_layout()

name seasonal_fraction
Stuart Range 0.994382
North Chelan Mountains 0.990610
Dakobed Range 0.989899
Chiwaukum Mountains 0.984866
Mount Rainier Massif 0.983193
Wenatchee Ridge 0.982456
Teanaway Area 0.982165
South Chelan Mountains 0.981481
North Entiat Mountains 0.979920
White Mountains 0.978261
Nason Ridge 0.978227
South Entiat Mountains 0.977227
Monte Cristo Area 0.972973
North Wenatchee Mountains 0.972789
Mount Daniel Area 0.972222
Central South Cascade Crest 0.970403
Sourdough Mountains 0.967890
Mount Aix Area 0.962569
Chiwawa Ridge 0.962175
Chikamin-Keechelus 0.956140
Mount Adams Area 0.939299
Mount Rainier East Foothills 0.938776
Kachess Ridge 0.938596
Mission-Naneum Ridges 0.935109
Wild Sky 0.893540
West Manastash-Umtanum Ridges 0.861527
Tatoosh Range 0.843077
North-Middle Forks Snoqualmie 0.817844
Goat Rocks 0.802289
Snoqualmie Pass North 0.792757
Columbia Gorge North 0.754087
Mount Rainier North Foothills 0.739738
Cedar River-South Snoqualmie Pass 0.730972
Simcoe Mountains 0.640266
Huckleberry-Grass 0.589147
Ragged Ridge 0.588339
Horse Heaven Hills 0.482185
Pilchuck Area 0.469914
Index-Tolt Area 0.462439
Mount Saint Helens Area 0.405912
Southwest Mount Rainier Area 0.387423
Constance-Buckhorn Group 0.375581
Mount Rainier West Foothills 0.368514
Mount Jupiter Area 0.321168
East Yakima Ridges 0.280423
Duckabush-Hamma Hamma Group 0.236264
Issaquah Alps 0.071429
Kitsap Peninsula 0.059701
Northeast Olympic Foothills 0.055556
Capitol Hills 0.010204
Willapa Hills 0.007843
Seattle-Everett Area 0.000000
Total running time of the script: (0 minutes 29.421 seconds)