Files
rfcp/RFCP-Iteration8.1-Clone-Coverage-Gaps.md
2026-01-30 14:21:53 +02:00

6.5 KiB

RFCP - Iteration 8.1: Clone Fix + Coverage Gaps

Issue 1: Clone Creates New Site (Critical!)

Problem: Clone button creates new site instead of adding sector to existing site.

Root Cause: cloneSector function in sites store creates new site object.

Solution

File: frontend/src/store/sites.ts

Current (WRONG):

const cloneSector = (siteId: string) => {
  const site = sites.find(s => s.id === siteId);
  const clone = { 
    ...site, 
    id: uuid(), 
    name: `${site.name}-clone` 
  };
  setSites([...sites, clone]); // Creates NEW site ❌
};

Fixed (CORRECT):

const cloneSector = (siteId: string) => {
  const site = sites.find(s => s.id === siteId);
  if (!site) return;
  
  // Get last sector as template
  const lastSector = site.sectors[site.sectors.length - 1];
  
  // Create new sector (NOT new site!)
  const newSector: Sector = {
    ...lastSector,
    id: `sector-${Date.now()}`,
    azimuth: (lastSector.azimuth + 120) % 360, // 120° offset for tri-sector
  };
  
  // Add sector to EXISTING site ✅
  updateSite(siteId, {
    sectors: [...site.sectors, newSector]
  });
  
  // Clear coverage to force recalculation
  useCoverageStore.getState().clearCoverage();
};

Update Button Label

File: frontend/src/components/panels/SiteList.tsx

<button onClick={() => cloneSector(site.id)}>
  + Add Sector {/* was: "Clone" */}
</button>

Issue 2: Coverage Gaps at 800m Wide

Problem: At 800m heatmap radius with 300m resolution, coverage points don't overlap enough → visible dots.

Why it happens:

  • Resolution: 300m (distance between coverage points)
  • Heatmap radius: 800m
  • At high zoom, 800m radius in pixels is HUGE
  • But point spacing (300m) stays same
  • Result: Gaps between points visible

Solution A: Warn User (Quick)

File: frontend/src/components/panels/CoverageSettings.tsx

<select 
  value={heatmapRadius} 
  onChange={(e) => setHeatmapRadius(Number(e.target.value))}
>
  <option value={200}>200m  Fast</option>
  <option value={400}>400m  Balanced</option>
  <option value={600}>600m  Smooth</option>
  <option value={800}>800m  Wide</option>
</select>

{/* Warning for 800m */}
{heatmapRadius === 800 && resolution > 200 && (
  <div className="warning">
    ⚠️ Wide radius works best with fine resolution (200m).
    Current: {resolution}m. Consider reducing for smoother coverage.
  </div>
)}

Solution B: Auto-adjust Resolution (Better)

File: frontend/src/store/coverage.ts

const calculateCoverage = async () => {
  // Auto-adjust resolution based on heatmap radius
  // Rule: resolution should be ≤ radius/2 for smooth coverage
  const recommendedResolution = Math.min(
    resolution,
    Math.floor(heatmapRadius / 2)
  );
  
  if (recommendedResolution < resolution) {
    console.log(`Auto-adjusting resolution: ${resolution}m → ${recommendedResolution}m for ${heatmapRadius}m radius`);
  }
  
  const effectiveResolution = recommendedResolution;
  
  // Calculate with adjusted resolution
  await worker.calculateCoverage({
    sites,
    radius,
    resolution: effectiveResolution,
    rsrpThreshold
  });
};

Solution C: Dynamic Point Sampling (Advanced)

File: frontend/src/components/map/HeatmapTileRenderer.ts

Add adaptive point sampling in renderer:

private drawPoint(
  intensityMap: Float32Array,
  point: CoveragePoint,
  centerX: number,
  centerY: number,
  radiusPixels: number
): void {
  // ... existing code
  
  // ADAPTIVE: If radius is very large, increase sampling
  const sampleFactor = radiusPixels > 100 ? 2 : 1;
  
  for (let y = minY; y < maxY; y += sampleFactor) {
    for (let x = minX; x < maxX; x += sampleFactor) {
      // ... draw with interpolation
    }
  }
}

Solution D: Clamp Max Radius (Safest)

File: frontend/src/components/panels/CoverageSettings.tsx

// Limit radius based on resolution
const maxAllowedRadius = resolution * 3; // 3x resolution max

<select 
  value={heatmapRadius} 
  onChange={(e) => {
    const newRadius = Number(e.target.value);
    if (newRadius > maxAllowedRadius) {
      toast.warning(`Radius ${newRadius}m too large for ${resolution}m resolution. Max: ${maxAllowedRadius}m`);
      return;
    }
    setHeatmapRadius(newRadius);
  }}
>
  <option value={200} disabled={resolution > 100}>200m  Fast</option>
  <option value={400} disabled={resolution > 200}>400m  Balanced</option>
  <option value={600} disabled={resolution > 300}>600m  Smooth</option>
  <option value={800} disabled={resolution > 400}>800m  Wide</option>
</select>

Issue 3: Coverage Not Cleared on Sector Delete

File: frontend/src/store/sites.ts

const removeSector = (siteId: string, sectorId: string) => {
  const site = sites.find(s => s.id === siteId);
  if (!site || site.sectors.length <= 1) {
    toast.error('Cannot remove last sector');
    return;
  }
  
  const updatedSectors = site.sectors.filter(s => s.id !== sectorId);
  updateSite(siteId, { sectors: updatedSectors });
  
  // CRITICAL: Clear coverage!
  useCoverageStore.getState().clearCoverage();
  toast.success('Sector removed. Recalculate coverage to update.');
};

Priority 1 (Critical):

  • Fix cloneSector to add sector, not create site
  • Update button label to "+ Add Sector"
  • Clear coverage on sector delete

Priority 2 (Important):

  • Add warning for 800m + 300m combo
  • OR auto-adjust resolution based on radius

Priority 3 (Nice to have):

  • Clamp max radius based on resolution
  • Dynamic point sampling

Testing

Clone Fix:

  1. Create site
  2. Click "+ Add Sector"
  3. Should show "Sites (1)" with 2 sectors
  4. NOT "Sites (2)"

Coverage Gaps:

  1. Set resolution 300m
  2. Set radius 800m
  3. Calculate coverage
  4. At high zoom (16+), check for dots
  5. If dots visible → show warning OR auto-adjust

Build & Deploy

cd /opt/rfcp/frontend
npm run build
sudo systemctl reload caddy

Commit Message

fix(sites): clone adds sector to existing site, not new site

- Fixed cloneSector to add sector to same site
- Changed button label to "+ Add Sector"
- Added coverage cache clear on sector delete
- Sites count now accurate (counts sites, not sectors)

fix(coverage): prevent gaps with 800m radius

- Added warning for wide radius + coarse resolution
- Auto-adjust resolution to radius/2 for smooth coverage
- Clear coverage cache on sector changes

🚀 Ready for 8.1!