{"spec_id":"scatter-map-geographic","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nscatter-map-geographic: Scatter Map with Geographic Points\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 95/100 | Updated: 2026-05-18\n\"\"\"\n\nimport os\n\nimport matplotlib.patches as mpatches\nimport matplotlib.pyplot as plt\nimport numpy as np\n\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Okabe-Ito palette — use positions 1→N in canonical order\nIMPRINT = [\n    \"#009E73\",  # 1: bluish green (ALWAYS first series)\n    \"#C475FD\",  # 2: vermillion\n    \"#4467A3\",  # 3: blue\n    \"#BD8233\",  # 4: reddish purple\n    \"#AE3030\",  # 5: orange\n    \"#2ABCCD\",  # 6: sky blue\n]\n\n# Data - Major world cities with population and region\nnp.random.seed(42)\n\ncities = {\n    \"Tokyo\": (35.6762, 139.6503, 37.4, \"Asia\"),\n    \"Delhi\": (28.6139, 77.2090, 32.9, \"Asia\"),\n    \"Shanghai\": (31.2304, 121.4737, 28.5, \"Asia\"),\n    \"São Paulo\": (-23.5505, -46.6333, 22.4, \"South America\"),\n    \"Mexico City\": (19.4326, -99.1332, 21.8, \"North America\"),\n    \"Cairo\": (30.0444, 31.2357, 21.3, \"Africa\"),\n    \"Mumbai\": (19.0760, 72.8777, 20.7, \"Asia\"),\n    \"Beijing\": (39.9042, 116.4074, 20.5, \"Asia\"),\n    \"Dhaka\": (23.8103, 90.4125, 22.5, \"Asia\"),\n    \"Osaka\": (34.6937, 135.5023, 19.2, \"Asia\"),\n    \"New York\": (40.7128, -74.0060, 18.8, \"North America\"),\n    \"Karachi\": (24.8607, 67.0011, 16.5, \"Asia\"),\n    \"Buenos Aires\": (-34.6037, -58.3816, 15.4, \"South America\"),\n    \"Istanbul\": (41.0082, 28.9784, 15.4, \"Europe\"),\n    \"Lagos\": (6.5244, 3.3792, 14.9, \"Africa\"),\n    \"Los Angeles\": (34.0522, -118.2437, 12.5, \"North America\"),\n    \"London\": (51.5074, -0.1278, 9.5, \"Europe\"),\n    \"Paris\": (48.8566, 2.3522, 11.0, \"Europe\"),\n    \"Moscow\": (55.7558, 37.6173, 12.5, \"Europe\"),\n    \"Chicago\": (41.8781, -87.6298, 8.9, \"North America\"),\n    \"Sydney\": (-33.8688, 151.2093, 5.4, \"Oceania\"),\n    \"Lima\": (-12.0464, -77.0428, 11.0, \"South America\"),\n    \"Bangkok\": (13.7563, 100.5018, 10.7, \"Asia\"),\n    \"Seoul\": (37.5665, 126.9780, 9.8, \"Asia\"),\n    \"Jakarta\": (-6.2088, 106.8456, 10.6, \"Asia\"),\n}\n\n# Extract data\nnames = list(cities.keys())\nlats = np.array([cities[c][0] for c in names])\nlons = np.array([cities[c][1] for c in names])\npopulations = np.array([cities[c][2] for c in names])\nregions = [cities[c][3] for c in names]\n\n# Map regions to Okabe-Ito colors\nunique_regions = [\"Asia\", \"South America\", \"North America\", \"Africa\", \"Europe\", \"Oceania\"]\nregion_colors = {region: IMPRINT[i] for i, region in enumerate(unique_regions)}\ncolors = [region_colors[r] for r in regions]\n\n# Scale sizes based on population\nsizes = populations * 22\n\n# Simplified world map coastlines\ncontinents = [\n    # North America\n    [(-168, 66), (-165, 60), (-141, 60), (-141, 70), (-156, 71), (-168, 66)],\n    # Alaska + Canada + USA main\n    [\n        (-168, 52),\n        (-162, 55),\n        (-152, 60),\n        (-141, 60),\n        (-130, 56),\n        (-125, 50),\n        (-124, 42),\n        (-117, 33),\n        (-110, 32),\n        (-105, 29),\n        (-97, 26),\n        (-97, 28),\n        (-95, 30),\n        (-90, 30),\n        (-85, 30),\n        (-82, 25),\n        (-81, 25),\n        (-80, 32),\n        (-75, 35),\n        (-70, 41),\n        (-67, 45),\n        (-65, 45),\n        (-64, 47),\n        (-67, 48),\n        (-70, 47),\n        (-75, 45),\n        (-80, 45),\n        (-84, 46),\n        (-88, 48),\n        (-95, 49),\n        (-102, 49),\n        (-120, 49),\n        (-123, 49),\n        (-130, 55),\n        (-140, 60),\n        (-148, 60),\n        (-153, 58),\n        (-162, 55),\n        (-168, 52),\n    ],\n    # Mexico + Central America\n    [\n        (-117, 33),\n        (-115, 30),\n        (-112, 29),\n        (-110, 25),\n        (-105, 22),\n        (-100, 20),\n        (-97, 20),\n        (-95, 18),\n        (-92, 16),\n        (-88, 18),\n        (-87, 16),\n        (-84, 10),\n        (-82, 9),\n        (-78, 9),\n        (-77, 8),\n        (-80, 8),\n        (-80, 15),\n        (-88, 21),\n        (-90, 22),\n        (-97, 26),\n        (-105, 29),\n        (-110, 32),\n        (-117, 33),\n    ],\n    # South America\n    [\n        (-78, 10),\n        (-71, 12),\n        (-67, 11),\n        (-63, 10),\n        (-60, 8),\n        (-55, 5),\n        (-50, 0),\n        (-45, -2),\n        (-40, -3),\n        (-35, -6),\n        (-35, -10),\n        (-37, -15),\n        (-40, -20),\n        (-42, -23),\n        (-47, -25),\n        (-50, -28),\n        (-53, -33),\n        (-58, -38),\n        (-66, -55),\n        (-74, -52),\n        (-76, -48),\n        (-75, -42),\n        (-72, -37),\n        (-72, -30),\n        (-71, -20),\n        (-70, -18),\n        (-78, -6),\n        (-81, -3),\n        (-80, 0),\n        (-78, 3),\n        (-77, 7),\n        (-78, 10),\n    ],\n    # Europe + UK\n    [(-10, 36), (-6, 37), (-2, 36), (3, 43), (0, 44), (-2, 43), (-8, 44), (-9, 42), (-10, 36)],\n    [(-6, 50), (-5, 54), (-4, 58), (-8, 58), (-6, 55), (-6, 50)],\n    # European mainland\n    [\n        (-5, 43),\n        (0, 43),\n        (3, 43),\n        (6, 44),\n        (8, 44),\n        (12, 46),\n        (14, 45),\n        (14, 41),\n        (16, 40),\n        (20, 40),\n        (24, 37),\n        (26, 38),\n        (28, 41),\n        (30, 42),\n        (32, 42),\n        (34, 42),\n        (37, 45),\n        (40, 46),\n        (44, 42),\n        (50, 37),\n        (52, 30),\n        (56, 27),\n        (60, 25),\n        (70, 25),\n        (75, 25),\n        (78, 22),\n        (78, 8),\n        (76, 8),\n        (72, 18),\n        (70, 22),\n        (66, 25),\n        (60, 25),\n        (56, 27),\n        (50, 30),\n        (42, 31),\n        (37, 32),\n        (33, 30),\n        (30, 31),\n        (25, 35),\n        (22, 36),\n        (18, 40),\n        (14, 41),\n        (12, 44),\n        (10, 47),\n        (8, 48),\n        (5, 49),\n        (4, 51),\n        (3, 51),\n        (5, 54),\n        (10, 54),\n        (10, 56),\n        (12, 56),\n        (14, 54),\n        (19, 55),\n        (22, 56),\n        (24, 55),\n        (28, 56),\n        (30, 60),\n        (32, 65),\n        (28, 70),\n        (20, 70),\n        (12, 65),\n        (10, 62),\n        (5, 58),\n        (3, 54),\n        (-2, 50),\n        (-5, 48),\n        (-5, 43),\n    ],\n    # Africa\n    [\n        (-17, 14),\n        (-17, 21),\n        (-13, 28),\n        (-10, 32),\n        (-6, 35),\n        (0, 36),\n        (10, 37),\n        (11, 34),\n        (15, 32),\n        (20, 32),\n        (25, 32),\n        (30, 31),\n        (33, 30),\n        (35, 28),\n        (37, 22),\n        (42, 14),\n        (44, 11),\n        (51, 11),\n        (51, 3),\n        (42, 0),\n        (42, -4),\n        (40, -10),\n        (38, -18),\n        (35, -22),\n        (32, -28),\n        (28, -33),\n        (20, -35),\n        (17, -30),\n        (15, -25),\n        (12, -17),\n        (12, -6),\n        (9, 4),\n        (5, 5),\n        (0, 6),\n        (-5, 5),\n        (-10, 7),\n        (-15, 11),\n        (-17, 14),\n    ],\n    # Asia (main landmass - simplified)\n    [\n        (28, 70),\n        (40, 70),\n        (50, 68),\n        (60, 70),\n        (80, 72),\n        (100, 77),\n        (120, 75),\n        (140, 72),\n        (160, 65),\n        (170, 60),\n        (165, 55),\n        (160, 52),\n        (150, 46),\n        (140, 44),\n        (135, 35),\n        (129, 33),\n        (125, 35),\n        (120, 32),\n        (122, 25),\n        (118, 23),\n        (110, 18),\n        (105, 16),\n        (100, 14),\n        (100, 20),\n        (105, 22),\n        (108, 22),\n        (100, 10),\n        (104, 2),\n        (98, 0),\n        (96, 6),\n        (92, 22),\n        (88, 22),\n        (92, 22),\n        (88, 26),\n        (82, 28),\n        (80, 28),\n        (78, 33),\n        (74, 35),\n        (72, 25),\n        (66, 25),\n        (60, 25),\n        (56, 27),\n        (52, 30),\n        (50, 37),\n        (44, 42),\n        (40, 46),\n        (37, 45),\n        (34, 42),\n        (32, 42),\n        (30, 42),\n        (28, 56),\n        (28, 70),\n    ],\n    # Japan\n    [\n        (130, 32),\n        (132, 34),\n        (136, 35),\n        (140, 36),\n        (141, 40),\n        (141, 45),\n        (145, 44),\n        (145, 42),\n        (144, 38),\n        (140, 36),\n        (136, 35),\n        (132, 32),\n        (130, 32),\n    ],\n    # Australia\n    [\n        (113, -22),\n        (115, -21),\n        (117, -20),\n        (122, -18),\n        (130, -12),\n        (135, -12),\n        (137, -16),\n        (139, -17),\n        (141, -13),\n        (145, -15),\n        (150, -23),\n        (153, -28),\n        (152, -33),\n        (150, -37),\n        (145, -38),\n        (140, -38),\n        (136, -35),\n        (130, -32),\n        (125, -32),\n        (117, -35),\n        (115, -34),\n        (115, -30),\n        (113, -25),\n        (113, -22),\n    ],\n]\n\n# Plot\nfig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Ocean color (theme-adaptive) - sophisticated water tones\nocean_bg = \"#C5D9ED\" if THEME == \"light\" else \"#3A4E68\"\n\n# Draw continents (theme-adaptive) - refined earth tones\ncontinent_color = \"#D6CCBE\" if THEME == \"light\" else \"#4B4540\"\ncontinent_edge = INK_SOFT\nfor continent in continents:\n    poly = plt.Polygon(continent, facecolor=continent_color, edgecolor=continent_edge, linewidth=0.6, zorder=1)\n    ax.add_patch(poly)\n\n# Add graticule (grid lines)\nfor lat in range(-60, 90, 30):\n    ax.axhline(y=lat, color=INK_SOFT, linewidth=0.4, linestyle=\":\", alpha=0.15, zorder=0)\nfor lon in range(-150, 181, 30):\n    ax.axvline(x=lon, color=INK_SOFT, linewidth=0.4, linestyle=\":\", alpha=0.15, zorder=0)\n\n# Plot cities\nax.scatter(lons, lats, c=colors, s=sizes, alpha=0.8, edgecolors=PAGE_BG, linewidths=2.5, zorder=5)\n\n# Highlight Tokyo with subtle annotation for visual storytelling\ntokyo_lat, tokyo_lon = 35.6762, 139.6503\nax.annotate(\n    \"Tokyo\\n(37.4M)\",\n    xy=(tokyo_lon, tokyo_lat),\n    xytext=(tokyo_lon + 8, tokyo_lat + 6),\n    fontsize=12,\n    color=INK_SOFT,\n    weight=\"medium\",\n    bbox={\"boxstyle\": \"round,pad=0.5\", \"facecolor\": ELEVATED_BG, \"edgecolor\": INK_SOFT, \"alpha\": 0.85},\n    arrowprops={\"arrowstyle\": \"->\", \"color\": INK_SOFT, \"lw\": 1.2, \"alpha\": 0.7},\n    zorder=10,\n)\n\n# Set axis limits\nax.set_xlim(-180, 180)\nax.set_ylim(-60, 80)\n\n# Style\nax.set_xlabel(\"Longitude (°)\", fontsize=20, color=INK)\nax.set_ylabel(\"Latitude (°)\", fontsize=20, color=INK)\nax.set_title(\n    \"World's Largest Cities by Population · scatter-map-geographic · python · matplotlib · anyplot.ai\",\n    fontsize=24,\n    fontweight=\"medium\",\n    color=INK,\n    pad=15,\n)\nax.tick_params(axis=\"both\", labelsize=18, colors=INK_SOFT)\nfor s in (\"left\", \"bottom\"):\n    ax.spines[s].set_color(INK_SOFT)\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\n\n# Create region legend\nlegend_handles = []\nfor region in unique_regions:\n    color = region_colors[region]\n    handle = mpatches.Patch(facecolor=color, edgecolor=PAGE_BG, linewidth=1.5, label=region)\n    legend_handles.append(handle)\n\nregion_legend = ax.legend(\n    handles=legend_handles,\n    title=\"Region\",\n    loc=\"lower left\",\n    fontsize=14,\n    title_fontsize=16,\n    framealpha=0.95,\n    facecolor=ELEVATED_BG,\n    edgecolor=INK_SOFT,\n)\nregion_legend.get_title().set_color(INK)\nplt.setp(region_legend.get_texts(), color=INK_SOFT)\nax.add_artist(region_legend)\n\n# Create size legend (population)\nsize_values = [10, 20, 35]\nsize_labels = [\"10M\", \"20M\", \"35M\"]\nsize_handles = []\nfor val, label in zip(size_values, size_labels, strict=True):\n    handle = ax.scatter([], [], c=IMPRINT[0], s=val * 22, label=label, edgecolors=PAGE_BG, linewidths=1.5, alpha=0.8)\n    size_handles.append(handle)\n\nsize_legend = ax.legend(\n    handles=size_handles,\n    title=\"Population\",\n    loc=\"lower right\",\n    fontsize=14,\n    title_fontsize=16,\n    framealpha=0.95,\n    facecolor=ELEVATED_BG,\n    edgecolor=INK_SOFT,\n)\nsize_legend.get_title().set_color(INK)\nplt.setp(size_legend.get_texts(), color=INK_SOFT)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}