|
| 1 | +""" pyplots.ai |
| 2 | +bubble-map-geographic: Bubble Map with Sized Geographic Markers |
| 3 | +Library: plotly 6.5.1 | Python 3.13.11 |
| 4 | +Quality: 91/100 | Created: 2026-01-10 |
| 5 | +""" |
| 6 | + |
| 7 | +import numpy as np |
| 8 | +import pandas as pd |
| 9 | +import plotly.graph_objects as go |
| 10 | + |
| 11 | + |
| 12 | +# Data: Major world cities with population (in millions) |
| 13 | +np.random.seed(42) |
| 14 | + |
| 15 | +cities = [ |
| 16 | + {"city": "Tokyo", "lat": 35.6762, "lon": 139.6503, "pop": 37.4, "region": "Asia"}, |
| 17 | + {"city": "Delhi", "lat": 28.7041, "lon": 77.1025, "pop": 31.2, "region": "Asia"}, |
| 18 | + {"city": "Shanghai", "lat": 31.2304, "lon": 121.4737, "pop": 27.8, "region": "Asia"}, |
| 19 | + {"city": "São Paulo", "lat": -23.5505, "lon": -46.6333, "pop": 22.4, "region": "South America"}, |
| 20 | + {"city": "Mexico City", "lat": 19.4326, "lon": -99.1332, "pop": 21.9, "region": "North America"}, |
| 21 | + {"city": "Cairo", "lat": 30.0444, "lon": 31.2357, "pop": 21.3, "region": "Africa"}, |
| 22 | + {"city": "Mumbai", "lat": 19.0760, "lon": 72.8777, "pop": 20.7, "region": "Asia"}, |
| 23 | + {"city": "Beijing", "lat": 39.9042, "lon": 116.4074, "pop": 20.5, "region": "Asia"}, |
| 24 | + {"city": "Dhaka", "lat": 23.8103, "lon": 90.4125, "pop": 22.5, "region": "Asia"}, |
| 25 | + {"city": "Osaka", "lat": 34.6937, "lon": 135.5023, "pop": 19.2, "region": "Asia"}, |
| 26 | + {"city": "New York", "lat": 40.7128, "lon": -74.0060, "pop": 18.8, "region": "North America"}, |
| 27 | + {"city": "Karachi", "lat": 24.8607, "lon": 67.0011, "pop": 16.5, "region": "Asia"}, |
| 28 | + {"city": "Buenos Aires", "lat": -34.6037, "lon": -58.3816, "pop": 15.2, "region": "South America"}, |
| 29 | + {"city": "Istanbul", "lat": 41.0082, "lon": 28.9784, "pop": 15.4, "region": "Europe"}, |
| 30 | + {"city": "Lagos", "lat": 6.5244, "lon": 3.3792, "pop": 14.9, "region": "Africa"}, |
| 31 | + {"city": "Manila", "lat": 14.5995, "lon": 120.9842, "pop": 14.4, "region": "Asia"}, |
| 32 | + {"city": "Rio de Janeiro", "lat": -22.9068, "lon": -43.1729, "pop": 13.5, "region": "South America"}, |
| 33 | + {"city": "Los Angeles", "lat": 34.0522, "lon": -118.2437, "pop": 12.5, "region": "North America"}, |
| 34 | + {"city": "Moscow", "lat": 55.7558, "lon": 37.6173, "pop": 12.5, "region": "Europe"}, |
| 35 | + {"city": "Paris", "lat": 48.8566, "lon": 2.3522, "pop": 11.1, "region": "Europe"}, |
| 36 | + {"city": "London", "lat": 51.5074, "lon": -0.1278, "pop": 9.5, "region": "Europe"}, |
| 37 | + {"city": "Lima", "lat": -12.0464, "lon": -77.0428, "pop": 10.9, "region": "South America"}, |
| 38 | + {"city": "Bangkok", "lat": 13.7563, "lon": 100.5018, "pop": 10.7, "region": "Asia"}, |
| 39 | + {"city": "Jakarta", "lat": -6.2088, "lon": 106.8456, "pop": 10.6, "region": "Asia"}, |
| 40 | + {"city": "Seoul", "lat": 37.5665, "lon": 126.9780, "pop": 9.9, "region": "Asia"}, |
| 41 | + {"city": "Sydney", "lat": -33.8688, "lon": 151.2093, "pop": 5.4, "region": "Oceania"}, |
| 42 | + {"city": "Melbourne", "lat": -37.8136, "lon": 144.9631, "pop": 5.0, "region": "Oceania"}, |
| 43 | + {"city": "Toronto", "lat": 43.6532, "lon": -79.3832, "pop": 6.3, "region": "North America"}, |
| 44 | + {"city": "Chicago", "lat": 41.8781, "lon": -87.6298, "pop": 8.9, "region": "North America"}, |
| 45 | + {"city": "Singapore", "lat": 1.3521, "lon": 103.8198, "pop": 5.9, "region": "Asia"}, |
| 46 | +] |
| 47 | + |
| 48 | +df = pd.DataFrame(cities) |
| 49 | + |
| 50 | +# Scale bubble sizes: area proportional to population |
| 51 | +# Using sqrt to make area proportional (since marker size is diameter) |
| 52 | +min_size = 15 |
| 53 | +max_size = 70 |
| 54 | +df["marker_size"] = min_size + (max_size - min_size) * np.sqrt( |
| 55 | + (df["pop"] - df["pop"].min()) / (df["pop"].max() - df["pop"].min()) |
| 56 | +) |
| 57 | + |
| 58 | +# Color palette for regions (colorblind-safe) |
| 59 | +region_colors = { |
| 60 | + "Asia": "#306998", # Python Blue |
| 61 | + "Europe": "#FFD43B", # Python Yellow |
| 62 | + "North America": "#2ca02c", # Green |
| 63 | + "South America": "#ff7f0e", # Orange |
| 64 | + "Africa": "#9467bd", # Purple |
| 65 | + "Oceania": "#17becf", # Cyan |
| 66 | +} |
| 67 | + |
| 68 | +# Create figure with geo map |
| 69 | +fig = go.Figure() |
| 70 | + |
| 71 | +# Add traces for each region to create proper legend |
| 72 | +for region in df["region"].unique(): |
| 73 | + region_df = df[df["region"] == region] |
| 74 | + fig.add_trace( |
| 75 | + go.Scattergeo( |
| 76 | + lon=region_df["lon"], |
| 77 | + lat=region_df["lat"], |
| 78 | + text=region_df.apply(lambda x: f"{x['city']}<br>Population: {x['pop']:.1f}M", axis=1), |
| 79 | + marker={ |
| 80 | + "size": region_df["marker_size"], |
| 81 | + "color": region_colors[region], |
| 82 | + "opacity": 0.65, |
| 83 | + "line": {"width": 1.5, "color": "white"}, |
| 84 | + "sizemode": "diameter", |
| 85 | + }, |
| 86 | + name=region, |
| 87 | + hovertemplate="%{text}<extra></extra>", |
| 88 | + ) |
| 89 | + ) |
| 90 | + |
| 91 | +# Update layout for 4800x2700 |
| 92 | +fig.update_layout( |
| 93 | + title={ |
| 94 | + "text": "World City Populations · bubble-map-geographic · plotly · pyplots.ai", |
| 95 | + "font": {"size": 32, "color": "#333333"}, |
| 96 | + "x": 0.5, |
| 97 | + "xanchor": "center", |
| 98 | + }, |
| 99 | + geo={ |
| 100 | + "showland": True, |
| 101 | + "landcolor": "rgb(243, 243, 243)", |
| 102 | + "showocean": True, |
| 103 | + "oceancolor": "rgb(230, 240, 250)", |
| 104 | + "showcoastlines": True, |
| 105 | + "coastlinecolor": "rgb(150, 150, 150)", |
| 106 | + "coastlinewidth": 1, |
| 107 | + "showframe": True, |
| 108 | + "framecolor": "rgb(100, 100, 100)", |
| 109 | + "framewidth": 1, |
| 110 | + "showcountries": True, |
| 111 | + "countrycolor": "rgb(200, 200, 200)", |
| 112 | + "countrywidth": 0.5, |
| 113 | + "projection_type": "natural earth", |
| 114 | + "lataxis": {"range": [-60, 75]}, |
| 115 | + "lonaxis": {"range": [-140, 180]}, |
| 116 | + }, |
| 117 | + legend={ |
| 118 | + "title": {"text": "Region", "font": {"size": 20}}, |
| 119 | + "font": {"size": 18}, |
| 120 | + "itemsizing": "constant", |
| 121 | + "x": 0.02, |
| 122 | + "y": 0.35, |
| 123 | + "bgcolor": "rgba(255,255,255,0.85)", |
| 124 | + "bordercolor": "rgba(0,0,0,0.2)", |
| 125 | + "borderwidth": 1, |
| 126 | + }, |
| 127 | + margin={"l": 20, "r": 20, "t": 80, "b": 20}, |
| 128 | + template="plotly_white", |
| 129 | +) |
| 130 | + |
| 131 | +# Add size legend annotation |
| 132 | +fig.add_annotation( |
| 133 | + text="Bubble size = Population (millions)", |
| 134 | + xref="paper", |
| 135 | + yref="paper", |
| 136 | + x=0.02, |
| 137 | + y=0.15, |
| 138 | + showarrow=False, |
| 139 | + font={"size": 16, "color": "#555555"}, |
| 140 | + align="left", |
| 141 | +) |
| 142 | + |
| 143 | +# Example size indicators |
| 144 | +size_examples = [5, 20, 35] |
| 145 | +for i, pop_val in enumerate(size_examples): |
| 146 | + size = min_size + (max_size - min_size) * np.sqrt((pop_val - df["pop"].min()) / (df["pop"].max() - df["pop"].min())) |
| 147 | + fig.add_annotation( |
| 148 | + text=f" {pop_val}M", |
| 149 | + xref="paper", |
| 150 | + yref="paper", |
| 151 | + x=0.065, |
| 152 | + y=0.09 - i * 0.028, |
| 153 | + showarrow=False, |
| 154 | + font={"size": 14, "color": "#666666"}, |
| 155 | + align="left", |
| 156 | + ) |
| 157 | + |
| 158 | +# Save as PNG (4800x2700) |
| 159 | +fig.write_image("plot.png", width=1600, height=900, scale=3) |
| 160 | + |
| 161 | +# Save as HTML for interactivity |
| 162 | +fig.write_html("plot.html", include_plotlyjs="cdn") |
0 commit comments