map now working
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import maplibregl from 'maplibre-gl';
|
||||
import 'maplibre-gl/dist/maplibre-gl.css';
|
||||
import type { Timeseries } from '../lib/types';
|
||||
|
||||
export let trackUrl: string;
|
||||
export let timeseries: Timeseries | null = null;
|
||||
export let bbox: [number, number, number, number] | null = null;
|
||||
export let accentColor: string = '#00c8ff';
|
||||
export let hoveredIdx: number | null = null;
|
||||
|
||||
let mapEl: HTMLDivElement;
|
||||
let map: any;
|
||||
const MarkerClass = maplibregl.Marker;
|
||||
let hoverMarker: any;
|
||||
let markersAdded = false;
|
||||
|
||||
const TILE_STYLE = 'https://tiles.openfreemap.org/styles/positron';
|
||||
|
||||
onMount(() => {
|
||||
map = new maplibregl.Map({
|
||||
container: mapEl,
|
||||
style: TILE_STYLE,
|
||||
center: [0, 0],
|
||||
zoom: 1,
|
||||
attributionControl: false,
|
||||
});
|
||||
|
||||
map.addControl(new maplibregl.AttributionControl({ compact: true }), 'bottom-right');
|
||||
|
||||
// Hover dot marker — must set lngLat before addTo in MapLibre v5
|
||||
const el = document.createElement('div');
|
||||
el.style.cssText = `
|
||||
width:12px;height:12px;border-radius:50%;
|
||||
background:white;border:2px solid ${accentColor};
|
||||
box-shadow:0 0 6px ${accentColor};display:none;pointer-events:none;
|
||||
`;
|
||||
hoverMarker = new maplibregl.Marker({ element: el, anchor: 'center' })
|
||||
.setLngLat([0, 0])
|
||||
.addTo(map);
|
||||
|
||||
map.on('load', () => {
|
||||
map.addSource('track', {
|
||||
type: 'geojson',
|
||||
data: trackUrl,
|
||||
lineMetrics: true,
|
||||
});
|
||||
|
||||
map.addLayer({
|
||||
id: 'track-shadow',
|
||||
type: 'line',
|
||||
source: 'track',
|
||||
paint: { 'line-color': 'rgba(0,0,0,0.3)', 'line-width': 5, 'line-blur': 2 },
|
||||
});
|
||||
|
||||
map.addLayer({
|
||||
id: 'track-line',
|
||||
type: 'line',
|
||||
source: 'track',
|
||||
layout: { 'line-cap': 'round', 'line-join': 'round' },
|
||||
paint: {
|
||||
'line-width': 3,
|
||||
'line-gradient': [
|
||||
'interpolate', ['linear'], ['line-progress'],
|
||||
0, accentColor,
|
||||
0.5, '#ff6b35',
|
||||
1, accentColor,
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Fit to bbox when detail JSON loads (bbox is null at map init)
|
||||
$: if (map && bbox) {
|
||||
const fit = () => map.fitBounds(
|
||||
[[bbox![0], bbox![1]], [bbox![2], bbox![3]]],
|
||||
{ padding: 40, animate: true },
|
||||
);
|
||||
map.loaded() ? fit() : map.once('load', fit);
|
||||
}
|
||||
|
||||
// Add start/end markers when timeseries arrives
|
||||
$: if (map && MarkerClass && timeseries && !markersAdded) {
|
||||
markersAdded = true;
|
||||
const add = () => {
|
||||
const lats = (timeseries!.lat ?? []).filter(v => v != null) as number[];
|
||||
const lons = (timeseries!.lon ?? []).filter(v => v != null) as number[];
|
||||
if (!lats.length) return;
|
||||
new MarkerClass({ element: makeDot('#4ade80'), anchor: 'center' })
|
||||
.setLngLat([lons[0], lats[0]]).addTo(map);
|
||||
new MarkerClass({ element: makeDot('#f87171'), anchor: 'center' })
|
||||
.setLngLat([lons[lons.length - 1], lats[lats.length - 1]]).addTo(map);
|
||||
};
|
||||
map.loaded() ? add() : map.once('load', add);
|
||||
}
|
||||
|
||||
// Hover dot linked to chart crosshair
|
||||
$: if (hoverMarker && timeseries && hoveredIdx != null) {
|
||||
const lat = timeseries.lat?.[hoveredIdx];
|
||||
const lon = timeseries.lon?.[hoveredIdx];
|
||||
if (lat != null && lon != null) {
|
||||
hoverMarker.getElement().style.display = 'block';
|
||||
hoverMarker.setLngLat([lon, lat]);
|
||||
}
|
||||
} else if (hoverMarker) {
|
||||
hoverMarker.getElement().style.display = 'none';
|
||||
}
|
||||
|
||||
function makeDot(color: string): HTMLDivElement {
|
||||
const el = document.createElement('div');
|
||||
el.style.cssText = `
|
||||
width:10px;height:10px;border-radius:50%;
|
||||
background:${color};border:2px solid white;
|
||||
box-shadow:0 0 4px rgba(0,0,0,0.5);
|
||||
`;
|
||||
return el;
|
||||
}
|
||||
|
||||
onDestroy(() => map?.remove());
|
||||
</script>
|
||||
|
||||
<div bind:this={mapEl} class="w-full h-full" />
|
||||
Reference in New Issue
Block a user