{"spec_id":"scatter-map-geographic","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nscatter-map-geographic: Scatter Map with Geographic Points\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-18\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import *\nfrom lets_plot import ggsave\n\n\nLetsPlot.setup_html()\n\n# Theme tokens\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\"\n\n# Okabe-Ito palette - first series always #009E73\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\"]\n\n# Data: Major world cities with population and region\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        \"Chongqing\",\n        \"Istanbul\",\n        \"Kolkata\",\n        \"Manila\",\n        \"Lagos\",\n        \"Rio de Janeiro\",\n        \"Tianjin\",\n        \"Kinshasa\",\n        \"Guangzhou\",\n        \"Los Angeles\",\n        \"Moscow\",\n        \"Shenzhen\",\n        \"Lahore\",\n        \"Bangalore\",\n        \"Paris\",\n        \"Bogota\",\n        \"Jakarta\",\n        \"Chennai\",\n        \"Lima\",\n        \"Bangkok\",\n        \"Seoul\",\n        \"Nagoya\",\n        \"Hyderabad\",\n        \"London\",\n        \"Tehran\",\n        \"Chicago\",\n        \"Chengdu\",\n        \"Nanjing\",\n        \"Wuhan\",\n        \"Ho Chi Minh City\",\n        \"Luanda\",\n        \"Ahmedabad\",\n        \"Kuala Lumpur\",\n        \"Hong Kong\",\n        \"Hangzhou\",\n        \"Sydney\",\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        29.43,\n        41.01,\n        22.57,\n        14.60,\n        6.52,\n        -22.91,\n        39.34,\n        -4.44,\n        23.13,\n        34.05,\n        55.76,\n        22.54,\n        31.56,\n        12.97,\n        48.86,\n        4.71,\n        -6.21,\n        13.08,\n        -12.05,\n        13.76,\n        37.57,\n        35.18,\n        17.39,\n        51.51,\n        35.69,\n        41.88,\n        30.57,\n        32.06,\n        30.59,\n        10.82,\n        -8.84,\n        23.02,\n        3.14,\n        22.32,\n        30.27,\n        -33.87,\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        106.91,\n        28.98,\n        88.36,\n        120.98,\n        3.38,\n        -43.17,\n        117.20,\n        15.27,\n        113.26,\n        -118.24,\n        37.62,\n        114.06,\n        74.35,\n        77.59,\n        2.35,\n        -74.07,\n        106.85,\n        80.27,\n        -77.04,\n        100.50,\n        127.00,\n        136.91,\n        78.49,\n        -0.13,\n        51.39,\n        -87.63,\n        104.07,\n        118.80,\n        114.31,\n        106.63,\n        13.23,\n        72.57,\n        101.69,\n        114.17,\n        120.15,\n        151.21,\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        16.9,\n        15.6,\n        15.1,\n        14.4,\n        15.3,\n        13.5,\n        13.6,\n        17.0,\n        14.3,\n        12.5,\n        12.5,\n        13.4,\n        13.5,\n        13.2,\n        11.0,\n        11.3,\n        11.2,\n        11.5,\n        11.0,\n        10.7,\n        9.9,\n        9.5,\n        10.5,\n        9.5,\n        9.4,\n        8.9,\n        9.4,\n        9.0,\n        8.3,\n        9.1,\n        8.9,\n        8.4,\n        8.3,\n        7.5,\n        8.2,\n        5.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        \"Asia\",\n        \"Europe\",\n        \"Asia\",\n        \"Asia\",\n        \"Africa\",\n        \"S. America\",\n        \"Asia\",\n        \"Africa\",\n        \"Asia\",\n        \"N. America\",\n        \"Europe\",\n        \"Asia\",\n        \"Asia\",\n        \"Asia\",\n        \"Europe\",\n        \"S. America\",\n        \"Asia\",\n        \"Asia\",\n        \"S. America\",\n        \"Asia\",\n        \"Asia\",\n        \"Asia\",\n        \"Asia\",\n        \"Europe\",\n        \"Asia\",\n        \"N. America\",\n        \"Asia\",\n        \"Asia\",\n        \"Asia\",\n        \"Asia\",\n        \"Africa\",\n        \"Asia\",\n        \"Asia\",\n        \"Asia\",\n        \"Asia\",\n        \"Oceania\",\n    ],\n}\n\ndf = pd.DataFrame(cities_data)\n\n# Continent basemap with Australia/Oceania\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 = [112, 130, 155, 168, 155, 145, 130, 115, 112]\nau_lat = [-10, -10, -5, 0, -20, -35, -40, -30, -10]\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# Region color mapping using Okabe-Ito\nregion_colors = {\n    \"Asia\": IMPRINT[0],  # #009E73\n    \"Europe\": IMPRINT[1],  # #C475FD\n    \"N. America\": IMPRINT[2],  # #4467A3\n    \"S. America\": IMPRINT[3],  # #BD8233\n    \"Africa\": IMPRINT[4],  # #AE3030\n    \"Oceania\": IMPRINT[5],  # #2ABCCD\n}\n\n# Basemap color\nbasemap_fill = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nbasemap_border = \"#B0B0B0\" if THEME == \"light\" else \"#6B6A63\"\n\n# Create the geographic scatter map\nplot = (\n    ggplot()\n    + geom_polygon(\n        aes(x=\"lon\", y=\"lat\", group=\"continent\"),\n        data=df_continents,\n        fill=basemap_fill,\n        color=basemap_border,\n        size=0.3,\n        alpha=0.6,\n    )\n    + geom_point(\n        aes(x=\"longitude\", y=\"latitude\", color=\"region\", size=\"population\"),\n        data=df,\n        alpha=0.85,\n        tooltips=layer_tooltips()\n        .title(\"@city\")\n        .line(\"Population|@population M\")\n        .line(\"Region|@region\"),\n    )\n    + scale_color_manual(values=list(region_colors.values()), name=\"Region\")\n    + scale_size(range=[4, 18], name=\"Population (M)\")\n    + labs(\n        title=\"scatter-map-geographic · python · letsplot · anyplot.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_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        plot_title=element_text(size=24, color=INK),\n        axis_title=element_text(size=20, color=INK),\n        axis_text=element_text(size=16, color=INK_SOFT),\n        axis_line=element_line(color=INK_SOFT, size=0.5),\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        legend_text=element_text(size=16, color=INK_SOFT),\n        legend_title=element_text(size=18, color=INK),\n        legend_position=\"bottom\",\n        panel_grid_major=element_line(color=INK_SOFT, size=0.2),\n        panel_grid_minor=element_blank(),\n    )\n)\n\n# Save PNG (scale 3x to get 4800 x 2700 px)\nggsave(plot, filename=f\"plot-{THEME}.png\", path=\".\", scale=3)\n\n# Save HTML for interactive version\nggsave(plot, filename=f\"plot-{THEME}.html\", path=\".\")\n"}