Major refactoring of RFCP backend: - Modular propagation models (8 models) - SharedMemoryManager for terrain data - ProcessPoolExecutor parallel processing - WebSocket progress streaming - Building filtering pipeline (351k → 15k) - 82 unit tests Performance: Standard preset 38s → 5s (7.6x speedup) Known issue: Detailed preset timeout (fix in 3.1.0)
84 lines
2.4 KiB
Python
84 lines
2.4 KiB
Python
"""
|
|
Grid generation for coverage calculations.
|
|
"""
|
|
|
|
import numpy as np
|
|
from dataclasses import dataclass
|
|
from typing import List, Tuple
|
|
from app.geometry.haversine import haversine_distance
|
|
|
|
|
|
@dataclass
|
|
class BoundingBox:
|
|
min_lat: float
|
|
min_lon: float
|
|
max_lat: float
|
|
max_lon: float
|
|
|
|
|
|
@dataclass
|
|
class Grid:
|
|
points: List[Tuple[float, float]]
|
|
bounding_box: BoundingBox
|
|
resolution: float
|
|
radius: float
|
|
|
|
|
|
class GridService:
|
|
"""Generate coverage grid points."""
|
|
|
|
@staticmethod
|
|
def generate(
|
|
center_lat: float,
|
|
center_lon: float,
|
|
radius: float,
|
|
resolution: float,
|
|
) -> Grid:
|
|
points = []
|
|
|
|
lat_step = resolution / 111000
|
|
lon_step = resolution / (111000 * np.cos(np.radians(center_lat)))
|
|
|
|
lat_delta = radius / 111000
|
|
lon_delta = radius / (111000 * np.cos(np.radians(center_lat)))
|
|
|
|
bbox = BoundingBox(
|
|
min_lat=center_lat - lat_delta,
|
|
min_lon=center_lon - lon_delta,
|
|
max_lat=center_lat + lat_delta,
|
|
max_lon=center_lon + lon_delta,
|
|
)
|
|
|
|
lat = center_lat - lat_delta
|
|
while lat <= center_lat + lat_delta:
|
|
lon = center_lon - lon_delta
|
|
while lon <= center_lon + lon_delta:
|
|
dist = haversine_distance(center_lat, center_lon, lat, lon)
|
|
if dist <= radius:
|
|
points.append((lat, lon))
|
|
lon += lon_step
|
|
lat += lat_step
|
|
|
|
return Grid(points=points, bounding_box=bbox, resolution=resolution, radius=radius)
|
|
|
|
@staticmethod
|
|
def generate_multi_site(sites: list, radius: float, resolution: float) -> Grid:
|
|
all_points = set()
|
|
min_lat = min_lon = float("inf")
|
|
max_lat = max_lon = float("-inf")
|
|
|
|
for site in sites:
|
|
grid = GridService.generate(site.lat, site.lon, radius, resolution)
|
|
for p in grid.points:
|
|
all_points.add((round(p[0], 7), round(p[1], 7)))
|
|
min_lat = min(min_lat, grid.bounding_box.min_lat)
|
|
min_lon = min(min_lon, grid.bounding_box.min_lon)
|
|
max_lat = max(max_lat, grid.bounding_box.max_lat)
|
|
max_lon = max(max_lon, grid.bounding_box.max_lon)
|
|
|
|
return Grid(
|
|
points=list(all_points),
|
|
bounding_box=BoundingBox(min_lat, min_lon, max_lat, max_lon),
|
|
resolution=resolution, radius=radius,
|
|
)
|