{"spec_id":"map-route-path","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nmap-route-path: Route Path Map\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 84/100 | Updated: 2026-05-21\n\"\"\"\n\nimport os\nimport sys\n\n\n# Remove this script's directory from sys.path so 'plotly' resolves to the\n# installed package, not this file (which shares the name).\n_here = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if os.path.abspath(p) != _here]\n\nimport numpy as np\nimport pandas as pd\nimport plotly.graph_objects as go\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\n\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: position 1 = start, position 2 = end, position 3 = waypoints\nSTART_COLOR = \"#009E73\"\nEND_COLOR = \"#C475FD\"\nWAYPOINT_COLOR = \"#4467A3\"\n\n# Data: Appalachian Trail section through Great Smoky Mountains, Tennessee\nnp.random.seed(42)\n\nstart_lat, start_lon = 35.6127, -83.4254  # Newfound Gap, TN\nn_points = 300\n\nt = np.linspace(0, 1, n_points)\n\n# Trail winds northeast with characteristic Smoky Mountain undulations\nlat_drift = 0.15 * t + 0.018 * np.sin(12 * np.pi * t) + 0.009 * np.sin(5 * np.pi * t)\nlon_drift = 0.20 * t + 0.022 * np.sin(8 * np.pi * t) + 0.012 * np.cos(4 * np.pi * t)\n\n# Reduced noise amplitude for a more realistic trail path (was 0.0007)\nlat = start_lat + lat_drift + np.cumsum(np.random.randn(n_points) * 0.0002)\nlon = start_lon + lon_drift + np.cumsum(np.random.randn(n_points) * 0.0002)\n\ntimestamps = pd.date_range(\"2024-09-14 07:30\", periods=n_points, freq=\"72s\")\nt_values = np.arange(n_points, dtype=float)\n\ndf = pd.DataFrame({\"lat\": lat, \"lon\": lon, \"sequence\": range(n_points), \"timestamp\": timestamps})\n\nfig = go.Figure()\n\n# Thin route line for path connectivity\nfig.add_trace(\n    go.Scattermap(\n        lat=df[\"lat\"],\n        lon=df[\"lon\"],\n        mode=\"lines\",\n        line={\"width\": 2, \"color\": INK_SOFT},\n        opacity=0.5,\n        hoverinfo=\"skip\",\n        showlegend=False,\n    )\n)\n\n# Single marker trace with viridis colorscale for time progression; colorbar provides the time scale\nhover_times = [ts.strftime(\"%H:%M\") for ts in timestamps]\nfig.add_trace(\n    go.Scattermap(\n        lat=df[\"lat\"],\n        lon=df[\"lon\"],\n        mode=\"markers\",\n        marker={\n            \"size\": 6,\n            \"color\": t_values,\n            \"colorscale\": \"Viridis\",\n            \"showscale\": True,\n            \"colorbar\": {\n                \"title\": {\"text\": \"Time\", \"font\": {\"size\": 11, \"color\": INK}},\n                \"tickvals\": [0, 75, 150, 225, 299],\n                \"ticktext\": [\"07:30\", \"09:00\", \"10:30\", \"12:00\", \"13:30\"],\n                \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n                \"bgcolor\": ELEVATED_BG,\n                \"bordercolor\": INK_SOFT,\n                \"borderwidth\": 1,\n                \"len\": 0.5,\n                \"thickness\": 15,\n            },\n        },\n        hovertemplate=\"<b>%{text}</b><br>Lat: %{lat:.4f}<br>Lon: %{lon:.4f}<extra></extra>\",\n        text=hover_times,\n        showlegend=False,\n    )\n)\n\n# Start marker — Okabe-Ito position 1 (bluish green)\nfig.add_trace(\n    go.Scattermap(\n        lat=[df[\"lat\"].iloc[0]],\n        lon=[df[\"lon\"].iloc[0]],\n        mode=\"markers+text\",\n        marker={\"size\": 18, \"color\": START_COLOR},\n        text=[\"Start\"],\n        textposition=\"top center\",\n        textfont={\"size\": 13, \"color\": START_COLOR},\n        name=\"Start (Newfound Gap)\",\n        hovertemplate=\"<b>Start — Newfound Gap</b><br>Lat: %{lat:.4f}<br>Lon: %{lon:.4f}<br>Time: 07:30<extra></extra>\",\n    )\n)\n\n# End marker — Okabe-Ito position 2 (vermillion)\nfig.add_trace(\n    go.Scattermap(\n        lat=[df[\"lat\"].iloc[-1]],\n        lon=[df[\"lon\"].iloc[-1]],\n        mode=\"markers+text\",\n        marker={\"size\": 18, \"color\": END_COLOR},\n        text=[\"End\"],\n        textposition=\"top center\",\n        textfont={\"size\": 13, \"color\": END_COLOR},\n        name=\"End (Mt. Kephart area)\",\n        hovertemplate=\"<b>End — Mt. Kephart area</b><br>Lat: %{lat:.4f}<br>Lon: %{lon:.4f}<br>Time: 13:30<extra></extra>\",\n    )\n)\n\n# Waypoints at regular intervals — Okabe-Ito position 3 (blue)\ninterval = 75\nwaypoints = df.iloc[interval::interval]\nwp_labels = [\n    f\"{int(row.timestamp.strftime('%H')) % 12 or 12}:{row.timestamp.strftime('%M')}\" for _, row in waypoints.iterrows()\n]\nfig.add_trace(\n    go.Scattermap(\n        lat=waypoints[\"lat\"],\n        lon=waypoints[\"lon\"],\n        mode=\"markers\",\n        marker={\"size\": 10, \"color\": WAYPOINT_COLOR, \"opacity\": 0.9},\n        name=\"Checkpoints\",\n        hovertemplate=\"<b>Checkpoint at %{text}</b><br>Lat: %{lat:.4f}<br>Lon: %{lon:.4f}<extra></extra>\",\n        text=wp_labels,\n    )\n)\n\ncenter_lat = df[\"lat\"].mean()\ncenter_lon = df[\"lon\"].mean()\n\n# Use dark tiles for dark theme to maintain visual coherence with dark chrome\nmap_style = \"carto-darkmatter\" if THEME == \"dark\" else \"open-street-map\"\n\nfig.update_layout(\n    autosize=False,\n    title={\n        \"text\": \"map-route-path · python · plotly · anyplot.ai\",\n        \"font\": {\"size\": 16, \"color\": INK},\n        \"x\": 0.5,\n        \"xanchor\": \"center\",\n    },\n    map={\"style\": map_style, \"center\": {\"lat\": center_lat, \"lon\": center_lon}, \"zoom\": 10},\n    paper_bgcolor=PAGE_BG,\n    font={\"color\": INK},\n    margin={\"l\": 20, \"r\": 90, \"t\": 60, \"b\": 20},\n    legend={\n        \"x\": 0.01,\n        \"y\": 0.99,\n        \"xanchor\": \"left\",\n        \"yanchor\": \"top\",\n        \"bgcolor\": ELEVATED_BG,\n        \"bordercolor\": INK_SOFT,\n        \"borderwidth\": 1,\n        \"font\": {\"size\": 12, \"color\": INK_SOFT},\n    },\n)\n\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\", full_html=True)\n"}