@mytec: iter 7.1 - gradient fix
This commit is contained in:
279
RFCP-QuickFix-Zoom-Bounds.md
Normal file
279
RFCP-QuickFix-Zoom-Bounds.md
Normal file
@@ -0,0 +1,279 @@
|
||||
# RFCP - Quick Fix: Zoom Gradient + Calculation Square
|
||||
|
||||
## Issue 1: Zoom Still Breaks Gradient
|
||||
|
||||
**Problem:** Despite maxIntensity=0.75, colors still shift with zoom.
|
||||
|
||||
**Debug:** Check console for `🔍 Heatmap Debug` - what does it show?
|
||||
|
||||
### Possible Root Causes:
|
||||
|
||||
**A. maxIntensity is STILL a formula** (not constant)
|
||||
|
||||
**File:** `frontend/src/components/map/Heatmap.tsx`
|
||||
|
||||
Check if this line exists:
|
||||
```typescript
|
||||
const maxIntensity = Math.max(0.5, Math.min(0.9, 1.0 - mapZoom * 0.03)); // ❌ BAD
|
||||
```
|
||||
|
||||
**Replace with:**
|
||||
```typescript
|
||||
const maxIntensity = 0.75; // ✅ MUST be constant!
|
||||
```
|
||||
|
||||
**B. Heatmap layer not re-rendering** on zoom
|
||||
|
||||
Add force re-render:
|
||||
```typescript
|
||||
const [key, setKey] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const handleZoomEnd = () => {
|
||||
setMapZoom(map.getZoom());
|
||||
setKey(prev => prev + 1); // Force re-render
|
||||
};
|
||||
map.on('zoomend', handleZoomEnd);
|
||||
return () => { map.off('zoomend', handleZoomEnd); };
|
||||
}, [map]);
|
||||
|
||||
return (
|
||||
<div style={{ opacity }} key={key}> {/* ← Add key */}
|
||||
<HeatmapLayer ... />
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
**C. RSRP normalization range too narrow**
|
||||
|
||||
Try wider range:
|
||||
```typescript
|
||||
const normalizeRSRP = (rsrp: number): number => {
|
||||
const minRSRP = -140; // Even wider (was -130)
|
||||
const maxRSRP = -40; // Even wider (was -50)
|
||||
const normalized = (rsrp - minRSRP) / (maxRSRP - minRSRP);
|
||||
return Math.max(0, Math.min(1, normalized));
|
||||
};
|
||||
```
|
||||
|
||||
### Nuclear Option: Remove Dynamic Parameters Entirely
|
||||
|
||||
```typescript
|
||||
export function Heatmap({ points, visible, opacity = 0.7 }: HeatmapProps) {
|
||||
const map = useMap();
|
||||
|
||||
if (!visible || points.length === 0) return null;
|
||||
|
||||
// FIXED RSRP normalization
|
||||
const normalizeRSRP = (rsrp: number): number => {
|
||||
return Math.max(0, Math.min(1, (rsrp + 140) / 100)); // -140 to -40 dBm
|
||||
};
|
||||
|
||||
const heatmapPoints = points.map(p => [
|
||||
p.lat,
|
||||
p.lon,
|
||||
normalizeRSRP(p.rsrp)
|
||||
] as [number, number, number]);
|
||||
|
||||
// CONSTANT parameters (NO zoom dependency!)
|
||||
return (
|
||||
<div style={{ opacity }}>
|
||||
<HeatmapLayer
|
||||
points={heatmapPoints}
|
||||
longitudeExtractor={(p) => p[1]}
|
||||
latitudeExtractor={(p) => p[0]}
|
||||
intensityExtractor={(p) => p[2]}
|
||||
gradient={{
|
||||
0.0: '#1a237e',
|
||||
0.2: '#2196f3',
|
||||
0.4: '#00bcd4',
|
||||
0.5: '#4caf50',
|
||||
0.6: '#8bc34a',
|
||||
0.7: '#ffeb3b',
|
||||
0.8: '#ff9800',
|
||||
1.0: '#f44336',
|
||||
}}
|
||||
radius={25} // FIXED (no zoom logic)
|
||||
blur={15} // FIXED
|
||||
max={0.75} // FIXED
|
||||
minOpacity={0.3}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Issue 2: Calculation Square Too Visible
|
||||
|
||||
**Problem:** Green rectangle shows calculation bounds - too distracting.
|
||||
|
||||
### Option A: Make It Subtle
|
||||
|
||||
**File:** Find where calculation bounds are drawn (probably in Map or Coverage component)
|
||||
|
||||
**Current (green bold line):**
|
||||
```typescript
|
||||
<Rectangle
|
||||
bounds={[[minLat, minLon], [maxLat, maxLon]]}
|
||||
pathOptions={{ color: '#00ff00', weight: 3, opacity: 1 }}
|
||||
/>
|
||||
```
|
||||
|
||||
**Change to subtle dashed line:**
|
||||
```typescript
|
||||
<Rectangle
|
||||
bounds={[[minLat, minLon], [maxLat, maxLon]]}
|
||||
pathOptions={{
|
||||
color: '#666', // Gray (was green)
|
||||
weight: 1, // Thin (was 3)
|
||||
opacity: 0.3, // Transparent (was 1)
|
||||
dashArray: '5, 5', // Dashed
|
||||
fillOpacity: 0 // No fill
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
### Option B: Hide It Entirely
|
||||
|
||||
**Add toggle:**
|
||||
```typescript
|
||||
// In settings store
|
||||
showCalculationBounds: false, // Default hidden
|
||||
|
||||
// In Map component
|
||||
{showCalculationBounds && (
|
||||
<Rectangle ... />
|
||||
)}
|
||||
|
||||
// In UI
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={showCalculationBounds}
|
||||
onChange={(e) => setShowCalculationBounds(e.target.checked)}
|
||||
/>
|
||||
Show Calculation Bounds
|
||||
</label>
|
||||
```
|
||||
|
||||
### Option C: Auto-Hide After Calculation
|
||||
|
||||
```typescript
|
||||
const [showBounds, setShowBounds] = useState(false);
|
||||
|
||||
// When calculation starts
|
||||
setShowBounds(true);
|
||||
|
||||
// After calculation completes
|
||||
setTimeout(() => setShowBounds(false), 3000); // Hide after 3s
|
||||
|
||||
{showBounds && <Rectangle ... />}
|
||||
```
|
||||
|
||||
### Option D: Progress Indicator Instead
|
||||
|
||||
Replace rectangle with corner markers:
|
||||
|
||||
```typescript
|
||||
// Instead of full rectangle, show 4 corner circles
|
||||
{calculationBounds && (
|
||||
<>
|
||||
<CircleMarker center={[minLat, minLon]} radius={3} color="#666" />
|
||||
<CircleMarker center={[maxLat, minLon]} radius={3} color="#666" />
|
||||
<CircleMarker center={[minLat, maxLon]} radius={3} color="#666" />
|
||||
<CircleMarker center={[maxLat, maxLon]} radius={3} color="#666" />
|
||||
</>
|
||||
)}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Recommended Fix
|
||||
|
||||
**File:** `frontend/src/components/map/Map.tsx` (or wherever Rectangle is)
|
||||
|
||||
```typescript
|
||||
// Find the calculation bounds Rectangle and replace with:
|
||||
{calculationInProgress && (
|
||||
<Rectangle
|
||||
bounds={calculationBounds}
|
||||
pathOptions={{
|
||||
color: '#3b82f6', // Blue
|
||||
weight: 1,
|
||||
opacity: 0.5,
|
||||
dashArray: '3, 3',
|
||||
fillOpacity: 0
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
// Auto-hide after calculation completes:
|
||||
useEffect(() => {
|
||||
if (!calculationInProgress && calculationBounds) {
|
||||
const timer = setTimeout(() => {
|
||||
setCalculationBounds(null);
|
||||
}, 2000);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [calculationInProgress]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
### Zoom Gradient:
|
||||
1. Calculate coverage at zoom 8
|
||||
2. Note color at specific location (e.g., 3km from site)
|
||||
3. Zoom to 10, 12, 14, 16
|
||||
4. Color at same location should NOT change
|
||||
5. Check console - maxIntensity should always be 0.75
|
||||
|
||||
### Calculation Square:
|
||||
1. Click "Calculate Coverage"
|
||||
2. Rectangle should be subtle (thin, dashed, gray)
|
||||
3. OR auto-hide after 2-3 seconds
|
||||
4. Should not distract from heatmap
|
||||
|
||||
---
|
||||
|
||||
## Quick Apply
|
||||
|
||||
**For Claude Code:**
|
||||
|
||||
```
|
||||
Fix two remaining issues:
|
||||
|
||||
1. Heatmap zoom gradient:
|
||||
- Ensure maxIntensity is CONSTANT 0.75 (not a formula)
|
||||
- Remove ALL zoom-dependent parameters from HeatmapLayer
|
||||
- Make radius/blur/max all fixed values
|
||||
- Test: same location = same color at any zoom
|
||||
|
||||
2. Calculation bounds rectangle:
|
||||
- Make it subtle: gray, thin (weight: 1), dashed, opacity: 0.3
|
||||
- OR auto-hide 2 seconds after calculation completes
|
||||
- Should not distract from coverage heatmap
|
||||
|
||||
Test gradient thoroughly at zoom levels 8, 10, 12, 14, 16.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build & Test
|
||||
|
||||
```bash
|
||||
cd /opt/rfcp/frontend
|
||||
npm run build
|
||||
sudo systemctl reload caddy
|
||||
|
||||
# Open https://rfcp.eliah.one
|
||||
# Test zoom gradient (critical!)
|
||||
# Check calculation bounds visibility
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Віддати на Claude Code ці 2 фікси? Після цього можна братись за Backend! 🚀
|
||||
Reference in New Issue
Block a user