{"spec_id":"bubble-map-geographic","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nbubble-map-geographic: Bubble Map with Sized Geographic Markers\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 85/100 | Updated: 2026-05-18\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    coord_fixed,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_point,\n    geom_polygon,\n    ggplot,\n    labs,\n    scale_color_manual,\n    scale_size_area,\n    theme,\n    theme_minimal,\n)\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nOCEAN_BG = \"#D4E8F7\" if THEME == \"light\" else \"#1A2E3D\"\nCONTINENT_FILL = \"#E0E0E0\" if THEME == \"light\" else \"#2A2A2A\"\nCONTINENT_COLOR = \"#A0A0A0\" if THEME == \"light\" else \"#555550\"\n\n# Seed for reproducibility\nnp.random.seed(42)\n\n# Major world cities with population data (in millions)\ncities_data = {\n    \"city\": [\n        \"Tokyo\",\n        \"Delhi\",\n        \"Shanghai\",\n        \"Sao Paulo\",\n        \"Mexico City\",\n        \"Cairo\",\n        \"Mumbai\",\n        \"Beijing\",\n        \"Dhaka\",\n        \"Osaka\",\n        \"New York\",\n        \"Karachi\",\n        \"Buenos Aires\",\n        \"Istanbul\",\n        \"Kolkata\",\n        \"Lagos\",\n        \"Rio de Janeiro\",\n        \"Los Angeles\",\n        \"Moscow\",\n        \"Paris\",\n        \"Bangkok\",\n        \"Seoul\",\n        \"London\",\n        \"Lima\",\n        \"Chicago\",\n        \"Santiago\",\n        \"Sydney\",\n        \"Toronto\",\n        \"Singapore\",\n        \"Dubai\",\n    ],\n    \"latitude\": [\n        35.68,\n        28.61,\n        31.23,\n        -23.55,\n        19.43,\n        30.04,\n        19.08,\n        39.90,\n        23.81,\n        34.69,\n        40.71,\n        24.86,\n        -34.60,\n        41.01,\n        22.57,\n        6.52,\n        -22.91,\n        34.05,\n        55.76,\n        48.86,\n        13.76,\n        37.57,\n        51.51,\n        -12.05,\n        41.88,\n        -33.45,\n        -33.87,\n        43.65,\n        1.35,\n        25.20,\n    ],\n    \"longitude\": [\n        139.69,\n        77.21,\n        121.47,\n        -46.63,\n        -99.13,\n        31.24,\n        72.88,\n        116.41,\n        90.41,\n        135.50,\n        -74.01,\n        67.01,\n        -58.38,\n        28.98,\n        88.36,\n        3.38,\n        -43.17,\n        -118.24,\n        37.62,\n        2.35,\n        100.50,\n        127.00,\n        -0.13,\n        -77.04,\n        -87.63,\n        -70.67,\n        151.21,\n        -79.38,\n        103.82,\n        55.27,\n    ],\n    \"population\": [\n        37.4,\n        32.9,\n        29.2,\n        22.4,\n        21.8,\n        21.3,\n        21.0,\n        20.9,\n        22.5,\n        19.1,\n        18.8,\n        16.8,\n        15.4,\n        15.6,\n        15.1,\n        15.3,\n        13.5,\n        12.5,\n        12.5,\n        11.0,\n        10.7,\n        9.9,\n        9.5,\n        11.0,\n        8.9,\n        6.8,\n        5.3,\n        6.3,\n        5.9,\n        3.4,\n    ],\n    \"region\": [\n        \"Asia\",\n        \"Asia\",\n        \"Asia\",\n        \"S. America\",\n        \"N. America\",\n        \"Africa\",\n        \"Asia\",\n        \"Asia\",\n        \"Asia\",\n        \"Asia\",\n        \"N. America\",\n        \"Asia\",\n        \"S. America\",\n        \"Europe\",\n        \"Asia\",\n        \"Africa\",\n        \"S. America\",\n        \"N. America\",\n        \"Europe\",\n        \"Europe\",\n        \"Asia\",\n        \"Asia\",\n        \"Europe\",\n        \"S. America\",\n        \"N. America\",\n        \"S. America\",\n        \"Oceania\",\n        \"N. America\",\n        \"Asia\",\n        \"Asia\",\n    ],\n}\n\ndf = pd.DataFrame(cities_data)\n\n# Sort descending so smaller bubbles render on top of larger ones\ndf = df.sort_values(\"population\", ascending=False).reset_index(drop=True)\n\n# Simplified continent outlines for basemap\ncontinents = []\n\n# North America\nna_lon = [\n    -170,\n    -168,\n    -140,\n    -125,\n    -124,\n    -117,\n    -105,\n    -97,\n    -82,\n    -77,\n    -68,\n    -55,\n    -52,\n    -80,\n    -87,\n    -97,\n    -105,\n    -125,\n    -145,\n    -165,\n    -170,\n]\nna_lat = [60, 65, 70, 55, 48, 33, 25, 26, 25, 35, 45, 48, 45, 27, 30, 20, 22, 50, 60, 55, 60]\nfor i in range(len(na_lon)):\n    continents.append({\"continent\": \"N. America\", \"order\": i, \"lon\": na_lon[i], \"lat\": na_lat[i]})\n\n# South America\nsa_lon = [-80, -68, -60, -50, -35, -40, -50, -55, -68, -72, -75, -80, -82, -80]\nsa_lat = [10, 12, 5, 0, -5, -22, -35, -52, -55, -18, -5, 0, 8, 10]\nfor i in range(len(sa_lon)):\n    continents.append({\"continent\": \"S. America\", \"order\": i, \"lon\": sa_lon[i], \"lat\": sa_lat[i]})\n\n# Europe\neu_lon = [-10, 0, 10, 20, 30, 40, 50, 60, 50, 35, 25, 20, 10, 0, -10, -10]\neu_lat = [35, 37, 36, 35, 35, 40, 45, 55, 70, 70, 70, 65, 60, 50, 40, 35]\nfor i in range(len(eu_lon)):\n    continents.append({\"continent\": \"Europe\", \"order\": i, \"lon\": eu_lon[i], \"lat\": eu_lat[i]})\n\n# Africa\naf_lon = [-17, -5, 10, 35, 50, 52, 43, 35, 30, 15, 0, -17, -17]\naf_lat = [15, 37, 37, 32, 12, 0, -25, -35, -35, -25, 5, 20, 15]\nfor i in range(len(af_lon)):\n    continents.append({\"continent\": \"Africa\", \"order\": i, \"lon\": af_lon[i], \"lat\": af_lat[i]})\n\n# Asia\nas_lon = [60, 80, 100, 120, 140, 145, 140, 130, 105, 100, 80, 60, 45, 30, 25, 30, 35, 50, 60]\nas_lat = [55, 70, 75, 70, 55, 45, 35, 30, 0, 5, 10, 25, 30, 35, 42, 55, 70, 70, 55]\nfor i in range(len(as_lon)):\n    continents.append({\"continent\": \"Asia\", \"order\": i, \"lon\": as_lon[i], \"lat\": as_lat[i]})\n\n# Australia\nau_lon = [113, 125, 135, 145, 152, 150, 140, 130, 115, 113]\nau_lat = [-22, -15, -12, -15, -25, -38, -38, -33, -35, -22]\nfor i in range(len(au_lon)):\n    continents.append({\"continent\": \"Australia\", \"order\": i, \"lon\": au_lon[i], \"lat\": au_lat[i]})\n\ndf_continents = pd.DataFrame(continents)\n\n# Okabe-Ito canonical order mapped to regions alphabetically:\n# Africa, Asia, Europe, N. America, Oceania, S. America\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\"]\n\n# Create the bubble map\nplot = (\n    ggplot()\n    # Draw continent polygons as basemap\n    + geom_polygon(\n        aes(x=\"lon\", y=\"lat\", group=\"continent\"),\n        data=df_continents,\n        fill=CONTINENT_FILL,\n        color=CONTINENT_COLOR,\n        size=0.5,\n        alpha=0.8,\n    )\n    # Draw bubble markers sized by population\n    + geom_point(aes(x=\"longitude\", y=\"latitude\", color=\"region\", size=\"population\"), data=df, alpha=0.7, stroke=0.5)\n    # Scale size by area for accurate perception (bubble area proportional to value)\n    + scale_size_area(max_size=20, name=\"Population (M)\")\n    + scale_color_manual(values=IMPRINT, name=\"Region\")\n    + coord_fixed(ratio=1.0, xlim=(-180, 180), ylim=(-60, 80))\n    + labs(\n        title=\"World City Populations · bubble-map-geographic · python · plotnine · anyplot.ai\",\n        x=\"Longitude (°)\",\n        y=\"Latitude (°)\",\n    )\n    + theme_minimal()\n    + theme(\n        figure_size=(16, 9),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=OCEAN_BG),\n        panel_grid_major=element_line(color=INK, size=0.3, alpha=0.10),\n        panel_grid_minor=element_blank(),\n        panel_border=element_rect(color=INK_SOFT, fill=None),\n        plot_title=element_text(size=24, weight=\"bold\", color=INK),\n        axis_title=element_text(size=20, color=INK),\n        axis_text=element_text(size=16, color=INK_SOFT),\n        legend_title=element_text(size=18, color=INK),\n        legend_text=element_text(size=14, color=INK_SOFT),\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        legend_position=\"right\",\n    )\n)\n\n# Save at 300 DPI for 4800x2700 px output\nplot.save(f\"plot-{THEME}.png\", dpi=300, verbose=False)\n"}