Initial scaffold: Vite + Svelte, MapLibre, Brouter routing, GPX export

This commit is contained in:
Davide Scaini
2026-05-13 22:36:53 +02:00
commit 42e7c20fc1
15 changed files with 2113 additions and 0 deletions
+66
View File
@@ -0,0 +1,66 @@
<script>
// Renders a simple SVG elevation profile from a Brouter GeoJSON route feature.
let { route } = $props();
const PAD = { top: 8, right: 8, bottom: 20, left: 36 };
const H = 140;
let svgEl;
let W = $state(400);
$effect(() => {
if (!svgEl) return;
const ro = new ResizeObserver(e => { W = e[0].contentRect.width; });
ro.observe(svgEl);
return () => ro.disconnect();
});
let profile = $derived.by(() => {
if (!route) return null;
const coords = route.geometry?.coordinates ?? [];
if (coords.length < 2) return null;
// Build cumulative distance + elevation arrays
const pts = [];
let dist = 0;
for (let i = 0; i < coords.length; i++) {
if (i > 0) {
const dx = coords[i][0] - coords[i-1][0];
const dy = coords[i][1] - coords[i-1][1];
dist += Math.sqrt(dx*dx + dy*dy) * 111320; // rough metres
}
pts.push({ d: dist, e: coords[i][2] ?? 0 });
}
const totalDist = pts[pts.length - 1].d;
const eles = pts.map(p => p.e);
const minE = Math.min(...eles);
const maxE = Math.max(...eles);
const rangeE = maxE - minE || 1;
const iW = W - PAD.left - PAD.right;
const iH = H - PAD.top - PAD.bottom;
const toX = d => PAD.left + (d / totalDist) * iW;
const toY = e => PAD.top + iH - ((e - minE) / rangeE) * iH;
const polyline = pts.map(p => `${toX(p.d).toFixed(1)},${toY(p.e).toFixed(1)}`).join(' ');
const area = `${toX(0)},${(PAD.top + iH).toFixed(1)} ` + polyline + ` ${toX(totalDist)},${(PAD.top + iH).toFixed(1)}`;
return { polyline, area, minE, maxE, totalDist, iW, iH };
});
</script>
<svg bind:this={svgEl} width="100%" height={H} style="display:block">
{#if profile}
<!-- area fill -->
<polygon points={profile.area} fill="#e879a022" />
<!-- line -->
<polyline points={profile.polyline} fill="none" stroke="#e879a0" stroke-width="1.5" />
<!-- y labels -->
<text x={PAD.left - 4} y={PAD.top + 4} text-anchor="end" font-size="9" fill="#71717a">{profile.maxE.toFixed(0)}m</text>
<text x={PAD.left - 4} y={PAD.top + profile.iH} text-anchor="end" font-size="9" fill="#71717a">{profile.minE.toFixed(0)}m</text>
<!-- x label -->
<text x={PAD.left + profile.iW / 2} y={H - 4} text-anchor="middle" font-size="9" fill="#71717a">{(profile.totalDist / 1000).toFixed(1)} km</text>
{/if}
</svg>