diff options
Diffstat (limited to 'web/frontend/climb-map.ts')
| -rw-r--r-- | web/frontend/climb-map.ts | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/web/frontend/climb-map.ts b/web/frontend/climb-map.ts new file mode 100644 index 0000000..909614d --- /dev/null +++ b/web/frontend/climb-map.ts @@ -0,0 +1,65 @@ +import type { Map as LeafletMap } from "leaflet"; + +interface ClimbMapPayload { + distanceKm: number; + elevations: number[]; + route?: [number, number][]; +} + +interface ClimbMapEndpoints { + start: [number, number]; + end: [number, number]; + name: string; +} + +const mountClimbMap = (): void => { + const element = document.getElementById("climb-map"); + const endpointsElement = document.getElementById("climb-endpoints"); + const profileElement = document.getElementById("climb-profile-data"); + const leaflet = window.L; + if (!element || !endpointsElement || !profileElement || !leaflet) return; + const endpoints = JSON.parse(endpointsElement.textContent ?? "null") as ClimbMapEndpoints | null; + const profile = JSON.parse(profileElement.textContent ?? "null") as ClimbMapPayload | null; + if (!endpoints) return; + const route = (profile?.route ?? []).map(([lon, lat]) => [lat, lon] as [number, number]); + const line: [number, number][] = route.length > 1 ? route : [endpoints.start, endpoints.end]; + + const map: LeafletMap = leaflet.map(element); + leaflet + .tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { + maxZoom: 19, + attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', + }) + .addTo(map); + + const startIcon = leaflet.divIcon({ + className: "ride-map-climb-start-icon", + html: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="6" fill="currentColor"/></svg>', + iconSize: [26, 26], + iconAnchor: [13, 13], + }); + const endIcon = leaflet.divIcon({ + className: "ride-map-climb-end-icon", + html: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 3v18" stroke="currentColor" stroke-width="2"/><path d="M6 4h13v8H6Z" fill="currentColor"/><path d="M6 4h3.25v4H6ZM12.5 4h3.25v4H12.5ZM9.25 8h3.25v4H9.25ZM15.75 8H19v4h-3.25Z" fill="var(--color-forest)"/></svg>', + iconSize: [26, 26], + iconAnchor: [13, 13], + }); + leaflet + .polyline(line, { + color: "var(--color-climb-route)", + weight: 4, + opacity: 0.9, + }) + .addTo(map); + leaflet + .marker(endpoints.start, { icon: startIcon, alt: `${endpoints.name} start` }) + .addTo(map) + .bindTooltip("Start"); + leaflet + .marker(endpoints.end, { icon: endIcon, alt: `${endpoints.name} end` }) + .addTo(map) + .bindTooltip("End"); + map.fitBounds(leaflet.latLngBounds(line), { padding: [40, 40], maxZoom: 16 }); +}; + +mountClimbMap(); |
