{"spec_id":"area-elevation-profile","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\narea-elevation-profile: Terrain Elevation Profile Along Transect\nLibrary: plotnine 0.15.5 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-06-10\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    coord_cartesian,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_line,\n    geom_point,\n    geom_ribbon,\n    geom_segment,\n    geom_text,\n    ggplot,\n    labs,\n    scale_x_continuous,\n    scale_y_continuous,\n    theme,\n    theme_minimal,\n)\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\"\nBRAND = \"#009E73\"  # Imprint palette position 1 — terrain fill\nSUMMIT_COLOR = \"#AE3030\"  # Imprint palette position 5 — high-peak emphasis\n\n# Data — Alpine hiking trail elevation profile (~120 km), fictional location names\nnp.random.seed(42)\nn_points = 480\ndistance = np.linspace(0, 120, n_points)\n\n# Terrain with controlled Gaussian peaks at landmark locations\nelevation = np.full(n_points, 800.0)\nelevation += 500 * np.exp(-((distance - 18) ** 2) / 50)\nelevation += 900 * np.exp(-((distance - 35) ** 2) / 80)\nelevation += 650 * np.exp(-((distance - 62) ** 2) / 60)\nelevation += 750 * np.exp(-((distance - 78) ** 2) / 70)\nelevation += 400 * np.exp(-((distance - 98) ** 2) / 90)\nelevation += 200 * np.sin(distance * np.pi / 20 + 0.8)\nelevation += 100 * np.sin(distance * np.pi / 10 + 1.5)\nelevation += np.random.normal(0, 15, n_points)\nelevation = pd.Series(elevation).rolling(window=8, center=True, min_periods=1).mean().values.copy()\nelevation[:12] = np.linspace(580, elevation[12], 12)\nelevation[-12:] = np.linspace(elevation[-12], 620, 12)\n\ny_min = 450\ny_max = int(np.ceil(elevation.max() / 100) * 100) + 280\nshadow_top = y_min + 280  # narrow base shadow band for elevation-depth effect\n\ndf = pd.DataFrame(\n    {\"distance\": distance, \"elevation\": elevation, \"y_min\": y_min, \"shadow_level\": np.minimum(elevation, shadow_top)}\n)\n\n# Landmarks with fictional Alpine names to avoid real-world elevation mismatches\nlandmarks = pd.DataFrame(\n    {\n        \"name\": [\n            \"Hochfeld\",\n            \"Steinpass\",\n            \"Gletscherhorn\",\n            \"Talboden\",\n            \"Felsalp\",\n            \"Windspitze\",\n            \"Moosbach\",\n            \"Niederdorf\",\n        ],\n        \"distance\": [0, 18, 35, 50, 62, 78, 98, 120],\n    }\n)\nlandmarks[\"elevation\"] = landmarks[\"distance\"].apply(lambda d: elevation[np.argmin(np.abs(distance - d))])\nlandmarks[\"label_y\"] = landmarks[\"elevation\"] + 180\nlandmarks[\"label\"] = landmarks.apply(lambda r: f\"{r['name']}\\n{int(r['elevation']):,} m\", axis=1)\nlandmarks[\"ha\"] = \"center\"\nlandmarks.loc[landmarks[\"distance\"] < 5, \"ha\"] = \"left\"\nlandmarks.loc[landmarks[\"distance\"] > 115, \"ha\"] = \"right\"\nlandmarks[\"seg_top\"] = landmarks[\"elevation\"] + 140\n\n# Mark top-2 highest landmarks as summits for distinct visual emphasis\nsummit_threshold = landmarks[\"elevation\"].nlargest(2).min()\nlandmarks[\"is_summit\"] = landmarks[\"elevation\"] >= summit_threshold\nsummits = landmarks[landmarks[\"is_summit\"]]\nvalleys = landmarks[~landmarks[\"is_summit\"]]\n\n# Title length-scaled fontsize (67-char baseline = 12pt)\ntitle = \"area-elevation-profile · python · plotnine · anyplot.ai\"\nn = len(title)\nratio = 67 / n if n > 67 else 1.0\ntitle_fontsize = max(8, round(12 * ratio))\n\n# Plot\nplot = (\n    ggplot(df, aes(x=\"distance\", y=\"elevation\"))\n    # Two-layer terrain: full fill + base shadow band for elevation depth\n    + geom_ribbon(aes(ymin=\"y_min\", ymax=\"elevation\"), fill=BRAND, alpha=0.55)\n    + geom_ribbon(aes(ymin=\"y_min\", ymax=\"shadow_level\"), fill=BRAND, alpha=0.30)\n    # Profile line with PAGE_BG halo for visual separation from terrain fill\n    + geom_line(color=PAGE_BG, size=2.5, alpha=0.6)\n    + geom_line(color=INK, size=1.2)\n    # Vertical marker lines from landmark point up to label\n    + geom_segment(\n        aes(x=\"distance\", xend=\"distance\", y=\"elevation\", yend=\"seg_top\"),\n        data=landmarks,\n        color=INK_SOFT,\n        linetype=\"dotted\",\n        size=0.5,\n    )\n    # Valley markers\n    + geom_point(aes(x=\"distance\", y=\"elevation\"), data=valleys, size=3.5, color=INK)\n    # Summit markers — larger and accent-colored for high-point drama\n    + geom_point(aes(x=\"distance\", y=\"elevation\"), data=summits, size=5.5, color=SUMMIT_COLOR)\n    # Left-aligned edge labels (nudged inward)\n    + geom_text(\n        aes(x=\"distance\", y=\"label_y\", label=\"label\"),\n        data=landmarks[landmarks[\"ha\"] == \"left\"],\n        size=3.5,\n        color=INK,\n        ha=\"left\",\n        va=\"bottom\",\n        fontweight=\"bold\",\n        nudge_x=3,\n    )\n    # Center-aligned labels\n    + geom_text(\n        aes(x=\"distance\", y=\"label_y\", label=\"label\"),\n        data=landmarks[landmarks[\"ha\"] == \"center\"],\n        size=3.5,\n        color=INK,\n        ha=\"center\",\n        va=\"bottom\",\n        fontweight=\"bold\",\n    )\n    # Right-aligned edge labels (nudged inward)\n    + geom_text(\n        aes(x=\"distance\", y=\"label_y\", label=\"label\"),\n        data=landmarks[landmarks[\"ha\"] == \"right\"],\n        size=3.5,\n        color=INK,\n        ha=\"right\",\n        va=\"bottom\",\n        fontweight=\"bold\",\n        nudge_x=-5,\n    )\n    + labs(\n        x=\"Distance (km)\",\n        y=\"Elevation (m)\",\n        title=title,\n        subtitle=\"Alpine Trail: Hochfeld to Niederdorf (120 km) · Vertical exaggeration ~10×\",\n    )\n    + scale_x_continuous(breaks=range(0, 130, 10), expand=(0.03, 2))\n    + scale_y_continuous(breaks=range(500, 2200, 250))\n    + coord_cartesian(ylim=(y_min, y_max))\n    + theme_minimal()\n    + theme(\n        figure_size=(8, 4.5),\n        text=element_text(size=7, color=INK),\n        axis_title=element_text(size=10, color=INK),\n        axis_text=element_text(size=8, color=INK_SOFT),\n        plot_title=element_text(size=title_fontsize, color=INK),\n        plot_subtitle=element_text(size=8, color=INK_MUTED, style=\"italic\"),\n        legend_text=element_text(size=8, color=INK_SOFT),\n        panel_background=element_rect(fill=PAGE_BG, color=\"none\"),\n        plot_background=element_rect(fill=PAGE_BG, color=\"none\"),\n        panel_grid_major_y=element_line(color=INK, size=0.3, alpha=0.15),\n        panel_grid_major_x=element_blank(),\n        panel_grid_minor=element_blank(),\n        panel_border=element_blank(),\n        axis_line_x=element_line(color=INK_SOFT, size=0.6),\n        axis_ticks_major_x=element_line(color=INK_SOFT, size=0.4),\n        axis_ticks_major_y=element_blank(),\n        plot_margin=0.04,\n    )\n)\n\n# Save\nplot.save(f\"plot-{THEME}.png\", dpi=400, width=8, height=4.5, units=\"in\", verbose=False)\n"}