@mytec: iter1.3 ready for test
This commit is contained in:
104
backend/app/api/routes/coverage.py
Normal file
104
backend/app/api/routes/coverage.py
Normal file
@@ -0,0 +1,104 @@
|
||||
from fastapi import APIRouter, HTTPException, BackgroundTasks
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel
|
||||
from app.services.coverage_service import (
|
||||
coverage_service,
|
||||
CoverageSettings,
|
||||
SiteParams,
|
||||
CoveragePoint
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
class CoverageRequest(BaseModel):
|
||||
"""Request body for coverage calculation"""
|
||||
sites: List[SiteParams]
|
||||
settings: CoverageSettings = CoverageSettings()
|
||||
|
||||
|
||||
class CoverageResponse(BaseModel):
|
||||
"""Coverage calculation response"""
|
||||
points: List[CoveragePoint]
|
||||
count: int
|
||||
settings: CoverageSettings
|
||||
stats: dict
|
||||
|
||||
|
||||
@router.post("/calculate")
|
||||
async def calculate_coverage(request: CoverageRequest) -> CoverageResponse:
|
||||
"""
|
||||
Calculate RF coverage for one or more sites
|
||||
|
||||
Returns grid of RSRP values with terrain and building effects
|
||||
"""
|
||||
if not request.sites:
|
||||
raise HTTPException(400, "At least one site required")
|
||||
|
||||
if len(request.sites) > 10:
|
||||
raise HTTPException(400, "Maximum 10 sites per request")
|
||||
|
||||
# Validate settings
|
||||
if request.settings.radius > 50000:
|
||||
raise HTTPException(400, "Maximum radius 50km")
|
||||
|
||||
if request.settings.resolution < 50:
|
||||
raise HTTPException(400, "Minimum resolution 50m")
|
||||
|
||||
# Calculate
|
||||
if len(request.sites) == 1:
|
||||
points = await coverage_service.calculate_coverage(
|
||||
request.sites[0],
|
||||
request.settings
|
||||
)
|
||||
else:
|
||||
points = await coverage_service.calculate_multi_site_coverage(
|
||||
request.sites,
|
||||
request.settings
|
||||
)
|
||||
|
||||
# Calculate stats
|
||||
rsrp_values = [p.rsrp for p in points]
|
||||
los_count = sum(1 for p in points if p.has_los)
|
||||
|
||||
stats = {
|
||||
"min_rsrp": min(rsrp_values) if rsrp_values else 0,
|
||||
"max_rsrp": max(rsrp_values) if rsrp_values else 0,
|
||||
"avg_rsrp": sum(rsrp_values) / len(rsrp_values) if rsrp_values else 0,
|
||||
"los_percentage": (los_count / len(points) * 100) if points else 0,
|
||||
"points_with_buildings": sum(1 for p in points if p.building_loss > 0),
|
||||
"points_with_terrain_loss": sum(1 for p in points if p.terrain_loss > 0),
|
||||
}
|
||||
|
||||
return CoverageResponse(
|
||||
points=points,
|
||||
count=len(points),
|
||||
settings=request.settings,
|
||||
stats=stats
|
||||
)
|
||||
|
||||
|
||||
@router.get("/buildings")
|
||||
async def get_buildings(
|
||||
min_lat: float,
|
||||
min_lon: float,
|
||||
max_lat: float,
|
||||
max_lon: float
|
||||
):
|
||||
"""
|
||||
Get buildings in bounding box (for debugging/visualization)
|
||||
"""
|
||||
from app.services.buildings_service import buildings_service
|
||||
|
||||
# Limit bbox size
|
||||
if (max_lat - min_lat) > 0.1 or (max_lon - min_lon) > 0.1:
|
||||
raise HTTPException(400, "Bbox too large (max 0.1 degrees)")
|
||||
|
||||
buildings = await buildings_service.fetch_buildings(
|
||||
min_lat, min_lon, max_lat, max_lon
|
||||
)
|
||||
|
||||
return {
|
||||
"count": len(buildings),
|
||||
"buildings": [b.model_dump() for b in buildings]
|
||||
}
|
||||
Reference in New Issue
Block a user