@mytec: iter10.3.2 start

This commit is contained in:
2026-01-30 18:53:19 +02:00
parent 176df9ddaf
commit 1d1f0c6a8a

View File

@@ -0,0 +1,207 @@
# RFCP Iteration 10.3.2 — Fix Coverage Boundary Rendering
**Date:** 2025-01-30
**Status:** Ready for Implementation
**Priority:** High
**Estimated Effort:** 30-45 minutes
---
## Problem Statement
CoverageBoundary component renders but is nearly invisible:
- Boundary polygon is only **25x25 pixels** instead of covering the entire coverage area
- Edge detection algorithm returns only **~11 points** instead of hundreds
- Path exists in DOM with correct styling but wrong geometry
### Debug Evidence
```
[CoverageBoundary] Computing: {visible: true, pointsCount: 9132, resolution: 200}
[CoverageBoundary] Paths: 1
```
```javascript
document.querySelectorAll('.leaflet-overlay-pane path')[1].getBoundingClientRect()
// → DOMRect {width: 25, height: 25} // Should be ~500x500 or larger!
// Path has only 11 vertices:
// M780 422L774 422L768 416L768 402L773 397L787 397L793 403L793 415L791 419L786 422L780 422
```
---
## Root Cause Analysis
In `/opt/rfcp/frontend/src/components/map/CoverageBoundary.tsx`, function `computeEdgePath()`:
1. **Grid cell size too coarse:**
- Cell size = `resolution` (200m) converted to degrees
- With 9132 points spread over coverage area, most cells have neighbors
- Only ~11 cells on the very edge are detected as "boundary"
2. **Angular sorting from centroid fails for sector shapes:**
- Coverage is a sector (wedge), not a circle
- Sorting by angle from centroid produces zigzag paths for concave shapes
3. **Single representative point per cell:**
- Algorithm picks one point per grid cell
- Loses boundary detail when cells are large
---
## Solution: Use Turf.js Concave Hull
Replace custom edge detection with `@turf/concave` — purpose-built for this exact use case.
### Why Turf.js?
- **Concave hull** follows actual shape (sectors, irregular coverage)
- **Battle-tested** library used in GIS applications
- **Configurable** maxEdge parameter controls detail level
- **Fast** — optimized for thousands of points
---
## Implementation Plan
### Step 1: Install Dependencies
```bash
cd /opt/rfcp/frontend
npm install @turf/concave @turf/helpers
```
### Step 2: Rewrite computeEdgePath()
Replace the entire `computeEdgePath` function with:
```typescript
import concave from '@turf/concave';
import { featureCollection, point } from '@turf/helpers';
/**
* Compute concave hull boundary for coverage points.
* Uses Turf.js concave hull algorithm (alpha shape).
*
* @param pts - Coverage points for one site
* @param resolutionM - Resolution in meters, used to set maxEdge
* @returns Ordered boundary coordinates for Leaflet polyline
*/
function computeEdgePath(
pts: CoveragePoint[],
resolutionM: number
): L.LatLngExpression[] {
if (pts.length < 3) return [];
// Convert to GeoJSON points
const features = pts.map(p => point([p.lon, p.lat]));
const fc = featureCollection(features);
// Compute concave hull
// maxEdge in kilometers — use resolution * 3 for good detail
const maxEdge = (resolutionM * 3) / 1000;
try {
const hull = concave(fc, { maxEdge, units: 'kilometers' });
if (!hull || hull.geometry.type !== 'Polygon') {
console.warn('[CoverageBoundary] Concave hull failed, falling back to convex');
return [];
}
// Extract coordinates (GeoJSON is [lon, lat], Leaflet needs [lat, lon])
const coords = hull.geometry.coordinates[0];
return coords.map(([lon, lat]) => [lat, lon] as L.LatLngExpression);
} catch (error) {
console.error('[CoverageBoundary] Hull computation error:', error);
return [];
}
}
```
### Step 3: Update Imports at Top of File
```typescript
import { useEffect, useRef, useMemo } from 'react';
import { useMap } from 'react-leaflet';
import L from 'leaflet';
import concave from '@turf/concave';
import { featureCollection, point } from '@turf/helpers';
import type { CoveragePoint } from '@/types/index.ts';
```
### Step 4: Filter Points by Threshold
**Important:** Currently CoverageBoundary receives ALL points, but heatmap filters by `rsrpThreshold`. Boundary should match heatmap edge.
In `App.tsx`, filter points before passing:
```tsx
<CoverageBoundary
points={coverageResult.points.filter(p => p.rsrp >= settings.rsrpThreshold)}
visible={heatmapVisible}
resolution={settings.resolution}
/>
```
### Step 5: Remove Debug Logging
After confirming fix works, remove the `console.log` statements added during debugging.
---
## Files to Modify
| File | Changes |
|------|---------|
| `package.json` | Add @turf/concave, @turf/helpers |
| `src/components/map/CoverageBoundary.tsx` | Replace computeEdgePath with Turf.js implementation |
| `src/App.tsx` | Filter coverage points by rsrpThreshold |
---
## Expected Results
### Before (Current Bug)
- Boundary: 25x25 pixels, ~11 vertices
- Not visible at normal zoom
### After (Fixed)
- Boundary: Follows heatmap edge closely
- Purple dashed line (#7c3aed) visible around orange gradient
- Updates when Min Signal slider changes
- Properly handles sector shapes (wedges, not just circles)
---
## Testing Checklist
- [ ] Boundary visible around coverage area
- [ ] Boundary follows actual coverage shape (sector/wedge)
- [ ] Boundary updates when resolution changes
- [ ] Boundary updates when Min Signal threshold changes
- [ ] No console errors
- [ ] Performance acceptable (< 100ms for 10k points)
---
## Rollback Plan
If Turf.js causes issues, revert to previous edge detection but with smaller grid cells:
```typescript
// In computeEdgePath, change:
const cellLat = resolutionM / 111_000;
// To:
const cellLat = (resolutionM / 4) / 111_000; // 4x finer grid
```
---
## Reference
- Turf.js concave: https://turfjs.org/docs/#concave
- Leaflet polyline: https://leafletjs.com/reference.html#polyline
- Previous iteration: RFCP-Iteration10.3.1-Threshold-Filter-Fix.md