Segment create: load existing segments in activity bbox as reference layer
This commit is contained in:
@@ -36,10 +36,13 @@
|
||||
let mapEl: HTMLDivElement;
|
||||
let map: any;
|
||||
let mapReady = false;
|
||||
let refSegments: { id: string; name: string; polyline: [number, number][] }[] = [];
|
||||
let refPopup: any = null;
|
||||
|
||||
const TILE_STYLE = 'https://tiles.openfreemap.org/styles/positron';
|
||||
const DIM_COLOR = '#71717a';
|
||||
const SEL_COLOR = '#3b82f6';
|
||||
const REF_GRADIENT = ['interpolate', ['linear'], ['line-progress'], 0, '#22c55e', 1, '#ef4444'];
|
||||
|
||||
// ── Derived ───────────────────────────────────────────────────────────────
|
||||
$: filteredActivities = activities.filter(a =>
|
||||
@@ -108,6 +111,18 @@
|
||||
startIdx = 0;
|
||||
endIdx = pts.length - 1;
|
||||
loadingTrack = false;
|
||||
|
||||
// Fetch existing segments in this bbox as reference (fire-and-forget)
|
||||
refSegments = [];
|
||||
const lonMin = Math.min(...pts.map(p => p[1]));
|
||||
const latMin = Math.min(...pts.map(p => p[0]));
|
||||
const lonMax = Math.max(...pts.map(p => p[1]));
|
||||
const latMax = Math.max(...pts.map(p => p[0]));
|
||||
fetch(`/api/segments?bbox=${lonMin.toFixed(5)},${latMin.toFixed(5)},${lonMax.toFixed(5)},${latMax.toFixed(5)}`, { credentials: 'include' })
|
||||
.then(r => r.ok ? r.json() : [])
|
||||
.then(segs => { refSegments = segs; })
|
||||
.catch(() => {});
|
||||
|
||||
// Map init happens after the DOM updates (bind:this needs the element)
|
||||
setTimeout(initMap, 0);
|
||||
} catch {
|
||||
@@ -143,6 +158,17 @@
|
||||
};
|
||||
}
|
||||
|
||||
function toRefCollection(segs: typeof refSegments) {
|
||||
return {
|
||||
type: 'FeatureCollection' as const,
|
||||
features: segs.map(s => ({
|
||||
type: 'Feature' as const,
|
||||
geometry: { type: 'LineString' as const, coordinates: s.polyline.map(([lat, lon]: [number, number]) => [lon, lat]) },
|
||||
properties: { id: s.id, name: s.name },
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
function initMap() {
|
||||
if (!mapEl || !gpsPoints.length) return;
|
||||
const lats = gpsPoints.map(p => p[0]);
|
||||
@@ -158,6 +184,37 @@
|
||||
map.addControl(new maplibregl.AttributionControl({ compact: true }), 'bottom-right');
|
||||
|
||||
map.on('load', () => {
|
||||
// Reference segments — rendered below the activity track
|
||||
map.addSource('refs', {
|
||||
type: 'geojson',
|
||||
lineMetrics: true,
|
||||
data: toRefCollection(refSegments),
|
||||
});
|
||||
map.addLayer({ id: 'ref-line', type: 'line', source: 'refs',
|
||||
layout: { 'line-cap': 'round', 'line-join': 'round' },
|
||||
paint: {
|
||||
'line-gradient': REF_GRADIENT,
|
||||
'line-width': 3,
|
||||
'line-opacity': 0.75,
|
||||
},
|
||||
});
|
||||
|
||||
map.on('mouseenter', 'ref-line', (e: any) => {
|
||||
map.getCanvas().style.cursor = 'pointer';
|
||||
const name = e.features?.[0]?.properties?.name;
|
||||
if (name) {
|
||||
refPopup = new maplibregl.Popup({ closeButton: false, closeOnClick: false, offset: 8 })
|
||||
.setLngLat(e.lngLat)
|
||||
.setText(name)
|
||||
.addTo(map);
|
||||
}
|
||||
});
|
||||
map.on('mouseleave', 'ref-line', () => {
|
||||
map.getCanvas().style.cursor = '';
|
||||
refPopup?.remove();
|
||||
refPopup = null;
|
||||
});
|
||||
|
||||
map.addSource('full', { type: 'geojson', data: toLine(gpsPoints) });
|
||||
map.addSource('selected', { type: 'geojson', data: toLine(selectedPolyline) });
|
||||
|
||||
@@ -177,6 +234,11 @@
|
||||
map.getSource('selected').setData(toLine(selectedPolyline));
|
||||
}
|
||||
|
||||
// Update ref layer when segments load after the map is already ready
|
||||
$: if (mapReady && map?.getSource('refs')) {
|
||||
map.getSource('refs').setData(toRefCollection(refSegments));
|
||||
}
|
||||
|
||||
// ── Slider guards ─────────────────────────────────────────────────────────
|
||||
function onStartInput() { if (startIdx >= endIdx) startIdx = Math.max(0, endIdx - 1); }
|
||||
function onEndInput() { if (endIdx <= startIdx) endIdx = Math.min(maxIdx, startIdx + 1); }
|
||||
@@ -351,7 +413,7 @@
|
||||
</div>
|
||||
<button
|
||||
class="text-xs text-zinc-400 hover:text-white transition-colors shrink-0"
|
||||
on:click={() => { selectedActivity = null; gpsPoints = []; elevations = []; map?.remove(); mapReady = false; }}
|
||||
on:click={() => { selectedActivity = null; gpsPoints = []; elevations = []; refSegments = []; map?.remove(); mapReady = false; }}
|
||||
>Change</button>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user