{"spec_id":"scatter-map-geographic","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nscatter-map-geographic: Scatter Map with Geographic Points\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 94/100 | Updated: 2026-05-18\n\"\"\"\n\nimport os\nimport sys\n\n\nscript_dir = os.path.dirname(os.path.abspath(__file__))\nwhile script_dir in sys.path:\n    sys.path.remove(script_dir)\n\nimport altair as alt\nimport pandas as pd\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nBASEMAP_FILL = \"#E5E3D8\" if THEME == \"light\" else \"#3A3935\"\nBASEMAP_STROKE = \"#9C9A91\" if THEME == \"light\" else \"#5A5855\"\n\n# Data: Major world cities with population and region\ncities_data = {\n    \"city\": [\n        \"Tokyo\",\n        \"Delhi\",\n        \"Shanghai\",\n        \"São 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        \"Bogotá\",\n        \"Jakarta\",\n        \"Lima\",\n        \"Bangkok\",\n        \"London\",\n        \"Chennai\",\n        \"Hyderabad\",\n        \"Nagoya\",\n        \"Ho Chi Minh City\",\n        \"Johannesburg\",\n        \"Toronto\",\n        \"Sydney\",\n        \"Casablanca\",\n        \"Addis Ababa\",\n        \"Nairobi\",\n        \"Dar es Salaam\",\n    ],\n    \"latitude\": [\n        35.6762,\n        28.7041,\n        31.2304,\n        -23.5505,\n        19.4326,\n        30.0444,\n        19.0760,\n        39.9042,\n        23.8103,\n        34.6937,\n        40.7128,\n        24.8607,\n        -34.6037,\n        29.4316,\n        41.0082,\n        22.5726,\n        14.5995,\n        6.5244,\n        -22.9068,\n        39.3434,\n        -4.4419,\n        23.1291,\n        34.0522,\n        55.7558,\n        22.5431,\n        31.5497,\n        12.9716,\n        48.8566,\n        4.7110,\n        -6.2088,\n        -12.0464,\n        13.7563,\n        51.5074,\n        13.0827,\n        17.3850,\n        35.1815,\n        10.8231,\n        -26.2041,\n        43.6532,\n        -33.8688,\n        33.5731,\n        9.0320,\n        -1.2921,\n        -6.8000,\n    ],\n    \"longitude\": [\n        139.6503,\n        77.1025,\n        121.4737,\n        -46.6333,\n        -99.1332,\n        31.2357,\n        72.8777,\n        116.4074,\n        90.4125,\n        135.5023,\n        -74.0060,\n        67.0011,\n        -58.3816,\n        106.9123,\n        28.9784,\n        88.3639,\n        120.9842,\n        3.3792,\n        -43.1729,\n        117.3616,\n        15.2663,\n        113.2644,\n        -118.2437,\n        37.6173,\n        114.0579,\n        74.3436,\n        77.5946,\n        2.3522,\n        -74.0721,\n        106.8456,\n        -77.0428,\n        100.5018,\n        -0.1278,\n        80.2707,\n        78.4867,\n        136.9066,\n        106.6297,\n        28.0473,\n        -79.3832,\n        151.2093,\n        -7.5898,\n        38.7469,\n        36.8219,\n        39.2069,\n    ],\n    \"population_millions\": [\n        37.4,\n        32.9,\n        29.2,\n        22.4,\n        21.9,\n        21.3,\n        21.0,\n        20.9,\n        20.3,\n        19.2,\n        18.8,\n        16.5,\n        15.4,\n        15.4,\n        15.2,\n        14.9,\n        14.2,\n        14.1,\n        13.6,\n        13.6,\n        13.2,\n        13.0,\n        12.5,\n        12.5,\n        12.4,\n        12.3,\n        12.2,\n        11.0,\n        10.9,\n        10.8,\n        10.7,\n        10.5,\n        9.5,\n        9.3,\n        9.2,\n        9.1,\n        8.8,\n        5.8,\n        6.2,\n        5.3,\n        3.9,\n        4.4,\n        4.0,\n        4.7,\n    ],\n    \"region\": [\n        \"Asia\",\n        \"Asia\",\n        \"Asia\",\n        \"South America\",\n        \"North America\",\n        \"Africa\",\n        \"Asia\",\n        \"Asia\",\n        \"Asia\",\n        \"Asia\",\n        \"North America\",\n        \"Asia\",\n        \"South America\",\n        \"Asia\",\n        \"Europe\",\n        \"Asia\",\n        \"Asia\",\n        \"Africa\",\n        \"South America\",\n        \"Asia\",\n        \"Africa\",\n        \"Asia\",\n        \"North America\",\n        \"Europe\",\n        \"Asia\",\n        \"Asia\",\n        \"Asia\",\n        \"Europe\",\n        \"South America\",\n        \"Asia\",\n        \"South America\",\n        \"Asia\",\n        \"Europe\",\n        \"Asia\",\n        \"Asia\",\n        \"Asia\",\n        \"Asia\",\n        \"Africa\",\n        \"North America\",\n        \"Oceania\",\n        \"Africa\",\n        \"Africa\",\n        \"Africa\",\n        \"Africa\",\n    ],\n}\n\ndf = pd.DataFrame(cities_data)\n\n# Load world basemap\nworld = alt.topo_feature(\"https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json\", \"countries\")\n\n# Create basemap layer\nbasemap = (\n    alt.Chart(world)\n    .mark_geoshape(fill=BASEMAP_FILL, stroke=BASEMAP_STROKE, strokeWidth=0.5)\n    .project(type=\"naturalEarth1\")\n    .properties(width=1600, height=900)\n)\n\n# Define color scale for regions using Okabe-Ito palette positions\nregion_colors = {\n    \"Asia\": \"#009E73\",  # OI position 1 (brand green)\n    \"Africa\": \"#C475FD\",  # OI position 2 (vermillion)\n    \"Europe\": \"#4467A3\",  # OI position 3 (blue)\n    \"North America\": \"#BD8233\",  # OI position 4 (reddish purple)\n    \"South America\": \"#AE3030\",  # OI position 5 (orange)\n    \"Oceania\": \"#2ABCCD\",  # OI position 6 (sky blue)\n}\n\n# Create scatter points layer\npoints = (\n    alt.Chart(df)\n    .mark_circle(opacity=0.75, stroke=PAGE_BG, strokeWidth=1.5)\n    .encode(\n        longitude=\"longitude:Q\",\n        latitude=\"latitude:Q\",\n        size=alt.Size(\n            \"population_millions:Q\",\n            scale=alt.Scale(range=[150, 2000]),\n            legend=alt.Legend(\n                title=\"Population (millions)\", titleFontSize=18, labelFontSize=16, orient=\"bottom-left\", offset=20\n            ),\n        ),\n        color=alt.Color(\n            \"region:N\",\n            scale=alt.Scale(domain=list(region_colors.keys()), range=list(region_colors.values())),\n            legend=alt.Legend(title=\"Region\", titleFontSize=18, labelFontSize=16, orient=\"bottom-right\", offset=20),\n        ),\n        tooltip=[\"city:N\", \"population_millions:Q\", \"region:N\"],\n    )\n    .project(type=\"naturalEarth1\")\n    .properties(width=1600, height=900)\n)\n\n# Combine layers with theme-adaptive styling\nchart = (\n    alt.layer(basemap, points)\n    .properties(\n        title=alt.Title(\n            text=\"World Major Cities · scatter-map-geographic · python · altair · anyplot.ai\",\n            fontSize=28,\n            anchor=\"middle\",\n            color=INK,\n        ),\n        background=PAGE_BG,\n    )\n    .configure_view(stroke=None, fill=PAGE_BG)\n    .configure_legend(\n        padding=15,\n        cornerRadius=0,\n        fillColor=\"transparent\",\n        strokeColor=INK_SOFT,\n        strokeWidth=1,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n    )\n    .configure_axis(labelColor=INK_SOFT, titleColor=INK)\n    .configure_title(color=INK)\n)\n\n# Save as PNG and HTML with theme-suffixed filenames\nscript_dir = os.path.dirname(os.path.abspath(__file__))\npng_path = os.path.join(script_dir, f\"plot-{THEME}.png\")\nhtml_path = os.path.join(script_dir, f\"plot-{THEME}.html\")\nchart.save(png_path, scale_factor=3.0)\nchart.save(html_path)\n"}