Skip to content

Commit 3dbd924

Browse files
feat(altair): implement bubble-map-geographic (#3636)
## Implementation: `bubble-map-geographic` - altair Implements the **altair** version of `bubble-map-geographic`. **File:** `plots/bubble-map-geographic/implementations/altair.py` **Parent Issue:** #3625 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20873958906)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 38305e7 commit 3dbd924

2 files changed

Lines changed: 477 additions & 0 deletions

File tree

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
""" pyplots.ai
2+
bubble-map-geographic: Bubble Map with Sized Geographic Markers
3+
Library: altair 6.0.0 | Python 3.13.11
4+
Quality: 93/100 | Created: 2026-01-10
5+
"""
6+
7+
import altair as alt
8+
import pandas as pd
9+
10+
11+
# Data: Major world cities with population (millions)
12+
cities_data = {
13+
"city": [
14+
"Tokyo",
15+
"Delhi",
16+
"Shanghai",
17+
"Sao Paulo",
18+
"Mexico City",
19+
"Cairo",
20+
"Mumbai",
21+
"Beijing",
22+
"Dhaka",
23+
"Osaka",
24+
"New York",
25+
"Karachi",
26+
"Buenos Aires",
27+
"Istanbul",
28+
"Lagos",
29+
"Los Angeles",
30+
"Kolkata",
31+
"Manila",
32+
"Rio de Janeiro",
33+
"Guangzhou",
34+
"Moscow",
35+
"Shenzhen",
36+
"Paris",
37+
"Jakarta",
38+
"Lima",
39+
"Bangkok",
40+
"London",
41+
"Chicago",
42+
"Bogota",
43+
"Sydney",
44+
],
45+
"latitude": [
46+
35.68,
47+
28.61,
48+
31.23,
49+
-23.55,
50+
19.43,
51+
30.04,
52+
19.08,
53+
39.90,
54+
23.81,
55+
34.69,
56+
40.71,
57+
24.86,
58+
-34.60,
59+
41.01,
60+
6.52,
61+
34.05,
62+
22.57,
63+
14.60,
64+
-22.91,
65+
23.13,
66+
55.76,
67+
22.54,
68+
48.86,
69+
-6.21,
70+
-12.05,
71+
13.76,
72+
51.51,
73+
41.88,
74+
4.71,
75+
-33.87,
76+
],
77+
"longitude": [
78+
139.69,
79+
77.21,
80+
121.47,
81+
-46.63,
82+
-99.13,
83+
31.24,
84+
72.88,
85+
116.41,
86+
90.41,
87+
135.50,
88+
-74.01,
89+
67.01,
90+
-58.38,
91+
28.98,
92+
3.38,
93+
-118.24,
94+
88.36,
95+
120.98,
96+
-43.17,
97+
113.26,
98+
37.62,
99+
114.06,
100+
2.35,
101+
106.85,
102+
-77.04,
103+
100.50,
104+
-0.13,
105+
-87.63,
106+
-74.07,
107+
151.21,
108+
],
109+
"population": [
110+
37.4,
111+
32.9,
112+
29.2,
113+
22.4,
114+
21.8,
115+
21.3,
116+
21.0,
117+
20.9,
118+
22.5,
119+
19.1,
120+
18.8,
121+
16.8,
122+
15.4,
123+
15.6,
124+
15.3,
125+
12.5,
126+
15.1,
127+
14.4,
128+
13.5,
129+
14.3,
130+
12.5,
131+
13.4,
132+
11.0,
133+
11.2,
134+
11.0,
135+
10.7,
136+
9.5,
137+
8.9,
138+
11.3,
139+
5.4,
140+
],
141+
"region": [
142+
"Asia",
143+
"Asia",
144+
"Asia",
145+
"South America",
146+
"North America",
147+
"Africa",
148+
"Asia",
149+
"Asia",
150+
"Asia",
151+
"Asia",
152+
"North America",
153+
"Asia",
154+
"South America",
155+
"Europe",
156+
"Africa",
157+
"North America",
158+
"Asia",
159+
"Asia",
160+
"South America",
161+
"Asia",
162+
"Europe",
163+
"Asia",
164+
"Europe",
165+
"Asia",
166+
"South America",
167+
"Asia",
168+
"Europe",
169+
"North America",
170+
"South America",
171+
"Oceania",
172+
],
173+
}
174+
175+
df = pd.DataFrame(cities_data)
176+
177+
# Load world map from vega-datasets URL (works without vega_datasets package)
178+
world_url = "https://cdn.jsdelivr.net/npm/vega-datasets@2/data/world-110m.json"
179+
countries = alt.topo_feature(world_url, "countries")
180+
181+
# Region color mapping (colorblind-safe)
182+
region_colors = {
183+
"Asia": "#306998",
184+
"Europe": "#FFD43B",
185+
"North America": "#2CA02C",
186+
"South America": "#D62728",
187+
"Africa": "#9467BD",
188+
"Oceania": "#17BECF",
189+
}
190+
191+
# Create base map with country boundaries
192+
base_map = (
193+
alt.Chart(countries)
194+
.mark_geoshape(fill="#E8E8E0", stroke="#B0B0B0", strokeWidth=0.5)
195+
.project(type="equirectangular", scale=280, translate=[800, 480])
196+
.properties(width=1600, height=900)
197+
)
198+
199+
# Create bubble layer with sized markers
200+
bubbles = (
201+
alt.Chart(df)
202+
.mark_circle(opacity=0.7, stroke="#FFFFFF", strokeWidth=1.5)
203+
.encode(
204+
longitude="longitude:Q",
205+
latitude="latitude:Q",
206+
size=alt.Size(
207+
"population:Q",
208+
scale=alt.Scale(domain=[5, 40], range=[100, 2500]),
209+
legend=alt.Legend(
210+
title="Population (millions)",
211+
titleFontSize=16,
212+
labelFontSize=14,
213+
symbolFillColor="#306998",
214+
orient="bottom-left",
215+
offset=20,
216+
),
217+
),
218+
color=alt.Color(
219+
"region:N",
220+
scale=alt.Scale(domain=list(region_colors.keys()), range=list(region_colors.values())),
221+
legend=alt.Legend(title="Region", titleFontSize=16, labelFontSize=14, orient="bottom-right", offset=20),
222+
),
223+
tooltip=[
224+
alt.Tooltip("city:N", title="City"),
225+
alt.Tooltip("population:Q", title="Population (M)", format=".1f"),
226+
alt.Tooltip("region:N", title="Region"),
227+
alt.Tooltip("latitude:Q", title="Latitude", format=".2f"),
228+
alt.Tooltip("longitude:Q", title="Longitude", format=".2f"),
229+
],
230+
)
231+
.project(type="equirectangular", scale=280, translate=[800, 480])
232+
)
233+
234+
# Combine base map and bubbles
235+
chart = (
236+
(base_map + bubbles)
237+
.properties(
238+
title=alt.Title(
239+
text="World City Populations · bubble-map-geographic · altair · pyplots.ai",
240+
fontSize=28,
241+
anchor="middle",
242+
color="#333333",
243+
),
244+
width=1600,
245+
height=900,
246+
)
247+
.configure_view(stroke=None)
248+
.configure_legend(titleColor="#333333", labelColor="#555555", padding=15, cornerRadius=5)
249+
)
250+
251+
# Save as PNG (scale 3x for 4800x2700)
252+
chart.save("plot.png", scale_factor=3.0)
253+
254+
# Save interactive HTML version
255+
chart.save("plot.html")

0 commit comments

Comments
 (0)