@mytec: iter4 ready for test
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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='© <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: © OpenStreetMap, SRTM | Style: © <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} />
|
||||
|
||||
@@ -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',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user