Several marked routes running down one path drew as a single line, only
the topmost color visible. maplibre-gl-lanes puts each route in its own
lane; OSM relations already share exact vertices, which is all it needs.
One LaneLayer across every picked country; click hit-testing and the
highlight move to queryLane/setHighlight.
A map-anchored Popup on a trail click routinely overlapped the very
line it described. Follows the same click -> docked panel pattern
gpx.js already uses for a track line click (TrackProfilePanel) rather
than inventing a new one: a fixed #trail-panel docks to the right edge
on desktop and the bottom edge on narrow viewports (this file's first
@media breakpoint), so the trail stays fully visible either way.
routeHtml()'s own markup is reused unchanged for the panel body —
.trail-panel__body's padding matches .maplibregl-popup-content's own
(15px 10px) so the header's canceling negative margin still lines up.
The popup-identity staleness guard for the async Overpass lookup is
replaced with a plain request token, since there's no longer a fresh
Popup instance to key off of.
The real app had no visual feedback for which line a click actually
matched, unlike the earlier SVG prototype's hover highlight — just a
popup. Draws the matched route's real geometry (from the offline
vector feature, or reassembled from the online Overpass response's own
member-way geometry) as a separate yellow highlight layer, cleared
when the popup closes.
Guarded against a stale popup's closeOnClick "close" event clearing a
highlight that a newer click already set in the same event dispatch —
tracks which popup is currently "active" and only clears on its own
close, not a predecessor's delayed one.
data/hiking_trails.geojson is now data/hiking_trails/<CODE>.geojson —
one file per country instead of a merge, generated per-source
(PmtilesBuilder.extract_hiking_trails), cached the same way
geofabrik_cache is (skipped once a country's own extract is already
newer than its source .osm.pbf, so growing the selection by one
country only re-extracts that one). GET /api/hiking-trails now lists
available country codes instead of a single file's status.
The Overlays panel's "Hiking trails" item becomes an expandable
per-country checklist once any offline extract exists (same shape as
Radar's own expand), replacing the plain on/off checkbox — nobody
covering a dozen countries' worth of offline map data needs all of
their hiking trails loaded and parsed at once, so each ticked country
fetches its own GeoJSON independently instead.
HikingTrails.extract_merged (server-side multi-country merge, no
longer needed now that the client picks per country) removed again.
Verified the new extract_hiking_trails + caching skip live on brick
against a real cached country PBF.
data/hiking_trails.geojson now generates automatically as the last
step of every offline.pmtiles build (PmtilesBuilder.extract_hiking_trails,
best-effort — a failure here doesn't fail the whole build), merged
across all selected countries with cross-border dedup by OSM relation
id (HikingTrails.extract_merged). Served at GET /tiles/hiking_trails.geojson
and GET /api/hiking-trails, fetched client-side only once the overlay
is actually toggled on.
hiking_trails.js now prefers this local file whenever it exists,
regardless of which base map (online/offline) is active — same
"prefer local when available" story as HillshadeControl already uses
for terrain. Offline click hit-tests the real vector layer directly
(queryRenderedFeatures) instead of querying Overpass — instant, no
network. Coordinates are rounded to 5dp to keep the combined file
smaller; colour tag values are normalized against a known-safe set
before ever reaching a style paint property.
Verified extract_merged (new: combines multiple countries' extracts,
dedupes overlapping relations) against two real cached country PBFs
on brick directly, not just fixtures.
The single shared overpass-api.de instance varies wildly in load —
Promise.any against three independent public mirrors (also CORS-open,
verified) means only the slowest one determines wait time, not
whichever happens to be hit.
Also show the description tag (falls back to it for the title when a
route has no name, e.g. many KST route relations), the ref number, and
a "View on OpenStreetMap" link — the popup was skipping data that was
already in the response for free.
The Waymarked Trails raster overlay has no queryable features (just
pixels), so this answers "what trail is this" with a one-off Overpass
query on click instead: way(around:40m) finds candidate ways, then
rel(bw) walks backward to their parent route=hiking relations. Overpass
has permissive CORS, so this runs straight from the browser, no server
proxy needed — verified against a real KST route in the Tatras.
Route length isn't tagged directly, so it's summed from each member
way's own geometry (measure.js's existing haversine lineLength, now
exported and reused rather than duplicated). The colour tag is only
ever used as a CSS background after matching a strict hex/word regex —
untrusted free text otherwise.
New Overlays panel toggle, same plain on/off shape as Contour lines —
raster tiles from waymarkedtrails.org, which already color each route
by its OSM colour tag (the KST/KČT paint-blaze colors used in
Slovakia/Czechia, plain red elsewhere), so no styling work needed here.
Online only for now; offline (baked into offline.pmtiles) needs a
custom Planetiler profile for route relations, deferred until there's
a route planner/router to pair it with.