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: '© OpenStreetMap contributors', }) .addTo(map); const startIcon = leaflet.divIcon({ className: "ride-map-climb-start-icon", html: '', iconSize: [26, 26], iconAnchor: [13, 13], }); const endIcon = leaflet.divIcon({ className: "ride-map-climb-end-icon", html: '', 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();