{"spec_id":"map-connection-lines","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nmap-connection-lines: Connection Lines Map (Origin-Destination)\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-05-28\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom matplotlib.lines import Line2D\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# Geographic background (map chrome, not data series)\nOCEAN_BG = \"#C8DCE8\" if THEME == \"light\" else \"#1A2830\"\nLAND_COLOR = \"#DDD9CF\" if THEME == \"light\" else \"#28271F\"\n\n# anyplot categorical palette — positions 1 and 3\nBRAND = \"#009E73\"  # connection lines — first series\nAIRPORT_COLOR = \"#4467A3\"  # airport markers — third series (blue fits sky/travel)\n\n\n# Data — Major international flight routes with passenger volume\nairports = [\n    (\"New York\", 40.6413, -73.7781, 4, 4),\n    (\"London\", 51.4700, -0.4543, 4, 6),\n    (\"Tokyo\", 35.5494, 139.7798, -4, 4),\n    (\"Dubai\", 25.2532, 55.3657, 4, 4),\n    (\"Singapore\", 1.3644, 103.9915, 4, -8),\n    (\"Sydney\", -33.9399, 151.1753, 4, 4),\n    (\"São Paulo\", -23.4356, -46.4731, 4, 4),\n    (\"Los Angeles\", 33.9416, -118.4085, -4, 4),\n    (\"Paris\", 49.0097, 2.5479, 4, -10),\n    (\"Hong Kong\", 22.3080, 113.9185, -4, -8),\n]\n\nconnections = [\n    (0, 1, 4.2),  # NYC - London (busiest transatlantic)\n    (0, 8, 2.1),  # NYC - Paris\n    (1, 3, 3.5),  # London - Dubai\n    (1, 9, 2.8),  # London - Hong Kong\n    (3, 4, 3.2),  # Dubai - Singapore\n    (4, 5, 2.4),  # Singapore - Sydney\n    (4, 9, 2.9),  # Singapore - Hong Kong\n    (2, 9, 3.1),  # Tokyo - Hong Kong\n    (2, 7, 2.2),  # Tokyo - LA\n    (0, 7, 2.5),  # NYC - LA\n    (6, 0, 1.8),  # São Paulo - NYC\n    (6, 1, 1.5),  # São Paulo - London\n    (5, 4, 1.9),  # Sydney - Singapore\n    (3, 9, 2.6),  # Dubai - Hong Kong\n    (7, 2, 2.0),  # LA - Tokyo\n]\n\nroutes = []\nfor orig_idx, dest_idx, volume in connections:\n    orig = airports[orig_idx]\n    dest = airports[dest_idx]\n    routes.append(\n        {\"origin_lat\": orig[1], \"origin_lon\": orig[2], \"dest_lat\": dest[1], \"dest_lon\": dest[2], \"volume\": volume}\n    )\n\n\n# Plot\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(OCEAN_BG)\nax.set_xlim(-180, 180)\nax.set_ylim(-90, 90)\n\n# Base map — simplified continent polygons\ncontinents = [\n    [(-170, 15), (-170, 75), (-50, 75), (-50, 15)],\n    [(-85, -60), (-85, 15), (-35, 15), (-35, -60)],\n    [(-10, 35), (-10, 72), (40, 72), (40, 35)],\n    [(-20, -35), (-20, 38), (55, 38), (55, -35)],\n    [(40, 5), (40, 80), (180, 80), (180, 5)],\n    [(110, -45), (110, -10), (155, -10), (155, -45)],\n]\nfor cont in continents:\n    xs = [p[0] for p in cont] + [cont[0][0]]\n    ys = [p[1] for p in cont] + [cont[0][1]]\n    ax.fill(xs, ys, color=LAND_COLOR, alpha=0.9, zorder=1)\n\n# Volume normalization\nvolumes = [r[\"volume\"] for r in routes]\nvol_min, vol_max = min(volumes), max(volumes)\n\n# Draw great circle connection lines using spherical interpolation\nn_points = 100\nfor route in routes:\n    norm = (route[\"volume\"] - vol_min) / (vol_max - vol_min)\n    linewidth = 0.8 + norm * 3.2\n    alpha = 0.40 + norm * 0.40\n\n    lon1, lat1 = route[\"origin_lon\"], route[\"origin_lat\"]\n    lon2, lat2 = route[\"dest_lon\"], route[\"dest_lat\"]\n    lon1_r, lat1_r = np.radians(lon1), np.radians(lat1)\n    lon2_r, lat2_r = np.radians(lon2), np.radians(lat2)\n\n    cos_d = np.clip(\n        np.sin(lat1_r) * np.sin(lat2_r) + np.cos(lat1_r) * np.cos(lat2_r) * np.cos(lon2_r - lon1_r), -1.0, 1.0\n    )\n    d = np.arccos(cos_d)\n\n    if d < 1e-10:\n        lons, lats = np.array([lon1, lon2]), np.array([lat1, lat2])\n    else:\n        t = np.linspace(0, 1, n_points)\n        a = np.sin((1 - t) * d) / np.sin(d)\n        b = np.sin(t * d) / np.sin(d)\n        x = a * np.cos(lat1_r) * np.cos(lon1_r) + b * np.cos(lat2_r) * np.cos(lon2_r)\n        y = a * np.cos(lat1_r) * np.sin(lon1_r) + b * np.cos(lat2_r) * np.sin(lon2_r)\n        z = a * np.sin(lat1_r) + b * np.sin(lat2_r)\n        lats = np.degrees(np.arctan2(z, np.sqrt(x**2 + y**2)))\n        lons = np.degrees(np.arctan2(y, x))\n\n    # Handle date line crossing by splitting the line\n    if np.any(np.abs(np.diff(lons)) > 180):\n        split_idx = np.where(np.abs(np.diff(lons)) > 180)[0][0] + 1\n        ax.plot(\n            lons[:split_idx],\n            lats[:split_idx],\n            color=BRAND,\n            linewidth=linewidth,\n            alpha=alpha,\n            solid_capstyle=\"round\",\n            zorder=2,\n        )\n        ax.plot(\n            lons[split_idx:],\n            lats[split_idx:],\n            color=BRAND,\n            linewidth=linewidth,\n            alpha=alpha,\n            solid_capstyle=\"round\",\n            zorder=2,\n        )\n    else:\n        ax.plot(lons, lats, color=BRAND, linewidth=linewidth, alpha=alpha, solid_capstyle=\"round\", zorder=2)\n\n# Airport markers\nfor _name, lat, lon, _ox, _oy in airports:\n    ax.plot(\n        lon, lat, marker=\"o\", markersize=5, color=AIRPORT_COLOR, markeredgecolor=PAGE_BG, markeredgewidth=0.8, zorder=3\n    )\n\n# Airport labels with custom offsets to prevent overlap\nfor name, lat, lon, offset_x, offset_y in airports:\n    ax.annotate(\n        name,\n        (lon, lat),\n        xytext=(offset_x, offset_y),\n        textcoords=\"offset points\",\n        fontsize=8,\n        fontweight=\"bold\",\n        color=INK,\n        ha=\"left\" if offset_x > 0 else \"right\",\n        va=\"bottom\" if offset_y > 0 else \"top\",\n        zorder=4,\n    )\n\n# Style\nax.set_xlabel(\"Longitude (°)\", fontsize=10, color=INK)\nax.set_ylabel(\"Latitude (°)\", fontsize=10, color=INK)\nax.tick_params(axis=\"both\", labelsize=8, colors=INK_SOFT, labelcolor=INK_SOFT)\nax.set_aspect(\"equal\", adjustable=\"box\")\nax.grid(True, alpha=0.12, linewidth=0.5, color=INK)\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nfor spine in (\"left\", \"bottom\"):\n    ax.spines[spine].set_color(INK_SOFT)\n\n# Title — scale fontsize for long title to avoid overflow\ntitle = \"Major International Flight Routes · map-connection-lines · python · matplotlib · anyplot.ai\"\ntitle_fontsize = max(8, round(12 * 67 / len(title)))\nax.set_title(title, fontsize=title_fontsize, fontweight=\"medium\", color=INK, pad=8)\n\n# Legend\nlegend_elements = [\n    Line2D([0], [0], color=BRAND, linewidth=0.9, alpha=0.50, label=\"1.5M pax/year\"),\n    Line2D([0], [0], color=BRAND, linewidth=2.4, alpha=0.65, label=\"3M pax/year\"),\n    Line2D([0], [0], color=BRAND, linewidth=4.0, alpha=0.80, label=\"4.2M pax/year\"),\n    Line2D(\n        [0],\n        [0],\n        marker=\"o\",\n        color=\"w\",\n        markerfacecolor=AIRPORT_COLOR,\n        markeredgecolor=PAGE_BG,\n        markeredgewidth=0.8,\n        markersize=7,\n        label=\"Major Airport\",\n    ),\n]\nleg = ax.legend(handles=legend_elements, loc=\"lower left\", fontsize=8)\nif leg:\n    leg.get_frame().set_facecolor(ELEVATED_BG)\n    leg.get_frame().set_edgecolor(INK_SOFT)\n    plt.setp(leg.get_texts(), color=INK_SOFT)\n\nfig.subplots_adjust(left=0.07, right=0.98, top=0.93, bottom=0.10)\n\n# Save\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}