{"spec_id":"map-route-path","library":"makie","language":"julia","code":"# anyplot.ai\n# map-route-path: Route Path Map\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 70/100 | Created: 2026-09-02\n\nusing CairoMakie\nusing Colors\nusing Random\n\nRandom.seed!(42)\n\n# --- Theme tokens ------------------------------------------------------------\nconst THEME     = get(ENV, \"ANYPLOT_THEME\", \"light\")\nconst PAGE_BG   = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\nconst INK       = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nconst INK_SOFT  = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\nconst TERRAIN_LINE = THEME == \"light\" ?\n    RGBAf(26f0 / 255f0, 26f0 / 255f0, 23f0 / 255f0, 0.12f0) :\n    RGBAf(240f0 / 255f0, 239f0 / 255f0, 232f0 / 255f0, 0.16f0)\n\n# Sequential colormap (Imprint): brand green -> blue, single-polarity elevation\nconst ANYPLOT_SEQ = cgrad([colorant\"#009E73\", colorant\"#4467A3\"])\nconst START_COLOR = colorant\"#009E73\"  # brand green, first categorical series\nconst END_COLOR   = colorant\"#AE3030\"  # matte red, semantic anchor for the endpoint\n\n# --- Data: a mountain loop trail GPS track -----------------------------------\n# The trail traces most of a loop around a peak but does not fully close,\n# so the trailhead (start) and the exit point (end) sit apart on the map.\nconst N_POINTS    = 260\nconst LAT_C, LON_C = 39.9800, -105.6800   # loop centroid (peak the trail circles)\nconst R_DEG        = 0.0075               # loop radius in degrees (~0.8 km)\n\nn_harmonics = 4\nharmonic_amp   = [0.28, 0.14, 0.08, 0.05]\nharmonic_phase = 2π .* rand(n_harmonics)\n\nclosure_gap = 0.22                        # fraction of the loop left open\ntheta = range(0, 2π * (1 - closure_gap); length = N_POINTS)\nradius = [\n    1.0 + sum(harmonic_amp[k] * sin(k * t + harmonic_phase[k]) for k in 1:n_harmonics)\n    for t in theta\n]\n\nlat_path = LAT_C .+ R_DEG .* radius .* sin.(theta)\nlon_path = LON_C .+ R_DEG .* radius .* cos.(theta)\n\n# GPS measurement noise, then smoothed like a real recorded track\nnoise_deg = 0.00025\nlat_noisy = lat_path .+ noise_deg .* (rand(N_POINTS) .- 0.5)\nlon_noisy = lon_path .+ noise_deg .* (rand(N_POINTS) .- 0.5)\n\nfunction smooth_track(v, half_window)\n    n = length(v)\n    return [\n        sum(v[max(1, i - half_window):min(n, i + half_window)]) /\n        length(max(1, i - half_window):min(n, i + half_window))\n        for i in 1:n\n    ]\nend\n\nlat_smooth = smooth_track(lat_noisy, 3)\nlon_smooth = smooth_track(lon_noisy, 3)\n\n# Elevation field: a single peak at the loop centroid, giving the trail a\n# realistic elevation profile (rises on the outbound leg, falls on return).\nelevation_field(lon, lat) = 2150.0 + 780.0 * exp(\n    -((lon - LON_C)^2 + (lat - LAT_C)^2) / (2 * 0.0045^2)\n)\nelev_m    = elevation_field.(lon_smooth, lat_smooth) .+ 15.0 .* (rand(N_POINTS) .- 0.5)\nelev_norm = (elev_m .- minimum(elev_m)) ./ (maximum(elev_m) - minimum(elev_m))\nelev_gain = sum(max.(diff(elev_m), 0.0))   # total climb along the recorded track\npeak_elev = elevation_field(LON_C, LAT_C)  # elevation at the loop's centroid peak\n\n# Terrain basemap: the elevation field is a single radially-symmetric peak,\n# so its contour lines are exact circles around the centroid — drawn\n# analytically instead of through a numeric contour grid.\npad = 0.006\nlon_min, lon_max = minimum(lon_smooth) - pad, maximum(lon_smooth) + pad\nlat_min, lat_max = minimum(lat_smooth) - pad, maximum(lat_smooth) + pad\nring_phi = range(0, 2π; length = 100)\nring_radii = R_DEG .* [0.3, 0.55, 0.8, 1.05, 1.3]\nlabeled_ring_idx = [2, 4]   # label a couple of rings with their elevation value\n\n# Direction arrows: a handful of rotated triangles along the smoothed track\narrow_idx = 20:40:(N_POINTS-10)\narrow_lon = [lon_smooth[i] for i in arrow_idx]\narrow_lat = [lat_smooth[i] for i in arrow_idx]\narrow_rot = [\n    atan(lat_smooth[i+5] - lat_smooth[i], lon_smooth[i+5] - lon_smooth[i]) - π / 2\n    for i in arrow_idx\n]\n\n# --- Plot ---------------------------------------------------------------------\nconst title_str = \"Alpine Loop Trail · map-route-path · julia · makie · anyplot.ai\"\nconst title_sz   = round(Int, 25 * min(1.0, 67 / length(title_str)))\n\nfig = Figure(\n    size            = (1600, 900),\n    fontsize        = 12,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title             = title_str,\n    titlesize         = title_sz,\n    titlecolor        = INK,\n    xlabel            = \"Longitude\",\n    ylabel            = \"Latitude\",\n    xlabelsize        = 14,\n    ylabelsize        = 14,\n    xlabelcolor       = INK,\n    ylabelcolor       = INK,\n    xticklabelsize    = 11,\n    yticklabelsize    = 11,\n    xticklabelcolor   = INK_SOFT,\n    yticklabelcolor   = INK_SOFT,\n    xtickcolor        = INK_SOFT,\n    ytickcolor        = INK_SOFT,\n    backgroundcolor   = PAGE_BG,\n    topspinevisible   = false,\n    rightspinevisible = false,\n    leftspinecolor    = INK_SOFT,\n    bottomspinecolor  = INK_SOFT,\n    xgridvisible      = false,\n    ygridvisible      = false,\n    limits            = (lon_min, lon_max, lat_min, lat_max),\n)\n\n# Basemap: concentric elevation rings around the peak the trail circles,\n# with a couple of rings labeled by contour value so they read as terrain\n# context rather than decoration.\nfor (i, r) in enumerate(ring_radii)\n    lines!(ax, LON_C .+ r .* cos.(ring_phi), LAT_C .+ r .* sin.(ring_phi);\n        color = TERRAIN_LINE, linewidth = 0.8)\n    if i in labeled_ring_idx\n        ring_elev = elevation_field(LON_C, LAT_C + r)\n        text!(ax, LON_C, LAT_C + r;\n            text      = \"$(round(Int, ring_elev)) m\",\n            fontsize  = 9,\n            color     = INK_SOFT,\n            align     = (:center, :bottom),\n            offset    = (0, 2),\n            space     = :data,\n        )\n    end\nend\n\n# Summit marker: the peak the contour rings and elevation gradient center on\nscatter!(ax, [LON_C], [LAT_C];\n    marker      = :diamond,\n    markersize  = 12,\n    color       = INK_SOFT,\n    strokewidth = 0,\n)\ntext!(ax, LON_C, LAT_C;\n    text      = \"Peak · $(round(Int, peak_elev)) m\",\n    fontsize  = 11,\n    color     = INK_SOFT,\n    align     = (:center, :top),\n    offset    = (0, -8),\n    space     = :data,\n)\n\n# Trail stat callout: total elevation gain along the recorded track\ntext!(ax, lon_min + 0.04 * (lon_max - lon_min), lat_max - 0.04 * (lat_max - lat_min);\n    text      = \"Elevation gain: +$(round(Int, elev_gain)) m\",\n    fontsize  = 12,\n    color     = INK_SOFT,\n    align     = (:left, :top),\n    space     = :data,\n)\n\n# Route path, colored by elevation (Imprint sequential: green -> blue)\nlines!(ax, lon_smooth, lat_smooth;\n    color     = elev_norm,\n    colormap  = ANYPLOT_SEQ,\n    linewidth = 4.0,\n)\n\n# Direction-of-travel markers\nscatter!(ax, arrow_lon, arrow_lat;\n    marker      = :utriangle,\n    markersize  = 16,\n    rotation    = arrow_rot,\n    color       = INK_SOFT,\n    strokewidth = 0,\n)\n\n# Start marker (trailhead)\nscatter!(ax, [lon_smooth[1]], [lat_smooth[1]];\n    marker      = :circle,\n    markersize  = 24,\n    color       = START_COLOR,\n    strokewidth = 2,\n    strokecolor = PAGE_BG,\n)\n\n# End marker (exit point)\nscatter!(ax, [lon_smooth[end]], [lat_smooth[end]];\n    marker      = :rect,\n    markersize  = 20,\n    color       = END_COLOR,\n    strokewidth = 2,\n    strokecolor = PAGE_BG,\n)\n\nColorbar(fig[1, 2];\n    colormap       = ANYPLOT_SEQ,\n    limits         = (minimum(elev_m), maximum(elev_m)),\n    label          = \"Elevation (m)\",\n    labelsize      = 13,\n    labelcolor     = INK,\n    ticklabelsize  = 10,\n    ticklabelcolor = INK_SOFT,\n    tickcolor      = INK_SOFT,\n    width          = 18,\n    tellheight     = false,\n)\n\ncolsize!(fig.layout, 2, Fixed(90))\n\n# --- Save ----------------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}