Skip to content

Commit c58fdc1

Browse files
feat(matplotlib): implement area-mountain-panorama (#5368)
## Implementation: `area-mountain-panorama` - python/matplotlib Implements the **python/matplotlib** version of `area-mountain-panorama`. **File:** `plots/area-mountain-panorama/implementations/python/matplotlib.py` **Parent Issue:** #5365 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/24918592166)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 60c6fd0 commit c58fdc1

2 files changed

Lines changed: 413 additions & 0 deletions

File tree

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
""" anyplot.ai
2+
area-mountain-panorama: Mountain Panorama Profile with Labeled Peaks
3+
Library: matplotlib 3.10.9 | Python 3.14.4
4+
Quality: 91/100 | Created: 2026-04-25
5+
"""
6+
7+
import os
8+
9+
import matplotlib.pyplot as plt
10+
import numpy as np
11+
from matplotlib.colors import LinearSegmentedColormap
12+
13+
14+
# Theme tokens
15+
THEME = os.getenv("ANYPLOT_THEME", "light")
16+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
17+
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
18+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
19+
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
20+
INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F"
21+
BRAND = "#009E73"
22+
SKY_TOP = "#E8C8A0" if THEME == "light" else "#252D40"
23+
24+
# Data — Wallis (Valais) summit panorama, ordered W → E
25+
peaks = [
26+
("Weisshorn", 12, 4506),
27+
("Zinalrothorn", 30, 4221),
28+
("Ober Gabelhorn", 45, 4063),
29+
("Dent Blanche", 58, 4358),
30+
("Dent d'Hérens", 76, 4171),
31+
("Matterhorn", 92, 4478),
32+
("Breithorn", 120, 4164),
33+
("Pollux", 132, 4092),
34+
("Castor", 139, 4223),
35+
("Liskamm", 152, 4527),
36+
("Monte Rosa", 170, 4634),
37+
("Strahlhorn", 192, 4190),
38+
("Rimpfischhorn", 204, 4199),
39+
("Allalinhorn", 215, 4027),
40+
("Alphubel", 225, 4206),
41+
("Täschhorn", 236, 4491),
42+
("Dom", 250, 4545),
43+
]
44+
45+
# Skyline construction
46+
np.random.seed(42)
47+
angle = np.linspace(0, 262, 2000)
48+
49+
# Base ridge: smoothed random walk in the 3000–3700 m belt (foothills + minor cols)
50+
walk = np.cumsum(np.random.randn(len(angle)) * 1.5)
51+
sigma_walk = 22
52+
g = np.arange(-3 * sigma_walk, 3 * sigma_walk + 1)
53+
kernel_walk = np.exp(-(g**2) / (2 * sigma_walk**2))
54+
walk = np.convolve(walk, kernel_walk / kernel_walk.sum(), mode="same")
55+
walk = (walk - walk.min()) / (walk.max() - walk.min())
56+
ridge = 3000 + walk * 700
57+
58+
# Major summits as Gaussian peaks (max-combined for the visible silhouette)
59+
for _, pos, elev in peaks:
60+
width = 5.5 + (elev - 4000) / 130
61+
bump = (elev - 2700) * np.exp(-((angle - pos) ** 2) / (2 * width**2))
62+
ridge = np.maximum(ridge, 2700 + bump)
63+
64+
# Light final smoothing of the combined ridge
65+
sigma_ridge = 0.8
66+
g = np.arange(-3, 4)
67+
kernel_ridge = np.exp(-(g**2) / (2 * sigma_ridge**2))
68+
ridge = np.convolve(ridge, kernel_ridge / kernel_ridge.sum(), mode="same")
69+
70+
# Plot
71+
fig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG)
72+
ax.set_facecolor(PAGE_BG)
73+
74+
# Sky gradient above ridgeline (dusk mood: warm peach for light, deep navy for dark)
75+
sky_cmap = LinearSegmentedColormap.from_list("sky", [PAGE_BG, SKY_TOP])
76+
sky_gradient = np.linspace(0, 1, 256).reshape(-1, 1)
77+
ax.imshow(
78+
sky_gradient,
79+
extent=(0, 262, 2400, 6050),
80+
aspect="auto",
81+
cmap=sky_cmap,
82+
origin="lower",
83+
zorder=1,
84+
interpolation="bilinear",
85+
)
86+
87+
# Mountain silhouette (covers the lower portion of the gradient — sky stays above ridge)
88+
ax.fill_between(angle, 2400, ridge, color=BRAND, linewidth=0, zorder=2)
89+
ax.plot(angle, ridge, color=BRAND, linewidth=1.0, zorder=3)
90+
91+
# Peak labels staggered across three vertical levels, with thin leader lines
92+
label_levels = [5050, 5320, 5590]
93+
sorted_peaks = sorted(peaks, key=lambda p: p[1])
94+
for i, (name, pos, elev) in enumerate(sorted_peaks):
95+
level = label_levels[i % 3]
96+
is_anchor = name == "Matterhorn"
97+
text_color = INK if is_anchor else INK_SOFT
98+
text_weight = "bold" if is_anchor else "regular"
99+
line_color = INK if is_anchor else INK_SOFT
100+
line_alpha = 0.85 if is_anchor else 0.45
101+
line_width = 1.4 if is_anchor else 0.8
102+
fsize = 16 if is_anchor else 14
103+
104+
ax.plot([pos, pos], [elev + 25, level - 90], color=line_color, linewidth=line_width, alpha=line_alpha, zorder=4)
105+
ax.text(
106+
pos,
107+
level,
108+
f"{name}\n{elev:,} m",
109+
ha="center",
110+
va="bottom",
111+
fontsize=fsize,
112+
fontweight=text_weight,
113+
color=text_color,
114+
linespacing=1.35,
115+
zorder=5,
116+
)
117+
118+
# Axes
119+
ax.set_ylim(2500, 6050)
120+
ax.set_xlim(0, 262)
121+
ax.set_ylabel("Elevation (m)", fontsize=20, color=INK)
122+
ax.set_title(
123+
"Wallis Alps · area-mountain-panorama · matplotlib · anyplot.ai",
124+
fontsize=24,
125+
fontweight="medium",
126+
color=INK,
127+
pad=18,
128+
)
129+
130+
# Compass bearings on x-axis
131+
compass_ticks = [10, 65, 120, 180, 245]
132+
compass_labels = ["W", "SW", "S", "SE", "E"]
133+
ax.set_xticks(compass_ticks)
134+
ax.set_xticklabels(compass_labels, fontsize=16, color=INK_SOFT)
135+
ax.tick_params(axis="x", colors=INK_SOFT, length=0)
136+
ax.tick_params(axis="y", labelsize=16, colors=INK_SOFT)
137+
138+
ax.spines["top"].set_visible(False)
139+
ax.spines["right"].set_visible(False)
140+
for s in ("left", "bottom"):
141+
ax.spines[s].set_color(INK_SOFT)
142+
ax.yaxis.grid(True, alpha=0.10, linewidth=0.8, color=INK)
143+
144+
plt.tight_layout()
145+
plt.savefig(f"plot-{THEME}.png", dpi=300, bbox_inches="tight", facecolor=PAGE_BG)

0 commit comments

Comments
 (0)