{"spec_id":"network-transport-static","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nnetwork-transport-static: Static Transport Network Diagram\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 92/100 | Updated: 2026-05-18\n\"\"\"\n\nimport os\n\nimport matplotlib.patches as mpatches\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport seaborn as sns\n\n\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 (positions 1-3 for route types)\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\"]\n\n# Set seaborn style with theme-adaptive colors\nsns.set_theme(\n    style=\"white\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK_SOFT,\n        \"grid.alpha\": 0.10,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Station data with coordinates\nstations = [\n    {\"id\": \"A\", \"label\": \"Central Station\", \"x\": 0.5, \"y\": 0.9},\n    {\"id\": \"B\", \"label\": \"North Terminal\", \"x\": 0.2, \"y\": 0.7},\n    {\"id\": \"C\", \"label\": \"East Hub\", \"x\": 0.8, \"y\": 0.7},\n    {\"id\": \"D\", \"label\": \"West Junction\", \"x\": 0.1, \"y\": 0.45},\n    {\"id\": \"E\", \"label\": \"Park Station\", \"x\": 0.4, \"y\": 0.55},\n    {\"id\": \"F\", \"label\": \"Market Square\", \"x\": 0.6, \"y\": 0.55},\n    {\"id\": \"G\", \"label\": \"Airport\", \"x\": 0.9, \"y\": 0.45},\n    {\"id\": \"H\", \"label\": \"Industrial Zone\", \"x\": 0.15, \"y\": 0.2},\n    {\"id\": \"I\", \"label\": \"University\", \"x\": 0.5, \"y\": 0.25},\n    {\"id\": \"J\", \"label\": \"Harbor\", \"x\": 0.85, \"y\": 0.15},\n]\n\n# Route data - directed edges with times and route IDs\nroutes = [\n    {\"source\": \"A\", \"target\": \"B\", \"route_id\": \"RE 01\", \"dep\": \"06:00\", \"arr\": \"06:18\", \"type\": \"regional\"},\n    {\"source\": \"A\", \"target\": \"C\", \"route_id\": \"RE 02\", \"dep\": \"06:15\", \"arr\": \"06:35\", \"type\": \"regional\"},\n    {\"source\": \"B\", \"target\": \"D\", \"route_id\": \"RE 01\", \"dep\": \"06:22\", \"arr\": \"06:45\", \"type\": \"regional\"},\n    {\"source\": \"C\", \"target\": \"G\", \"route_id\": \"RE 02\", \"dep\": \"06:40\", \"arr\": \"07:05\", \"type\": \"regional\"},\n    {\"source\": \"D\", \"target\": \"H\", \"route_id\": \"RE 01\", \"dep\": \"06:50\", \"arr\": \"07:15\", \"type\": \"regional\"},\n    {\"source\": \"G\", \"target\": \"J\", \"route_id\": \"RE 02\", \"dep\": \"07:10\", \"arr\": \"07:40\", \"type\": \"regional\"},\n    {\"source\": \"B\", \"target\": \"E\", \"route_id\": \"S 10\", \"dep\": \"07:00\", \"arr\": \"07:12\", \"type\": \"local\"},\n    {\"source\": \"E\", \"target\": \"F\", \"route_id\": \"S 10\", \"dep\": \"07:15\", \"arr\": \"07:25\", \"type\": \"local\"},\n    {\"source\": \"F\", \"target\": \"C\", \"route_id\": \"S 10\", \"dep\": \"07:28\", \"arr\": \"07:42\", \"type\": \"local\"},\n    {\"source\": \"D\", \"target\": \"E\", \"route_id\": \"S 20\", \"dep\": \"07:30\", \"arr\": \"07:48\", \"type\": \"local\"},\n    {\"source\": \"E\", \"target\": \"I\", \"route_id\": \"S 20\", \"dep\": \"07:52\", \"arr\": \"08:10\", \"type\": \"local\"},\n    {\"source\": \"F\", \"target\": \"I\", \"route_id\": \"S 10\", \"dep\": \"08:00\", \"arr\": \"08:18\", \"type\": \"local\"},\n    {\"source\": \"A\", \"target\": \"G\", \"route_id\": \"EX 99\", \"dep\": \"08:00\", \"arr\": \"08:35\", \"type\": \"express\"},\n    {\"source\": \"H\", \"target\": \"I\", \"route_id\": \"S 30\", \"dep\": \"08:15\", \"arr\": \"08:40\", \"type\": \"local\"},\n    {\"source\": \"I\", \"target\": \"J\", \"route_id\": \"S 30\", \"dep\": \"08:45\", \"arr\": \"09:15\", \"type\": \"local\"},\n]\n\n# Create lookup for station positions\nstation_pos = {s[\"id\"]: (s[\"x\"], s[\"y\"]) for s in stations}\n\n# Color mapping for route types using Okabe-Ito palette\nroute_colors = {\n    \"regional\": IMPRINT[0],  # #009E73 - brand green\n    \"local\": IMPRINT[1],  # #C475FD - vermillion\n    \"express\": IMPRINT[2],  # #4467A3 - blue\n}\n\n# Create figure\nfig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Draw edges (routes) first so nodes appear on top\nedge_counter = {}  # Track edges between same station pairs for offset\n\nfor route in routes:\n    src = route[\"source\"]\n    tgt = route[\"target\"]\n    x1, y1 = station_pos[src]\n    x2, y2 = station_pos[tgt]\n\n    # Track edge count for parallel routes\n    edge_key = tuple(sorted([src, tgt]))\n    edge_counter[edge_key] = edge_counter.get(edge_key, 0) + 1\n    offset = (edge_counter[edge_key] - 1) * 0.025\n\n    # Calculate perpendicular offset for parallel routes\n    dx = x2 - x1\n    dy = y2 - y1\n    length = np.sqrt(dx**2 + dy**2)\n    if length > 0:\n        perp_x = -dy / length * offset\n        perp_y = dx / length * offset\n    else:\n        perp_x, perp_y = 0, 0\n\n    # Apply offset\n    x1_off, y1_off = x1 + perp_x, y1 + perp_y\n    x2_off, y2_off = x2 + perp_x, y2 + perp_y\n\n    color = route_colors[route[\"type\"]]\n\n    # Draw arrow\n    ax.annotate(\n        \"\",\n        xy=(x2_off, y2_off),\n        xytext=(x1_off, y1_off),\n        arrowprops={\"arrowstyle\": \"-|>\", \"color\": color, \"lw\": 2.5, \"shrinkA\": 25, \"shrinkB\": 25, \"mutation_scale\": 20},\n    )\n\n    # Edge label position (middle of edge)\n    mid_x = (x1_off + x2_off) / 2\n    mid_y = (y1_off + y2_off) / 2\n\n    # Create edge label\n    label_text = f\"{route['route_id']}\\n{route['dep']}→{route['arr']}\"\n\n    # Add label with background\n    ax.text(\n        mid_x,\n        mid_y,\n        label_text,\n        fontsize=11,\n        ha=\"center\",\n        va=\"center\",\n        bbox={\n            \"boxstyle\": \"round,pad=0.3\",\n            \"facecolor\": ELEVATED_BG,\n            \"edgecolor\": color,\n            \"alpha\": 0.92,\n            \"linewidth\": 1.5,\n        },\n        color=INK,\n        zorder=5,\n    )\n\n# Draw station nodes\nstation_x = [s[\"x\"] for s in stations]\nstation_y = [s[\"y\"] for s in stations]\n\n# Use seaborn scatterplot for nodes with Okabe-Ito brand green\nsns.scatterplot(\n    x=station_x,\n    y=station_y,\n    s=1600,\n    color=IMPRINT[0],\n    edgecolor=INK_SOFT,\n    linewidth=2.5,\n    ax=ax,\n    zorder=10,\n    legend=False,\n)\n\n# Add station labels and IDs\nfor s in stations:\n    # Station label above node\n    ax.text(\n        s[\"x\"],\n        s[\"y\"] + 0.065,\n        s[\"label\"],\n        fontsize=13,\n        fontweight=\"bold\",\n        ha=\"center\",\n        va=\"bottom\",\n        color=INK,\n        bbox={\n            \"boxstyle\": \"round,pad=0.35\",\n            \"facecolor\": ELEVATED_BG,\n            \"edgecolor\": INK_SOFT,\n            \"alpha\": 0.94,\n            \"linewidth\": 1,\n        },\n        zorder=11,\n    )\n    # Station ID inside node\n    ax.text(s[\"x\"], s[\"y\"], s[\"id\"], fontsize=16, fontweight=\"bold\", color=PAGE_BG, ha=\"center\", va=\"center\", zorder=11)\n\n# Create legend\nlegend_handles = [\n    mpatches.Patch(color=IMPRINT[0], label=\"Regional Express (RE)\"),\n    mpatches.Patch(color=IMPRINT[1], label=\"Local Service (S)\"),\n    mpatches.Patch(color=IMPRINT[2], label=\"Express (EX)\"),\n]\nax.legend(handles=legend_handles, loc=\"lower right\", fontsize=15, framealpha=0.92, edgecolor=INK_SOFT)\n\n# Styling\nax.set_xlim(-0.05, 1.05)\nax.set_ylim(0.0, 1.05)\nax.set_aspect(\"equal\")\nax.axis(\"off\")\n\n# Title\nax.set_title(\n    \"network-transport-static · python · seaborn · anyplot.ai\", fontsize=24, fontweight=\"medium\", color=INK, pad=20\n)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}