fix: use CSS-based coverage rings on scanner markers
Coverage rings now render as CSS pseudo-elements on scanner markers, following the same floor offset positioning. Toggle .heatmap-enabled class on map container to show/hide coverage visualization. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -978,6 +978,33 @@ body {
|
||||
background: rgba(0, 200, 255, 0.9);
|
||||
}
|
||||
|
||||
/* Scanner coverage rings (shown when heatmap enabled) */
|
||||
.heatmap-enabled .marker-3d.center .marker-icon::before,
|
||||
.heatmap-enabled .marker-3d.peer-scanner .marker-icon::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle,
|
||||
rgba(100, 255, 100, 0.3) 0%,
|
||||
rgba(100, 255, 100, 0.2) 25%,
|
||||
rgba(255, 255, 100, 0.15) 50%,
|
||||
rgba(255, 100, 100, 0.1) 75%,
|
||||
transparent 100%
|
||||
);
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.heatmap-enabled .marker-3d.center .marker-icon,
|
||||
.heatmap-enabled .marker-3d.peer-scanner .marker-icon {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Trilaterated device markers - gold border */
|
||||
.marker-3d.trilaterated .marker-icon {
|
||||
border: 2px dashed #ffd700 !important;
|
||||
|
||||
@@ -2548,6 +2548,13 @@ function toggleHeatMap() {
|
||||
const btn = document.getElementById('btn-heatmap');
|
||||
if (btn) btn.classList.toggle('active', heatMapEnabled);
|
||||
|
||||
// Toggle CSS class on map container for scanner coverage rings
|
||||
const mapContainer = document.getElementById('map-3d');
|
||||
if (mapContainer) {
|
||||
mapContainer.classList.toggle('heatmap-enabled', heatMapEnabled);
|
||||
}
|
||||
|
||||
// Also render/remove MapLibre layers for additional visualization
|
||||
if (heatMapEnabled) {
|
||||
renderHeatMap();
|
||||
} else {
|
||||
@@ -2555,95 +2562,13 @@ function toggleHeatMap() {
|
||||
}
|
||||
}
|
||||
|
||||
// Render heat map layer on 3D map
|
||||
// Uses circle layers for scanner coverage visualization
|
||||
// Render heat map visualization
|
||||
// Coverage rings are CSS-based on scanner markers (via .heatmap-enabled class)
|
||||
async function renderHeatMap() {
|
||||
if (!map3d || !map3dLoaded) {
|
||||
console.log('[HeatMap] 3D map not ready');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const resp = await fetch('/api/heatmap/signal');
|
||||
if (!resp.ok) return;
|
||||
|
||||
const data = await resp.json();
|
||||
console.log(`[HeatMap] Loaded ${data.count} points`);
|
||||
|
||||
// Remove existing heat map if present
|
||||
removeHeatMap();
|
||||
|
||||
// Filter to scanner positions only (weight=1.0 indicates scanners)
|
||||
const scannerPoints = data.points.filter(p => p.weight >= 0.9);
|
||||
|
||||
// Add coverage circles source
|
||||
map3d.addSource('signal-heatmap', {
|
||||
type: 'geojson',
|
||||
data: {
|
||||
type: 'FeatureCollection',
|
||||
features: scannerPoints.map(p => ({
|
||||
type: 'Feature',
|
||||
geometry: { type: 'Point', coordinates: [p.lon, p.lat] },
|
||||
properties: { weight: p.weight }
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
||||
// Add outer coverage ring (weak signal ~15m)
|
||||
map3d.addLayer({
|
||||
id: 'signal-heatmap-outer',
|
||||
type: 'circle',
|
||||
source: 'signal-heatmap',
|
||||
paint: {
|
||||
'circle-radius': 80,
|
||||
'circle-color': 'rgba(255, 100, 100, 0.15)',
|
||||
'circle-stroke-width': 1,
|
||||
'circle-stroke-color': 'rgba(255, 100, 100, 0.3)'
|
||||
}
|
||||
});
|
||||
|
||||
// Add middle coverage ring (fair signal ~10m)
|
||||
map3d.addLayer({
|
||||
id: 'signal-heatmap-middle',
|
||||
type: 'circle',
|
||||
source: 'signal-heatmap',
|
||||
paint: {
|
||||
'circle-radius': 50,
|
||||
'circle-color': 'rgba(255, 255, 100, 0.2)',
|
||||
'circle-stroke-width': 1,
|
||||
'circle-stroke-color': 'rgba(255, 255, 100, 0.4)'
|
||||
}
|
||||
});
|
||||
|
||||
// Add inner coverage ring (good signal ~5m)
|
||||
map3d.addLayer({
|
||||
id: 'signal-heatmap-inner',
|
||||
type: 'circle',
|
||||
source: 'signal-heatmap',
|
||||
paint: {
|
||||
'circle-radius': 25,
|
||||
'circle-color': 'rgba(100, 255, 100, 0.25)',
|
||||
'circle-stroke-width': 1,
|
||||
'circle-stroke-color': 'rgba(100, 255, 100, 0.5)'
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('[HeatMap] Error:', e);
|
||||
}
|
||||
console.log('[HeatMap] Enabled - coverage rings shown around scanners');
|
||||
}
|
||||
|
||||
// Remove heat map layer from 3D map
|
||||
// Remove heat map visualization
|
||||
function removeHeatMap() {
|
||||
if (!map3d) return;
|
||||
|
||||
// Remove all heatmap layers
|
||||
['signal-heatmap-inner', 'signal-heatmap-middle', 'signal-heatmap-outer', 'signal-heatmap-layer'].forEach(layerId => {
|
||||
if (map3d.getLayer(layerId)) {
|
||||
map3d.removeLayer(layerId);
|
||||
}
|
||||
});
|
||||
|
||||
if (map3d.getSource('signal-heatmap')) {
|
||||
map3d.removeSource('signal-heatmap');
|
||||
}
|
||||
console.log('[HeatMap] Disabled');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user