Skip to content

Commit 71da199

Browse files
feat(makie): implement area-elevation-profile (#8638)
## Implementation: `area-elevation-profile` - julia/makie Implements the **julia/makie** version of `area-elevation-profile`. **File:** `plots/area-elevation-profile/implementations/julia/makie.jl` **Parent Issue:** #4578 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/27256310300)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com>
1 parent d7a706e commit 71da199

2 files changed

Lines changed: 412 additions & 0 deletions

File tree

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# anyplot.ai
2+
# area-elevation-profile: Terrain Elevation Profile Along Transect
3+
# Library: makie 0.22.10 | Julia 1.11.9
4+
# Quality: 90/100 | Created: 2026-06-10
5+
6+
using CairoMakie
7+
using Colors
8+
using Random
9+
10+
Random.seed!(42)
11+
12+
# Theme tokens
13+
const THEME = get(ENV, "ANYPLOT_THEME", "light")
14+
const PAGE_BG = THEME == "light" ? colorant"#FAF8F1" : colorant"#1A1A17"
15+
const ELEVATED_BG = THEME == "light" ? colorant"#FFFDF6" : colorant"#242420"
16+
const INK = THEME == "light" ? colorant"#1A1A17" : colorant"#F0EFE8"
17+
const INK_SOFT = THEME == "light" ? colorant"#4A4A44" : colorant"#B8B7B0"
18+
const INK_MUTED = THEME == "light" ? colorant"#6B6A63" : colorant"#A8A79F"
19+
const FILL_ALPHA = THEME == "light" ? 0.28f0 : 0.42f0
20+
21+
const IMPRINT_PALETTE = [
22+
colorant"#009E73", # 1 — brand green (first series, always)
23+
colorant"#C475FD", # 2 — lavender
24+
colorant"#4467A3", # 3 — blue
25+
colorant"#BD8233", # 4 — ochre
26+
colorant"#AE3030", # 5 — matte red (semantic: bad/loss)
27+
colorant"#2ABCCD", # 6 — cyan
28+
colorant"#954477", # 7 — rose
29+
colorant"#99B314", # 8 — lime
30+
]
31+
32+
# Data: Fictional alpine traverse — 80 km route across passes and summits
33+
wp_dist = [ 0.0, 10.0, 18.0, 26.0, 35.0, 45.0, 60.0, 70.0, 80.0]
34+
wp_elev = [950.0, 1600.0, 2750.0, 2050.0, 3180.0, 2600.0, 2900.0, 2150.0, 1050.0]
35+
36+
n = 320
37+
distance = collect(range(0.0, 80.0, length=n))
38+
39+
function lerp_elev(d)
40+
for i in 2:length(wp_dist)
41+
if d <= wp_dist[i]
42+
t = (d - wp_dist[i-1]) / (wp_dist[i] - wp_dist[i-1])
43+
return wp_elev[i-1] + t * (wp_elev[i] - wp_elev[i-1])
44+
end
45+
end
46+
return wp_elev[end]
47+
end
48+
49+
elev_base = lerp_elev.(distance)
50+
51+
function gauss_smooth(v, sigma)
52+
nv = length(v)
53+
ks = round(Int, 3 * sigma)
54+
result = similar(v, Float64)
55+
for i in 1:nv
56+
s = 0.0; w = 0.0
57+
for j in max(1, i - ks):min(nv, i + ks)
58+
wj = exp(-0.5 * ((j - i) / sigma)^2)
59+
s += wj * v[j]; w += wj
60+
end
61+
result[i] = s / w
62+
end
63+
return result
64+
end
65+
66+
# Terrain micro-texture via superimposed sinusoids (no cumulative drift)
67+
terrain_noise = zeros(n)
68+
for freq in [4, 9, 17, 29]
69+
terrain_noise .+= randn() .* sin.(range(0.0, Float64(freq) * π, length=n) .+ rand() * 2π)
70+
end
71+
terrain_noise = gauss_smooth(terrain_noise, 3.0) .* 28.0
72+
73+
elevation = elev_base .+ terrain_noise
74+
75+
# Key landmarks — pairs of (distance_km, label)
76+
lm_dists = [ 0.0, 18.0, 35.0, 45.0, 60.0, 80.0]
77+
lm_labels = ["Trailhead", "First Pass", "Summit Peak", "Col de Neige", "Grand Col", "Trail End"]
78+
lm_idx = [argmin(abs.(distance .- d)) for d in lm_dists]
79+
lm_elevs = elevation[lm_idx]
80+
lm_offsets = [180.0, 180.0, 200.0, -230.0, 180.0, 180.0]
81+
lm_valigns = [:bottom, :bottom, :bottom, :top, :bottom, :bottom]
82+
lm_haligns = [:left, :center, :center, :center, :center, :right ]
83+
84+
const TITLE = "area-elevation-profile · julia · makie · anyplot.ai"
85+
title_fontsize = 20 # 51 chars < 67 baseline — no scaling needed
86+
87+
# Figure
88+
fig = Figure(
89+
size = (1600, 900),
90+
fontsize = 14,
91+
backgroundcolor = PAGE_BG,
92+
)
93+
94+
ax = Axis(
95+
fig[1, 1];
96+
title = TITLE,
97+
titlesize = title_fontsize,
98+
titlecolor = INK,
99+
xlabel = "Distance (km)",
100+
ylabel = "Elevation (m)",
101+
xlabelsize = 14,
102+
ylabelsize = 14,
103+
xlabelcolor = INK,
104+
ylabelcolor = INK,
105+
xticklabelsize = 12,
106+
yticklabelsize = 12,
107+
xticklabelcolor = INK_SOFT,
108+
yticklabelcolor = INK_SOFT,
109+
xtickcolor = INK_SOFT,
110+
ytickcolor = INK_SOFT,
111+
backgroundcolor = PAGE_BG,
112+
topspinevisible = false,
113+
rightspinevisible = false,
114+
leftspinecolor = INK_SOFT,
115+
bottomspinecolor = INK_SOFT,
116+
xgridvisible = false,
117+
ygridcolor = RGBAf(INK.r, INK.g, INK.b, 0.12),
118+
yminorgridvisible = false,
119+
xminorgridvisible = false,
120+
limits = (0.0, 82.0, 500.0, 3700.0),
121+
)
122+
123+
# Filled terrain silhouette (Imprint brand green; alpha higher on dark for equal visual weight)
124+
band!(ax, distance, fill(500.0, n), elevation;
125+
color = RGBAf(IMPRINT_PALETTE[1].r, IMPRINT_PALETTE[1].g, IMPRINT_PALETTE[1].b, FILL_ALPHA))
126+
127+
# Profile line
128+
lines!(ax, distance, elevation;
129+
color = IMPRINT_PALETTE[1], linewidth = 2.5)
130+
131+
# Landmark annotations — Summit Peak gets extra emphasis as the highest point
132+
for i in 1:length(lm_dists)
133+
d = lm_dists[i]
134+
name = lm_labels[i]
135+
elev = lm_elevs[i]
136+
off = lm_offsets[i]
137+
va = lm_valigns[i]
138+
139+
is_summit = (name == "Summit Peak")
140+
msize = is_summit ? 13 : 9
141+
mstroke = is_summit ? 2.5f0 : 1.5f0
142+
143+
# Connector line from terrain point to label position
144+
lines!(ax, [d, d], [elev, elev + off];
145+
color = RGBAf(INK_MUTED.r, INK_MUTED.g, INK_MUTED.b, 0.5),
146+
linewidth = 0.8)
147+
148+
# Dot marker at elevation (Summit Peak larger for focal hierarchy)
149+
scatter!(ax, [d], [elev];
150+
color = IMPRINT_PALETTE[1], markersize = msize,
151+
strokecolor = PAGE_BG, strokewidth = mstroke)
152+
153+
# Label: name + elevation; 13 pt for mobile readability
154+
text!(ax, d, elev + off;
155+
text = "$(name)\n$(round(Int, elev)) m",
156+
align = (lm_haligns[i], va),
157+
fontsize = 13,
158+
color = INK_SOFT)
159+
end
160+
161+
# Vertical exaggeration note (bottom-right)
162+
text!(ax, 80.0, 540.0;
163+
text = "VE ≈ 15×",
164+
align = (:right, :bottom),
165+
fontsize = 13,
166+
color = INK_MUTED)
167+
168+
save("plot-$(THEME).png", fig; px_per_unit = 2)

0 commit comments

Comments
 (0)