|
| 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