explore: default heatmap/by-type; month All button; bbox filtering on map move
This commit is contained in:
@@ -22,10 +22,11 @@
|
|||||||
let dateFrom = '';
|
let dateFrom = '';
|
||||||
let dateTo = '';
|
let dateTo = '';
|
||||||
let selectedYear: string | null = null;
|
let selectedYear: string | null = null;
|
||||||
|
let selectedMonth: number | null = null;
|
||||||
|
|
||||||
// View
|
// View
|
||||||
let viewMode: 'lines' | 'heatmap' = 'lines';
|
let viewMode: 'lines' | 'heatmap' = 'heatmap';
|
||||||
let heatmapMode: 'global' | 'bytype' = 'global';
|
let heatmapMode: 'global' | 'bytype' = 'bytype';
|
||||||
|
|
||||||
// Tile layers — same as planner
|
// Tile layers — same as planner
|
||||||
const TILES: Record<string, { tiles: string[]; attribution: string; label: string }> = {
|
const TILES: Record<string, { tiles: string[]; attribution: string; label: string }> = {
|
||||||
@@ -83,26 +84,49 @@
|
|||||||
function clearAllTypes() { selectedTypes = new Set(); }
|
function clearAllTypes() { selectedTypes = new Set(); }
|
||||||
|
|
||||||
function setYear(y: string) {
|
function setYear(y: string) {
|
||||||
if (selectedYear === y) { selectedYear = null; dateFrom = ''; dateTo = ''; return; }
|
if (selectedYear === y) { selectedYear = null; selectedMonth = null; dateFrom = ''; dateTo = ''; return; }
|
||||||
selectedYear = y;
|
selectedYear = y;
|
||||||
|
selectedMonth = null;
|
||||||
dateFrom = `${y}-01-01`;
|
dateFrom = `${y}-01-01`;
|
||||||
dateTo = `${y}-12-31`;
|
dateTo = `${y}-12-31`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setMonth(m: number) { // m: 1-12
|
function setMonth(m: number) { // m: 1-12
|
||||||
if (!selectedYear) return;
|
if (!selectedYear) return;
|
||||||
|
selectedMonth = m;
|
||||||
const mm = String(m).padStart(2, '0');
|
const mm = String(m).padStart(2, '0');
|
||||||
const last = new Date(+selectedYear, m, 0).getDate();
|
const last = new Date(+selectedYear, m, 0).getDate();
|
||||||
dateFrom = `${selectedYear}-${mm}-01`;
|
dateFrom = `${selectedYear}-${mm}-01`;
|
||||||
dateTo = `${selectedYear}-${mm}-${String(last).padStart(2,'0')}`;
|
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 ────────────────────────────────────────────────────────────────────
|
// ── 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[]) {
|
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',
|
type: 'Feature',
|
||||||
geometry: { type: 'LineString', coordinates: t.coords },
|
geometry: { type: 'LineString', coordinates: t.coords },
|
||||||
properties: { type: t.type },
|
properties: { type: t.type },
|
||||||
@@ -113,7 +137,8 @@
|
|||||||
const features: any[] = [];
|
const features: any[] = [];
|
||||||
for (const t of ts)
|
for (const t of ts)
|
||||||
for (const [lng, lat] of t.coords)
|
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 };
|
return { type: 'FeatureCollection', features };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,9 +228,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
mapReady = true;
|
mapReady = true;
|
||||||
|
_bbox = _getBbox();
|
||||||
_updateMap(tracks, viewMode, heatmapMode);
|
_updateMap(tracks, viewMode, heatmapMode);
|
||||||
_fitBounds(tracks);
|
_fitBounds(tracks);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
map.on('moveend', () => {
|
||||||
|
_bbox = _getBbox();
|
||||||
|
if (mapReady) _updateMap(filteredTracks, viewMode, heatmapMode);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => { if (map) map.remove(); });
|
onDestroy(() => { if (map) map.remove(); });
|
||||||
@@ -261,9 +292,10 @@
|
|||||||
</div>
|
</div>
|
||||||
{#if selectedYear}
|
{#if selectedYear}
|
||||||
<div class="pills month-pills">
|
<div class="pills month-pills">
|
||||||
|
<button class="pill small" class:active={selectedMonth === null} onclick={clearMonth}>All</button>
|
||||||
{#each MONTHS as m, i}
|
{#each MONTHS as m, i}
|
||||||
<button class="pill small"
|
<button class="pill small"
|
||||||
class:active={dateFrom === `${selectedYear}-${String(i+1).padStart(2,'0')}-01`}
|
class:active={selectedMonth === i + 1}
|
||||||
onclick={() => setMonth(i + 1)}>{m}</button>
|
onclick={() => setMonth(i + 1)}>{m}</button>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user