{"spec_id":"area-elevation-profile","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\narea-elevation-profile: Terrain Elevation Profile Along Transect\nLibrary: bokeh 3.9.1 | Python 3.13.13\nQuality: 92/100 | Updated: 2026-06-10\n\"\"\"\n\nimport os\nimport sys\n\n\n# Prevent self-import: this file is named bokeh.py, which shadows the installed\n# bokeh package when its directory sits at the front of sys.path.\n_this_dir = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if os.path.abspath(p or \".\") != _this_dir]\n\nimport time\nfrom pathlib import Path\n\nimport numpy as np\nfrom bokeh.io import output_file, save\nfrom bokeh.models import ColumnDataSource, HoverTool, Label, Span\nfrom bokeh.plotting import figure\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n\n# Imprint sequential colormap: green (flat) → blue (steep)\n# #009E73 → #4467A3: r:0→68, g:158→103, b:115→163\nIMPRINT_SEQ256 = [\n    f\"#{int(round(68 * t / 255)):02X}{int(round(158 - 55 * t / 255)):02X}{int(round(115 + 48 * t / 255)):02X}\"\n    for t in range(256)\n]\n\n# Data — Alpine hiking trail (120 km) with realistic terrain\nnp.random.seed(42)\nn_points = 480\ndistance = np.linspace(0, 120, n_points)\n\nbase_elevation = 800\nelevation = np.full(n_points, base_elevation, dtype=float)\n\n# Broad terrain features\nelevation += 600 * np.sin(distance * np.pi / 40) ** 2\nelevation += 400 * np.sin(distance * np.pi / 25 + 1.2) ** 2\nelevation += 300 * np.sin(distance * np.pi / 60 + 0.5)\nelevation += 120 * np.sin(distance * np.pi / 8)\nelevation += 80 * np.sin(distance * np.pi / 3.5 + 2.0)\n\nnoise = np.convolve(np.random.randn(n_points + 20) * 30, np.ones(20) / 20, mode=\"valid\")[:n_points]\nelevation += noise\nelevation = np.maximum(elevation, 450)\n\nslope = np.gradient(elevation, distance)\nabs_slope = np.abs(slope)\n\n# Landmarks — fictional Alpine locations along the transect\nlandmarks = [\n    (0.0, \"Bergdorf\"),\n    (18.5, \"Hochalm\"),\n    (38.0, \"Westgrat\"),\n    (55.0, \"Talbach\"),\n    (72.0, \"Mittelalp\"),\n    (92.0, \"Gipfelhorn\"),\n    (120.0, \"Waldenfels\"),\n]\n\nlandmark_distances = [lm[0] for lm in landmarks]\nlandmark_elevations = [float(np.interp(lm[0], distance, elevation)) for lm in landmarks]\nlandmark_names = [lm[1] for lm in landmarks]\n\nelev_min = float(min(elevation))\nelev_max = float(max(elevation))\n\n# Slope-colored multi_line segments using Imprint sequential palette\nslope_95 = float(np.percentile(abs_slope, 95))\nseg_colors = [IMPRINT_SEQ256[int(np.clip(s / slope_95, 0, 1) * 255)] for s in abs_slope]\n\nsource = ColumnDataSource(\n    data={\n        \"distance\": distance,\n        \"elevation\": elevation,\n        \"slope\": abs_slope,\n        \"elev_fmt\": [f\"{e:.0f}\" for e in elevation],\n        \"slope_fmt\": [f\"{s:.1f}\" for s in abs_slope],\n    }\n)\n\nseg_xs = [[distance[i], distance[i + 1]] for i in range(n_points - 1)]\nseg_ys = [[elevation[i], elevation[i + 1]] for i in range(n_points - 1)]\nseg_source = ColumnDataSource(data={\"xs\": seg_xs, \"ys\": seg_ys, \"color\": seg_colors[:-1]})\n\n# Title — scaled fontsize: n=75 chars → round(50 * 67/75) = 45pt\ntitle_str = \"Alpine Trail Profile · area-elevation-profile · python · bokeh · anyplot.ai\"\ntitle_fontsize = f\"{max(34, round(50 * 67 / len(title_str)))}pt\"\n\n# Plot\ny_floor = max(0, elev_min - 100)\n\np = figure(\n    width=3200,\n    height=1800,\n    title=title_str,\n    x_axis_label=\"Distance (km)\",\n    y_axis_label=\"Elevation (m)\",\n    toolbar_location=None,\n    min_border_bottom=160,\n    min_border_left=180,\n    min_border_top=110,\n    min_border_right=300,  # extra room for right-edge landmark label\n)\n\n# Layered terrain fills using Imprint palette (green terrain + blue depth)\np.varea(x=\"distance\", y1=y_floor, y2=\"elevation\", source=source, fill_color=\"#009E73\", fill_alpha=0.18)\n\nmid_elev = (np.array(elevation) + y_floor) / 2\nmid_source = ColumnDataSource(data={\"distance\": distance, \"mid\": mid_elev})\np.varea(x=\"distance\", y1=y_floor, y2=\"mid\", source=mid_source, fill_color=\"#4467A3\", fill_alpha=0.12)\n\n# Slope-colored profile line (Imprint sequential: green=flat → blue=steep)\np.multi_line(xs=\"xs\", ys=\"ys\", source=seg_source, line_color=\"color\", line_width=5, line_alpha=0.95)\n\n# HoverTool for interactive HTML version\nhover = HoverTool(\n    tooltips=[(\"Distance\", \"@distance{0.1} km\"), (\"Elevation\", \"@elev_fmt m\"), (\"Slope\", \"@slope_fmt m/km\")],\n    mode=\"vline\",\n)\np.add_tools(hover)\n\n# Landmark vertical markers and labels — peak landmark uses ochre star for climax emphasis\npeak_lm_idx = int(np.argmax(landmark_elevations))\nfor i, (lm_dist, lm_elev, lm_name) in enumerate(\n    zip(landmark_distances, landmark_elevations, landmark_names, strict=True)\n):\n    vline = Span(\n        location=lm_dist, dimension=\"height\", line_color=INK_SOFT, line_width=2, line_alpha=0.3, line_dash=\"dashed\"\n    )\n    p.add_layout(vline)\n\n    label_text = f\"{lm_name}\\n{int(lm_elev)} m\"\n    align = \"center\"\n    x_off = 0\n    if i == 0:\n        align = \"left\"\n        x_off = 10\n    elif i == len(landmarks) - 1:\n        align = \"right\"\n        x_off = -50\n    label = Label(\n        x=lm_dist,\n        y=lm_elev,\n        text=label_text,\n        text_font_size=\"28pt\",\n        text_color=INK,\n        text_font_style=\"bold\",\n        text_align=align,\n        x_offset=x_off,\n        y_offset=45,\n    )\n    p.add_layout(label)\n\n    dot_color = \"#BD8233\" if i == peak_lm_idx else \"#4467A3\"\n    dot_marker = \"star\" if i == peak_lm_idx else \"circle\"\n    dot_size = 30 if i == peak_lm_idx else 22\n    p.scatter(\n        x=[lm_dist],\n        y=[lm_elev],\n        size=dot_size,\n        fill_color=dot_color,\n        line_color=PAGE_BG,\n        line_width=3,\n        marker=dot_marker,\n    )\n\n# Theme-adaptive chrome\np.background_fill_color = PAGE_BG\np.border_fill_color = PAGE_BG\np.outline_line_color = None\n\np.title.text_font_size = title_fontsize\np.title.text_color = INK\n\np.xaxis.axis_label_text_font_size = \"42pt\"\np.yaxis.axis_label_text_font_size = \"42pt\"\np.xaxis.major_label_text_font_size = \"34pt\"\np.yaxis.major_label_text_font_size = \"34pt\"\np.xaxis.axis_label_text_color = INK\np.yaxis.axis_label_text_color = INK\np.xaxis.major_label_text_color = INK_SOFT\np.yaxis.major_label_text_color = INK_SOFT\np.xaxis.axis_line_color = INK_SOFT\np.yaxis.axis_line_color = INK_SOFT\np.xaxis.major_tick_line_color = INK_SOFT\np.yaxis.major_tick_line_color = INK_SOFT\np.xaxis.axis_line_width = 2\np.yaxis.axis_line_width = 2\n\n# Grid — y-axis only, subtle\np.xgrid.grid_line_alpha = 0\np.ygrid.grid_line_alpha = 0.12\np.ygrid.grid_line_dash = [4, 4]\np.ygrid.grid_line_color = INK\n\np.y_range.start = y_floor\np.y_range.end = elev_max * 1.15\n\n# Annotation notes\np.add_layout(\n    Label(\n        x=5,\n        y=elev_max * 1.08,\n        text=\"Note: Vertical exaggeration ~10×\",\n        text_font_size=\"26pt\",\n        text_color=INK_MUTED,\n        text_font_style=\"italic\",\n    )\n)\np.add_layout(\n    Label(\n        x=5,\n        y=elev_max * 1.03,\n        text=\"Profile color: green (flat) → blue (steep)  [Imprint sequential]\",\n        text_font_size=\"22pt\",\n        text_color=INK_MUTED,\n        text_font_style=\"italic\",\n    )\n)\n\n# Save — HTML artifact + headless Chrome screenshot\noutput_file(f\"plot-{THEME}.html\")\nsave(p)\n\nW, H = 3200, 1800\nopts = Options()\nfor arg in (\n    \"--headless=new\",\n    \"--no-sandbox\",\n    \"--disable-dev-shm-usage\",\n    \"--disable-gpu\",\n    f\"--window-size={W},{H}\",\n    \"--hide-scrollbars\",\n):\n    opts.add_argument(arg)\ndriver = webdriver.Chrome(options=opts)\n# CDP override forces exact W×H viewport regardless of outer window chrome\ndriver.execute_cdp_cmd(\n    \"Emulation.setDeviceMetricsOverride\", {\"width\": W, \"height\": H, \"deviceScaleFactor\": 1, \"mobile\": False}\n)\ndriver.get(f\"file://{Path(f'plot-{THEME}.html').resolve()}\")\ntime.sleep(3)\ndriver.save_screenshot(f\"plot-{THEME}.png\")\ndriver.quit()\n"}