From 3f86210706ad3bb6e34ebcfcfee487e87ff0457a Mon Sep 17 00:00:00 2001 From: mytec Date: Fri, 30 Jan 2026 09:11:15 +0200 Subject: [PATCH] @mytec: iter2 tested, added heatmapfix task --- RFCP-Heatmap-Fix.md | 151 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 RFCP-Heatmap-Fix.md diff --git a/RFCP-Heatmap-Fix.md b/RFCP-Heatmap-Fix.md new file mode 100644 index 0000000..ac20652 --- /dev/null +++ b/RFCP-Heatmap-Fix.md @@ -0,0 +1,151 @@ +# RFCP Heatmap Fix - Zoom-Dependent Intensity + +## Problem +At close zoom (12-15), heatmap becomes solid yellow/orange with no gradient visible. + +## Root Cause +The `intensity` values and `max` parameter aren't scaling properly with zoom level, causing saturation. + +## Solution + +### File: `frontend/src/components/map/Heatmap.tsx` + +Replace the current implementation with this improved version: + +```typescript +import { useEffect, useState } from 'react'; +import { useMap } from 'react-leaflet'; +import { HeatmapLayerFactory } from '@vgrid/react-leaflet-heatmap-layer'; + +const HeatmapLayer = HeatmapLayerFactory<[number, number, number]>(); + +interface HeatmapProps { + points: Array<{ + lat: number; + lon: number; + rsrp: number; + siteId: string; + }>; + visible: boolean; +} + +export function Heatmap({ points, visible }: HeatmapProps) { + const map = useMap(); + const [mapZoom, setMapZoom] = useState(map.getZoom()); + + useEffect(() => { + const handleZoomEnd = () => { + setMapZoom(map.getZoom()); + }; + + map.on('zoomend', handleZoomEnd); + return () => { + map.off('zoomend', handleZoomEnd); + }; + }, [map]); + + if (!visible || points.length === 0) { + return null; + } + + // Zoom-dependent heatmap parameters + const getHeatmapParams = (zoom: number) => { + // Radius scales inversely with zoom + // zoom 6 (country): radius=40, blur=20 + // zoom 10 (region): radius=28, blur=14 + // zoom 14 (city): radius=16, blur=10 + // zoom 18 (street): radius=8, blur=6 + const radius = Math.max(8, Math.min(40, 50 - zoom * 2.5)); + const blur = Math.max(6, Math.min(20, 30 - zoom * 1.5)); + + // Max intensity also scales with zoom + // Lower zoom (zoomed out) = higher max (more spread) + // Higher zoom (zoomed in) = lower max (more detail) + const maxIntensity = Math.max(0.3, Math.min(1.0, 1.2 - zoom * 0.05)); + + return { radius, blur, maxIntensity }; + }; + + const { radius, blur, maxIntensity } = getHeatmapParams(mapZoom); + + // Normalize RSRP to 0-1 intensity + // RSRP ranges: -120 (very weak) to -70 (excellent) + const normalizeRSRP = (rsrp: number): number => { + const minRSRP = -120; + const maxRSRP = -70; + const normalized = (rsrp - minRSRP) / (maxRSRP - minRSRP); + return Math.max(0, Math.min(1, normalized)); + }; + + // Convert points to heatmap format: [lat, lon, intensity] + const heatmapPoints = points.map(point => [ + point.lat, + point.lon, + normalizeRSRP(point.rsrp) + ] as [number, number, number]); + + return ( + p[1]} + latitudeExtractor={(p) => p[0]} + intensityExtractor={(p) => p[2]} + gradient={{ + 0.0: '#0d47a1', // Dark Blue (very weak) + 0.2: '#00bcd4', // Cyan (weak) + 0.4: '#4caf50', // Green (fair) + 0.6: '#ffeb3b', // Yellow (good) + 0.8: '#ff9800', // Orange (strong) + 1.0: '#f44336', // Red (excellent) + }} + radius={radius} + blur={blur} + max={maxIntensity} // ← KEY FIX: dynamic max + minOpacity={0.3} + /> + ); +} +``` + +## Key Changes + +1. **Dynamic `max` parameter**: + ```typescript + const maxIntensity = Math.max(0.3, Math.min(1.0, 1.2 - zoom * 0.05)); + ``` + - Zoom 6: max = 0.9 (spread out, needs higher threshold) + - Zoom 12: max = 0.6 (medium detail) + - Zoom 18: max = 0.3 (tight detail, lower threshold) + +2. **Better RSRP normalization**: + ```typescript + const normalized = (rsrp - minRSRP) / (maxRSRP - minRSRP); + ``` + - Ensures full 0-1 range is used + - Maps -120 dBm → 0.0 (blue) + - Maps -70 dBm → 1.0 (red) + +3. **Clearer variable names** and comments + +## Testing + +After applying this fix: + +1. **Zoom out (level 6-8)**: Should see smooth gradient, larger blob +2. **Zoom medium (level 10-12)**: Clear color transitions +3. **Zoom close (level 14-16)**: Should still show gradient, not solid color +4. **Very close (level 18+)**: Small detailed dots with gradient + +## If Still Solid at Close Zoom + +Try adjusting the `maxIntensity` formula: + +```typescript +// More aggressive scaling (lower max at high zoom) +const maxIntensity = Math.max(0.2, Math.min(1.0, 1.5 - zoom * 0.08)); + +// Or even more aggressive +const maxIntensity = Math.max(0.15, Math.min(1.0, 2.0 - zoom * 0.1)); +``` + +This will make the gradient more visible at close zoom levels.