{"spec_id":"bubble-map-geographic","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nbubble-map-geographic: Bubble Map with Sized Geographic Markers\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 80/100 | Updated: 2026-05-18\n\"\"\"\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import *\nfrom lets_plot.export import ggsave as export_ggsave\n\n\nLetsPlot.setup_html()\n\n# Data: Major world cities with population (millions)\n# Bubble size represents population magnitude - the primary visual encoding\nnp.random.seed(42)\n\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        \"Lagos\",\n        \"Rio de Janeiro\",\n        \"Moscow\",\n        \"Paris\",\n        \"London\",\n        \"Los Angeles\",\n        \"Bangkok\",\n        \"Seoul\",\n        \"Lima\",\n        \"Sydney\",\n        \"Toronto\",\n        \"Singapore\",\n        \"Dubai\",\n        \"Madrid\",\n        \"Berlin\",\n        \"Rome\",\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        6.52,\n        -22.91,\n        55.76,\n        48.86,\n        51.51,\n        34.05,\n        13.76,\n        37.57,\n        -12.05,\n        -33.87,\n        43.65,\n        1.35,\n        25.20,\n        40.42,\n        52.52,\n        41.90,\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        3.38,\n        -43.17,\n        37.62,\n        2.35,\n        -0.13,\n        -118.24,\n        100.50,\n        127.00,\n        -77.04,\n        151.21,\n        -79.38,\n        103.82,\n        55.27,\n        -3.70,\n        13.40,\n        12.50,\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.3,\n        13.5,\n        12.5,\n        11.0,\n        9.5,\n        12.5,\n        10.7,\n        9.9,\n        11.0,\n        5.4,\n        6.2,\n        5.9,\n        3.5,\n        6.7,\n        3.6,\n        4.3,\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        \"Africa\",\n        \"S. America\",\n        \"Europe\",\n        \"Europe\",\n        \"Europe\",\n        \"N. America\",\n        \"Asia\",\n        \"Asia\",\n        \"S. America\",\n        \"Oceania\",\n        \"N. America\",\n        \"Asia\",\n        \"Asia\",\n        \"Europe\",\n        \"Europe\",\n        \"Europe\",\n    ],\n}\n\ndf = pd.DataFrame(cities_data)\n\n# Simplified continent outlines for basemap (closed polygons)\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/Oceania\nau_lon = [113, 130, 150, 155, 153, 145, 130, 115, 113]\nau_lat = [-25, -12, -15, -25, -35, -40, -35, -35, -25]\nfor i in range(len(au_lon)):\n    continents.append({\"continent\": \"Oceania\", \"order\": i, \"lon\": au_lon[i], \"lat\": au_lat[i]})\n\ndf_continents = pd.DataFrame(continents)\n\n# Create the geographic bubble map\n# Size is the PRIMARY visual encoding - use scale_size to map area proportionally\nplot = (\n    ggplot()\n    + geom_polygon(\n        aes(x=\"lon\", y=\"lat\", group=\"continent\"),\n        data=df_continents,\n        fill=\"#E5E5E5\",\n        color=\"#A0A0A0\",\n        size=0.4,\n        alpha=0.8,\n    )\n    + geom_point(\n        aes(x=\"longitude\", y=\"latitude\", size=\"population\", color=\"region\"),\n        data=df,\n        alpha=0.65,\n        stroke=0.8,\n        tooltips=layer_tooltips()\n        .title(\"@city\")\n        .line(\"Population|@population million\")\n        .line(\"Region|@region\"),\n    )\n    # Scale bubble area proportionally - larger range for bubble emphasis\n    + scale_size(range=[4, 28], name=\"Population (M)\", breaks=[5, 10, 20, 30])\n    + scale_color_manual(\n        values=[\"#306998\", \"#DC2626\", \"#2CA02C\", \"#9467BD\", \"#FFD43B\", \"#17BECF\"], name=\"Region\"\n    )\n    + labs(\n        title=\"World City Populations · bubble-map-geographic · letsplot · pyplots.ai\", x=\"Longitude\", y=\"Latitude\"\n    )\n    + coord_fixed(ratio=1.0, xlim=[-180, 180], ylim=[-60, 80])\n    + ggsize(1600, 900)\n    + theme_minimal()\n    + theme(\n        plot_title=element_text(size=26, face=\"bold\"),\n        axis_title=element_text(size=20),\n        axis_text=element_text(size=16),\n        legend_title=element_text(size=18),\n        legend_text=element_text(size=14),\n        legend_position=\"right\",\n        panel_grid_major=element_line(color=\"#D0D0D0\", size=0.3),\n        panel_grid_minor=element_blank(),\n        plot_background=element_rect(fill=\"#F8F8F8\"),\n    )\n)\n\n# Save PNG (scale 3x to get 4800 x 2700 px)\nexport_ggsave(plot, filename=\"plot.png\", path=\".\", scale=3)\n\n# Save HTML for interactive version\nexport_ggsave(plot, filename=\"plot.html\", path=\".\")\n"}