diff --git a/site/src/components/Explore.svelte b/site/src/components/Explore.svelte index 4b4dd77..805aa1a 100644 --- a/site/src/components/Explore.svelte +++ b/site/src/components/Explore.svelte @@ -22,10 +22,11 @@ let dateFrom = ''; let dateTo = ''; let selectedYear: string | null = null; + let selectedMonth: number | null = null; // View - let viewMode: 'lines' | 'heatmap' = 'lines'; - let heatmapMode: 'global' | 'bytype' = 'global'; + let viewMode: 'lines' | 'heatmap' = 'heatmap'; + let heatmapMode: 'global' | 'bytype' = 'bytype'; // Tile layers — same as planner const TILES: Record = { @@ -83,26 +84,49 @@ function clearAllTypes() { selectedTypes = new Set(); } function setYear(y: string) { - if (selectedYear === y) { selectedYear = null; dateFrom = ''; dateTo = ''; return; } + if (selectedYear === y) { selectedYear = null; selectedMonth = null; dateFrom = ''; dateTo = ''; return; } selectedYear = y; + selectedMonth = null; dateFrom = `${y}-01-01`; dateTo = `${y}-12-31`; } function setMonth(m: number) { // m: 1-12 if (!selectedYear) return; + selectedMonth = m; const mm = String(m).padStart(2, '0'); const last = new Date(+selectedYear, m, 0).getDate(); dateFrom = `${selectedYear}-${mm}-01`; dateTo = `${selectedYear}-${mm}-${String(last).padStart(2,'0')}`; } - function clearDates() { dateFrom = ''; dateTo = ''; selectedYear = null; } + function clearMonth() { + if (!selectedYear) return; + selectedMonth = null; + dateFrom = `${selectedYear}-01-01`; + dateTo = `${selectedYear}-12-31`; + } + + function clearDates() { dateFrom = ''; dateTo = ''; selectedYear = null; selectedMonth = null; } // ── Map ──────────────────────────────────────────────────────────────────── + let _bbox: { w: number; e: number; s: number; n: number } | null = null; + + function _getBbox() { + if (!map) return null; + const b = map.getBounds(); + return { w: b.getWest(), e: b.getEast(), s: b.getSouth(), n: b.getNorth() }; + } + + function _inBbox(lng: number, lat: number): boolean { + if (!_bbox) return true; + return lng >= _bbox.w && lng <= _bbox.e && lat >= _bbox.s && lat <= _bbox.n; + } + function _linesGeoJSON(ts: Track[]) { - return { type: 'FeatureCollection', features: ts.map(t => ({ + const visible = _bbox ? ts.filter(t => t.coords.some(([lng, lat]) => _inBbox(lng, lat))) : ts; + return { type: 'FeatureCollection', features: visible.map(t => ({ type: 'Feature', geometry: { type: 'LineString', coordinates: t.coords }, properties: { type: t.type }, @@ -113,7 +137,8 @@ const features: any[] = []; for (const t of ts) for (const [lng, lat] of t.coords) - features.push({ type: 'Feature', geometry: { type: 'Point', coordinates: [lng, lat] }, properties: { type: t.type } }); + if (_inBbox(lng, lat)) + features.push({ type: 'Feature', geometry: { type: 'Point', coordinates: [lng, lat] }, properties: { type: t.type } }); return { type: 'FeatureCollection', features }; } @@ -203,9 +228,15 @@ } mapReady = true; + _bbox = _getBbox(); _updateMap(tracks, viewMode, heatmapMode); _fitBounds(tracks); }); + + map.on('moveend', () => { + _bbox = _getBbox(); + if (mapReady) _updateMap(filteredTracks, viewMode, heatmapMode); + }); }); onDestroy(() => { if (map) map.remove(); }); @@ -261,9 +292,10 @@ {#if selectedYear}
+ {#each MONTHS as m, i} {/each}