Skip to content

Commit 4dd441f

Browse files
feat(makie): implement hexbin-map-geographic (#7751)
## Implementation: `hexbin-map-geographic` - julia/makie Implements the **julia/makie** version of `hexbin-map-geographic`. **File:** `plots/hexbin-map-geographic/implementations/julia/makie.jl` **Parent Issue:** #3767 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/26514203873)* --------- 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 b75ef2a commit 4dd441f

2 files changed

Lines changed: 405 additions & 0 deletions

File tree

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# anyplot.ai
2+
# hexbin-map-geographic: Hexagonal Binning Map
3+
# Library: makie 0.22.10 | Julia 1.11.9
4+
# Quality: 85/100 | Created: 2026-05-27
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 GRID_COLOR = THEME == "light" ?
20+
RGBAf(26/255f0, 26/255f0, 23/255f0, 0.12f0) :
21+
RGBAf(240/255f0, 239/255f0, 232/255f0, 0.12f0)
22+
23+
# Water fill and coastline colors for the geographic base map
24+
const WATER_FILL = THEME == "light" ?
25+
RGBAf(0.58f0, 0.75f0, 0.88f0, 0.32f0) :
26+
RGBAf(0.18f0, 0.35f0, 0.56f0, 0.45f0)
27+
const COAST_LINE = THEME == "light" ?
28+
RGBAf(0.27f0, 0.46f0, 0.63f0, 0.65f0) :
29+
RGBAf(0.42f0, 0.62f0, 0.80f0, 0.52f0)
30+
31+
# Anyplot sequential colormap for density (single-polarity)
32+
const ANYPLOT_SEQ = cgrad([colorant"#009E73", colorant"#4467A3"])
33+
34+
# Data: synthetic bike-share ride starts in San Francisco
35+
cluster_centers = [
36+
(-122.4057, 37.7871), # Union Square / Downtown
37+
(-122.4194, 37.7599), # Mission District
38+
(-122.3971, 37.7749), # SoMa
39+
(-122.4352, 37.8012), # Marina
40+
(-122.4447, 37.7698), # Haight-Ashbury
41+
]
42+
cluster_weights = [0.33, 0.22, 0.20, 0.13, 0.12]
43+
cluster_stds = [0.013, 0.012, 0.011, 0.010, 0.013]
44+
45+
n_total = 14000
46+
lons = Float64[]
47+
lats = Float64[]
48+
49+
for ((cx, cy), w, σ) in zip(cluster_centers, cluster_weights, cluster_stds)
50+
n_i = round(Int, n_total * w)
51+
append!(lons, cx .+ σ .* randn(n_i))
52+
append!(lats, cy .+ σ .* randn(n_i))
53+
end
54+
55+
# Map extent — tightened from original to reduce empty ocean space upper-left
56+
lon_lo, lon_hi = -122.515, -122.360
57+
lat_lo, lat_hi = 37.700, 37.835
58+
59+
# Clip to bounds
60+
mask = (lons .>= lon_lo) .& (lons .<= lon_hi) .& (lats .>= lat_lo) .& (lats .<= lat_hi)
61+
lons = lons[mask]
62+
lats = lats[mask]
63+
64+
# --- Simplified geographic base map -----------------------------------------
65+
# Polygon vertices tracing simplified SF coastlines; Makie auto-closes last→first.
66+
67+
# SF Bay water polygon: follows the eastern SF waterfront (south→north),
68+
# then closes via the box top-right corner and right edge.
69+
bay_pts = Point2f[
70+
(-122.374, 37.700), (-122.376, 37.748), (-122.381, 37.762),
71+
(-122.386, 37.776), (-122.393, 37.790), (-122.400, 37.800),
72+
(-122.407, 37.805), (-122.414, 37.808), (-122.432, 37.804),
73+
(-122.443, 37.803), (-122.455, 37.803), (-122.464, 37.804),
74+
(-122.479, 37.808), # Golden Gate Bridge — SF side
75+
(-122.479, 37.835), (-122.360, 37.835), (-122.360, 37.700),
76+
]
77+
78+
# Pacific Ocean polygon: follows the western SF coast (north→south),
79+
# then closes via the box bottom-left and left edge back to the top.
80+
pac_pts = Point2f[
81+
(-122.479, 37.808), # Golden Gate Bridge — SF side
82+
(-122.484, 37.800), (-122.493, 37.798),
83+
(-122.499, 37.794), (-122.505, 37.789),
84+
(-122.509, 37.782), (-122.511, 37.770),
85+
(-122.512, 37.748), (-122.511, 37.710),
86+
(-122.515, 37.700), (-122.515, 37.835),
87+
(-122.479, 37.835),
88+
]
89+
90+
# --- Figure -----------------------------------------------------------------
91+
fig = Figure(
92+
size = (1600, 900),
93+
fontsize = 14,
94+
backgroundcolor = PAGE_BG,
95+
)
96+
97+
ax = Axis(
98+
fig[1, 1];
99+
title = "SF Bike-Share · hexbin-map-geographic · julia · makie · anyplot.ai",
100+
titlesize = 20,
101+
titlecolor = INK,
102+
xlabel = "Longitude",
103+
ylabel = "Latitude",
104+
xlabelsize = 14,
105+
ylabelsize = 14,
106+
xticklabelsize = 12,
107+
yticklabelsize = 12,
108+
xlabelcolor = INK,
109+
ylabelcolor = INK,
110+
xticklabelcolor = INK_SOFT,
111+
yticklabelcolor = INK_SOFT,
112+
xtickcolor = INK_SOFT,
113+
ytickcolor = INK_SOFT,
114+
backgroundcolor = PAGE_BG,
115+
topspinevisible = false,
116+
rightspinevisible = false,
117+
leftspinecolor = INK_SOFT,
118+
bottomspinecolor = INK_SOFT,
119+
xgridcolor = GRID_COLOR,
120+
ygridcolor = GRID_COLOR,
121+
xminorgridvisible = false,
122+
yminorgridvisible = false,
123+
)
124+
125+
# Base map: water bodies rendered before hexbins so land clusters stay visible
126+
poly!(ax, bay_pts; color = WATER_FILL, strokecolor = COAST_LINE, strokewidth = 1.2)
127+
poly!(ax, pac_pts; color = WATER_FILL, strokecolor = COAST_LINE, strokewidth = 1.2)
128+
129+
# Hexagonal density map overlaid on base map
130+
hb = hexbin!(ax, lons, lats;
131+
cellsize = 0.009,
132+
colormap = ANYPLOT_SEQ,
133+
threshold = 1,
134+
strokecolor = PAGE_BG,
135+
strokewidth = 0.5,
136+
)
137+
138+
# Colorbar
139+
Colorbar(fig[1, 2], hb;
140+
label = "Ride Count",
141+
labelsize = 13,
142+
labelcolor = INK,
143+
ticklabelsize = 11,
144+
ticklabelcolor = INK_SOFT,
145+
tickcolor = INK_SOFT,
146+
width = 18,
147+
)
148+
149+
limits!(ax, lon_lo, lon_hi, lat_lo, lat_hi)
150+
colgap!(fig.layout, 12)
151+
152+
save("plot-$(THEME).png", fig; px_per_unit = 2)

0 commit comments

Comments
 (0)