explore: replace gaussian heatmap with line-accumulation (strava-style)

This commit is contained in:
Davide Scaini
2026-05-14 14:54:47 +02:00
parent 6d13993f98
commit b3c41967f6
+9 -33
View File
@@ -133,29 +133,13 @@
}))}; }))};
} }
function _heatGeoJSON(ts: Track[]) {
const features: any[] = [];
for (const t of ts)
for (const [lng, lat] of t.coords)
if (_inBbox(lng, lat))
features.push({ type: 'Feature', geometry: { type: 'Point', coordinates: [lng, lat] }, properties: { type: t.type } });
return { type: 'FeatureCollection', features };
}
function _empty() { return { type: 'FeatureCollection', features: [] }; } function _empty() { return { type: 'FeatureCollection', features: [] }; }
function _hexRgb(hex: string): [number,number,number] {
const m = hex.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i);
return m ? [parseInt(m[1],16), parseInt(m[2],16), parseInt(m[3],16)] : [160,160,160];
}
function _updateMap(filtered: Track[], view: string, heatMode: string) { function _updateMap(filtered: Track[], view: string, heatMode: string) {
const linesSrc = map.getSource('explore-lines'); const linesSrc = map.getSource('explore-lines');
const heatSrc = map.getSource('explore-heat'); if (!linesSrc) return;
if (!linesSrc || !heatSrc) return;
linesSrc.setData(view === 'lines' ? _linesGeoJSON(filtered) : _empty()); linesSrc.setData(_linesGeoJSON(filtered));
heatSrc.setData(view === 'heatmap' ? _heatGeoJSON(filtered) : _empty());
map.setLayoutProperty('explore-lines', 'visibility', view === 'lines' ? 'visible' : 'none'); map.setLayoutProperty('explore-lines', 'visibility', view === 'lines' ? 'visible' : 'none');
map.setLayoutProperty('explore-heat-global', 'visibility', view === 'heatmap' && heatMode === 'global' ? 'visible' : 'none'); map.setLayoutProperty('explore-heat-global', 'visibility', view === 'heatmap' && heatMode === 'global' ? 'visible' : 'none');
@@ -195,9 +179,8 @@
map.on('load', () => { map.on('load', () => {
map.addSource('explore-lines', { type: 'geojson', data: _empty() }); map.addSource('explore-lines', { type: 'geojson', data: _empty() });
map.addSource('explore-heat', { type: 'geojson', data: _empty() });
// Lines layer — color by type // Normal lines — color by type, readable opacity
map.addLayer({ id: 'explore-lines', type: 'line', source: 'explore-lines', layout: { visibility: 'none' }, map.addLayer({ id: 'explore-lines', type: 'line', source: 'explore-lines', layout: { visibility: 'none' },
paint: { 'line-width': 2, 'line-opacity': 0.5, paint: { 'line-width': 2, 'line-opacity': 0.5,
'line-color': ['match', ['get', 'type'], 'line-color': ['match', ['get', 'type'],
@@ -207,23 +190,16 @@
}, },
}); });
// Global heatmap // Global heatmap — all lines in warm amber, very low opacity so overlapping routes stack up
map.addLayer({ id: 'explore-heat-global', type: 'heatmap', source: 'explore-heat', layout: { visibility: 'none' }, map.addLayer({ id: 'explore-heat-global', type: 'line', source: 'explore-lines', layout: { visibility: 'none' },
paint: { 'heatmap-radius': 14, 'heatmap-opacity': 0.85, paint: { 'line-width': 2, 'line-opacity': 0.08, 'line-blur': 0.5, 'line-color': '#f97316' },
'heatmap-color': ['interpolate', ['linear'], ['heatmap-density'],
0, 'rgba(0,0,0,0)', 0.2, '#4ade80', 0.5, '#facc15', 0.8, '#f97316', 1, '#ef4444'],
},
}); });
// Per-type heatmap layers // Per-type heatmap layers — same accumulation trick, type-specific colour
for (const [type, hex] of Object.entries(TYPE_COLORS)) { for (const [type, hex] of Object.entries(TYPE_COLORS)) {
const [r,g,b] = _hexRgb(hex); map.addLayer({ id: `explore-heat-${type}`, type: 'line', source: 'explore-lines',
map.addLayer({ id: `explore-heat-${type}`, type: 'heatmap', source: 'explore-heat',
filter: ['==', ['get', 'type'], type], layout: { visibility: 'none' }, filter: ['==', ['get', 'type'], type], layout: { visibility: 'none' },
paint: { 'heatmap-radius': 14, 'heatmap-opacity': 0.85, paint: { 'line-width': 2, 'line-opacity': 0.1, 'line-blur': 0.5, 'line-color': hex },
'heatmap-color': ['interpolate', ['linear'], ['heatmap-density'],
0, `rgba(${r},${g},${b},0)`, 0.3, `rgba(${r},${g},${b},0.4)`, 1, `rgba(${r},${g},${b},1)`],
},
}); });
} }