@mytec: iter4 ready for test

This commit is contained in:
2026-01-30 11:55:21 +02:00
parent 201aeeabd6
commit 641832bc7b
6 changed files with 100 additions and 6 deletions

View File

@@ -81,6 +81,25 @@ export default function Heatmap({ points, visible, opacity = 0.7 }: HeatmapProps
const { radius, blur, maxIntensity } = getHeatmapParams(mapZoom);
// Debug: log RSRP stats and heatmap params
if (import.meta.env.DEV) {
const rsrpValues = points.map((p) => p.rsrp);
const intensityValues = heatData.map((d) => d[2]);
console.log('Heatmap Debug:', {
pointCount: points.length,
rsrpMin: Math.min(...rsrpValues).toFixed(1),
rsrpMax: Math.max(...rsrpValues).toFixed(1),
rsrpSample: rsrpValues.slice(0, 5).map((v) => v.toFixed(1)),
intensityMin: Math.min(...intensityValues).toFixed(3),
intensityMax: Math.max(...intensityValues).toFixed(3),
mapZoom,
radius,
blur,
maxIntensity: maxIntensity.toFixed(2),
opacity,
});
}
const heatLayer = L.heatLayer(heatData, {
radius,
blur,

View File

@@ -44,6 +44,7 @@ export default function MapView({ onMapClick, onEditSite, children }: MapViewPro
const sites = useSitesStore((s) => s.sites);
const isPlacingMode = useSitesStore((s) => s.isPlacingMode);
const showTerrain = useSettingsStore((s) => s.showTerrain);
const terrainOpacity = useSettingsStore((s) => s.terrainOpacity);
const setShowTerrain = useSettingsStore((s) => s.setShowTerrain);
const mapRef = useRef<LeafletMap | null>(null);
@@ -70,12 +71,13 @@ export default function MapView({ onMapClick, onEditSite, children }: MapViewPro
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{/* Terrain overlay (OpenTopoMap, semi-transparent when enabled) */}
{/* Terrain overlay (OpenTopoMap, above base map, below heatmap) */}
{showTerrain && (
<TileLayer
attribution='Map data: &copy; OpenStreetMap, SRTM | Style: &copy; <a href="https://opentopomap.org">OpenTopoMap</a>'
url="https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png"
opacity={0.6}
opacity={terrainOpacity}
zIndex={100}
/>
)}
<MapClickHandler onMapClick={onMapClick} />

View File

@@ -1,4 +1,4 @@
import { Marker, Popup, useMap } from 'react-leaflet';
import { Marker, Popup, Polygon, useMap } from 'react-leaflet';
import L from 'leaflet';
import type { Site } from '@/types/index.ts';
import { useSitesStore } from '@/store/sites.ts';
@@ -27,6 +27,34 @@ function createSiteIcon(color: string, isSelected: boolean): L.DivIcon {
});
}
/**
* Generate polygon points for a sector antenna wedge visualization.
* Creates an arc from the site center spanning the beamwidth around the azimuth.
*/
function generateSectorWedge(site: Site): [number, number][] {
const points: [number, number][] = [[site.lat, site.lon]];
const radius = 0.5; // km visual radius on map
const beamwidth = site.beamwidth || 65;
const azimuth = site.azimuth || 0;
const startAngle = azimuth - beamwidth / 2;
const endAngle = azimuth + beamwidth / 2;
// Generate arc points every 5 degrees
for (let angle = startAngle; angle <= endAngle; angle += 5) {
const rad = (angle * Math.PI) / 180;
// North = 0°, East = 90° — use sin for lon offset, cos for lat offset
const latOffset = (radius / 111) * Math.cos(rad);
const lonOffset =
(radius / (111 * Math.cos((site.lat * Math.PI) / 180))) * Math.sin(rad);
points.push([site.lat + latOffset, site.lon + lonOffset]);
}
// Close the wedge back to origin
points.push([site.lat, site.lon]);
return points;
}
function FlyToSelected({ site, isSelected }: { site: Site; isSelected: boolean }) {
const map = useMap();
useEffect(() => {
@@ -83,6 +111,19 @@ export default function SiteMarker({ site, onEdit }: SiteMarkerProps) {
</div>
</Popup>
</Marker>
{site.antennaType === 'sector' && (
<Polygon
positions={generateSectorWedge(site)}
pathOptions={{
color: site.color,
weight: 2,
opacity: 0.6,
fillColor: site.color,
fillOpacity: 0.1,
dashArray: '5, 5',
}}
/>
)}
</>
);
}