From 8f843833e0640cc0a2637392e0603a78b5fce9e8 Mon Sep 17 00:00:00 2001 From: Alexander Bocken Date: Fri, 22 May 2026 18:51:02 +0200 Subject: [PATCH] fix(route-builder): use Swisstopo elevations for snapped routes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit routeWaypoints passed BRouter's coarse, SRTM-based inline elevations straight through, and the client only queried Swisstopo when a point lacked an altitude — so snapped routes never hit the Swiss terrain model and shipped a jagged profile that disagreed with the densified off-trail path. Overwrite every routed point with enrichElevations (geo.admin.ch), keeping the router's value only as a fallback where Swisstopo returns null (hikes abroad). Disk-cached by coordinate list, so repeat snaps stay free. --- package.json | 2 +- src/lib/server/hikesRouting.ts | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 4aeaa078..be662ac7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homepage", - "version": "1.84.0", + "version": "1.84.1", "private": true, "type": "module", "scripts": { diff --git a/src/lib/server/hikesRouting.ts b/src/lib/server/hikesRouting.ts index 0efb63be..90d5f7b4 100644 --- a/src/lib/server/hikesRouting.ts +++ b/src/lib/server/hikesRouting.ts @@ -217,6 +217,25 @@ export async function routeWaypoints(opts: { clearTimeout(timeout); } + // Swisstopo is the authoritative elevation source. BRouter ships its own + // (coarse, SRTM-based) elevations inline, which made snapped routes carry a + // jagged profile that disagreed with the densified off-trail path. Overwrite + // every point with the Swiss terrain model; coordinates outside Switzerland + // come back `null` and keep the router's elevation as a fallback (hikes + // abroad). `enrichElevations` is disk-cached by coordinate list, so repeated + // snaps are free. + const flat = segments.flat(); + if (flat.length > 0) { + const elevs = await enrichElevations(flat.map((p) => [p[0], p[1]] as [number, number])); + let idx = 0; + for (const seg of segments) { + for (let i = 0; i < seg.length; i++) { + const e = elevs[idx++]; + if (typeof e === 'number') seg[i] = [seg[i][0], seg[i][1], e]; + } + } + } + const source = sources.size === 1 ? [...sources][0] : 'mixed'; return { segments, source }; }