@mytec: iter2 tested, added heatmapfix task
This commit is contained in:
151
RFCP-Heatmap-Fix.md
Normal file
151
RFCP-Heatmap-Fix.md
Normal file
@@ -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 (
|
||||
<HeatmapLayer
|
||||
points={heatmapPoints}
|
||||
longitudeExtractor={(p) => 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.
|
||||
Reference in New Issue
Block a user