import { memo } from 'react'; import type { CoveragePoint } from '@/types/index.ts'; interface CoverageStatsProps { points: CoveragePoint[]; resolution: number; // meters } /** * Estimate total coverage area from grid points. * Each point represents a resolution × resolution cell. */ function estimateAreaKm2(pointCount: number, resolutionM: number): number { const cellAreaM2 = resolutionM * resolutionM; return (pointCount * cellAreaM2) / 1_000_000; } const LEVELS = [ { label: 'Excellent', threshold: -70, color: 'bg-green-500' }, { label: 'Good', threshold: -85, color: 'bg-lime-500' }, { label: 'Fair', threshold: -100, color: 'bg-yellow-500' }, { label: 'Weak', threshold: -Infinity, color: 'bg-red-500' }, ] as const; function classifyPoints(points: CoveragePoint[]) { const counts = { excellent: 0, good: 0, fair: 0, weak: 0 }; for (const p of points) { if (p.rsrp > -70) counts.excellent++; else if (p.rsrp > -85) counts.good++; else if (p.rsrp > -100) counts.fair++; else counts.weak++; } return counts; } export default memo(function CoverageStats({ points, resolution }: CoverageStatsProps) { if (points.length === 0) { return (

Coverage Analysis

📊

No coverage data yet.

Press Ctrl+Enter to calculate.

); } const counts = classifyPoints(points); const totalArea = estimateAreaKm2(points.length, resolution); const total = points.length; // Use reduce instead of Math.min/max spread — spread crashes on 65k+ elements let minRSRP = Infinity; let maxRSRP = -Infinity; let sumRSRP = 0; for (const p of points) { if (p.rsrp < minRSRP) minRSRP = p.rsrp; if (p.rsrp > maxRSRP) maxRSRP = p.rsrp; sumRSRP += p.rsrp; } const avgRSRP = sumRSRP / total; // Unique sites contributing to coverage const uniqueSites = new Set(points.map((p) => p.siteId)).size; const levels = [ { ...LEVELS[0], count: counts.excellent }, { ...LEVELS[1], count: counts.good }, { ...LEVELS[2], count: counts.fair }, { ...LEVELS[3], count: counts.weak }, ]; return (

Coverage Analysis

{/* Summary stats */}
Total Area
{totalArea.toFixed(1)} km²
Grid Points
{total.toLocaleString()}
Avg RSRP
{avgRSRP.toFixed(1)} dBm
Sites
{uniqueSites}
{/* RSRP range */}
Range: {minRSRP.toFixed(1)} to {maxRSRP.toFixed(1)} dBm
{/* Signal quality breakdown */}
{levels.map((level) => { const pct = total > 0 ? (level.count / total) * 100 : 0; return (
{level.label} ({level.threshold === -Infinity ? '< -100' : `> ${level.threshold}`} dBm) {pct.toFixed(1)}%
); })}
); });