A cross-sectional line plot showing ground elevation along a path or transect line, with the area below the profile filled to create a terrain silhouette. Distance along the transect is plotted on the x-axis and elevation on the y-axis. This visualization is widely used in hiking and cycling route planning, civil engineering corridor design, and geomorphology to convey terrain shape and difficulty at a glance.

""" anyplot.ai
area-elevation-profile: Terrain Elevation Profile Along Transect
Library: seaborn 0.13.2 | Python 3.13.13
Quality: 91/100 | Updated: 2026-06-10
"""
import os
import matplotlib.gridspec as gridspec
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from matplotlib.colors import LinearSegmentedColormap
# Theme tokens
THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F"
# Imprint palette — first categorical series always #009E73
IMPRINT_PALETTE = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD", "#954477", "#99B314"]
# Imprint sequential cmap for continuous terrain fill and slope-coded markers
imprint_seq = LinearSegmentedColormap.from_list("imprint_seq", ["#009E73", "#4467A3"])
sns.set_theme(
style="ticks",
rc={
"figure.facecolor": PAGE_BG,
"axes.facecolor": PAGE_BG,
"axes.edgecolor": INK_SOFT,
"axes.labelcolor": INK,
"text.color": INK,
"xtick.color": INK_SOFT,
"ytick.color": INK_SOFT,
"grid.color": INK,
"grid.alpha": 0.15,
"grid.linestyle": "-",
"grid.linewidth": 0.5,
"legend.facecolor": ELEVATED_BG,
"legend.edgecolor": INK_SOFT,
},
)
# Data
np.random.seed(42)
total_distance = 120
n_points = 480
distance = np.linspace(0, total_distance, n_points)
base_profile = (
800
+ 600 * np.sin(distance / total_distance * np.pi * 0.8)
+ 400 * np.sin(distance / total_distance * np.pi * 2.5 + 0.5)
+ 200 * np.sin(distance / total_distance * np.pi * 5 + 1.2)
+ 150 * np.cos(distance / total_distance * np.pi * 3.8)
)
noise = np.cumsum(np.random.normal(0, 2, n_points))
noise -= np.linspace(noise[0], noise[-1], n_points)
elevation = base_profile + noise
elevation = np.clip(elevation, 400, None)
df = pd.DataFrame({"distance_km": distance, "elevation_m": elevation})
landmarks = pd.DataFrame(
{
"name": ["Trailhead", "North Peak", "Lake Valley", "Ridge Pass", "River Crossing", "Summit", "End Station"],
"distance_km": [0.0, 8.0, 28.0, 52.0, 75.0, 105.0, 120.0],
}
)
landmark_elevations = []
for d in landmarks["distance_km"]:
idx = np.argmin(np.abs(distance - d))
landmark_elevations.append(elevation[idx])
landmarks["elevation_m"] = landmark_elevations
slopes = np.gradient(elevation, distance)
landmark_slopes = []
for d in landmarks["distance_km"]:
idx = np.argmin(np.abs(distance - d))
landmark_slopes.append(abs(slopes[idx]))
landmarks["slope"] = landmark_slopes
slope_max = max(landmark_slopes)
# Figure layout: main profile + marginal elevation KDE strip
fig = plt.figure(figsize=(8, 4.5), dpi=400)
gs = gridspec.GridSpec(1, 2, width_ratios=[20, 1], wspace=0.02)
ax = fig.add_subplot(gs[0])
ax_kde = fig.add_subplot(gs[1], sharey=ax)
# Gradient terrain fill — Imprint sequential cmap (green→blue, low→high elevation)
elev_min_val, elev_max_val = elevation.min(), elevation.max()
elev_range = elev_max_val - elev_min_val
y_min = elev_min_val - 0.05 * elev_range
n_bands = 40
for i in range(n_bands):
band_low = elev_min_val + i / n_bands * elev_range
band_high = elev_min_val + (i + 1) / n_bands * elev_range
clipped = np.clip(elevation, band_low, band_high)
ax.fill_between(
distance, np.full_like(distance, band_low), clipped, color=imprint_seq(i / n_bands), alpha=0.8, linewidth=0
)
ax.fill_between(distance, y_min, np.full_like(distance, elev_min_val), color=imprint_seq(0.0), alpha=0.8, linewidth=0)
# Profile line via seaborn lineplot
sns.lineplot(data=df, x="distance_km", y="elevation_m", ax=ax, color=IMPRINT_PALETTE[2], linewidth=2.0, legend=False)
# Landmark markers — slope intensity encoded via Imprint sequential cmap
for _, lm in landmarks.iterrows():
ax.scatter(
lm["distance_km"],
lm["elevation_m"],
color=imprint_seq(lm["slope"] / slope_max),
s=90,
edgecolor=PAGE_BG,
linewidth=1.0,
zorder=5,
)
# Landmark annotations with smart positioning
for _, lm in landmarks.iterrows():
ax.vlines(lm["distance_km"], y_min, lm["elevation_m"], color=INK_SOFT, linewidth=0.5, linestyle=":", alpha=0.5)
label_text = f"{lm['name']}\n{int(lm['elevation_m'])} m"
y_offset = 35 if lm["elevation_m"] < (elev_max_val - 200) else -50
x_offset = -28 if lm["distance_km"] >= total_distance - 1 else (28 if lm["distance_km"] <= 1 else 0)
ha = "right" if lm["distance_km"] >= total_distance - 1 else ("left" if lm["distance_km"] <= 1 else "center")
ax.annotate(
label_text,
xy=(lm["distance_km"], lm["elevation_m"]),
xytext=(x_offset, y_offset),
textcoords="offset points",
fontsize=7,
fontweight="bold",
color=INK,
ha=ha,
va="bottom" if y_offset > 0 else "top",
bbox={"boxstyle": "round,pad=0.25", "facecolor": ELEVATED_BG, "edgecolor": INK_SOFT, "alpha": 0.9},
zorder=6,
)
# Marginal KDE strip — distinctive seaborn feature showing elevation distribution
sns.kdeplot(y=df["elevation_m"], ax=ax_kde, fill=True, color=IMPRINT_PALETTE[2], alpha=0.3, linewidth=1.2)
sns.rugplot(data=landmarks, y="elevation_m", ax=ax_kde, color=IMPRINT_PALETTE[2], height=0.3, linewidth=1.5, alpha=0.7)
ax_kde.set_xlabel("")
ax_kde.set_ylabel("")
ax_kde.tick_params(left=False, labelleft=False, bottom=False, labelbottom=False)
ax_kde.set_facecolor(PAGE_BG)
sns.despine(ax=ax_kde, left=True, bottom=True)
# Style main axes
title = "area-elevation-profile · python · seaborn · anyplot.ai"
ax.set_title(title, fontsize=12, fontweight="medium", color=INK, pad=8)
ax.set_xlabel("Distance (km)", fontsize=10, color=INK)
ax.set_ylabel("Elevation (m)", fontsize=10, color=INK)
ax.tick_params(axis="both", labelsize=8, colors=INK_SOFT)
ax.yaxis.set_major_locator(plt.MultipleLocator(200))
ax.xaxis.set_major_locator(plt.MultipleLocator(20))
sns.despine(ax=ax)
ax.yaxis.grid(True, alpha=0.15, linewidth=0.5)
ax.set_xlim(0, total_distance)
ax.set_ylim(bottom=y_min)
ax.text(
0.98,
0.02,
"Vertical exaggeration ~10×",
transform=ax.transAxes,
fontsize=7,
color=INK_MUTED,
ha="right",
va="bottom",
style="italic",
)
fig.subplots_adjust(left=0.07, right=0.97, top=0.92, bottom=0.10)
plt.savefig(f"plot-{THEME}.png", dpi=400, facecolor=PAGE_BG)
Part of Terrain Elevation Profile Along Transect on anyplot.ai.