from typing import Literal from pydantic import BaseModel, Field from app.models.common import AntennaPoint, DataSources, GeoJSON, Point, SurfaceKind class ElevationResponse(Point): elevation_m: float surface: SurfaceKind data_sources: DataSources = Field(default_factory=DataSources) class TerrainProfileRequest(BaseModel): start: Point end: Point samples: int = Field(default=512, ge=2, le=2048) include_buildings: bool = True include_canopy: bool = True k_factor: float = Field(default=1.333, gt=0) class ProfileSample(BaseModel): i: int lat: float lon: float distance_m: float ground_m: float building_m: float canopy_m: float surface_m: float curvature_corr_m: float class TerrainProfileResponse(BaseModel): distance_m: float samples: list[ProfileSample] path_geojson: GeoJSON data_sources: DataSources = Field(default_factory=DataSources) class LosRequest(BaseModel): tx: AntennaPoint rx: AntennaPoint frequency_mhz: float = Field(gt=0) fresnel_clearance: float = Field(default=0.6, ge=0) include_buildings: bool = True include_canopy: bool = True include_vegetation: bool = True k_factor: float = Field(default=1.333, gt=0) samples: int = Field(default=512, ge=2, le=2048) class FresnelSliceRequest(BaseModel): tx: AntennaPoint rx: AntennaPoint frequency_mhz: float = Field(gt=0) fresnel_fraction: float = Field(default=0.6, ge=0) include_buildings: bool = True include_canopy: bool = True k_factor: float = Field(default=1.333, gt=0) samples: int = Field(default=512, ge=2, le=2048) building_limit: int = Field(default=5000, gt=0) class Obstruction(BaseModel): distance_m: float clearance_m: float fresnel_radius_m: float required_clearance_m: float type: Literal["terrain", "building", "canopy"] class FresnelProfileSample(BaseModel): i: int lat: float lon: float distance_m: float surface_m: float path_height_m: float obstacle_height_m: float clearance_m: float fresnel_radius_m: float required_clearance_m: float fresnel_upper_60pct_m: float fresnel_upper_100pct_m: float fresnel_lower_60pct_m: float fresnel_lower_100pct_m: float type: Literal["terrain", "building", "canopy"] class LosResponse(BaseModel): los_clear: bool geometric_los: bool first_fresnel_clearance_pct: float worst_obstruction: Obstruction | None obstructions: list[Obstruction] geometric_obstructions: list[Obstruction] fresnel_profile: list[FresnelProfileSample] fresnel_violations_count: int geometric_obstructions_count: int building_obstructions_count: int diffraction_loss_db: float vegetation_loss_db: float data_sources: DataSources = Field(default_factory=DataSources) profile_ref: str class FresnelSliceResponse(BaseModel): path_geojson: GeoJSON fresnel_geojson: GeoJSON buildings_geojson: GeoJSON profile: list[FresnelProfileSample] data_sources: DataSources = Field(default_factory=DataSources)