diff --git a/atciss-frontend/src/components/map/AerodromeLayer.tsx b/atciss-frontend/src/components/map/AerodromeLayer.tsx index bc1d6bfa..13d645fc 100644 --- a/atciss-frontend/src/components/map/AerodromeLayer.tsx +++ b/atciss-frontend/src/components/map/AerodromeLayer.tsx @@ -49,7 +49,10 @@ export const AerodromeLayer = () => { {allADs.map((ad) => { const station = data?.airports?.[ad]?.topdown?.find( - (pos) => activePositions[pos]?.[syncedToOnline ? "online" : "manual"], + (pos) => + // TODO don't ignore rwy-dependent topdown + typeof pos === "string" && + activePositions[pos]?.[syncedToOnline ? "online" : "manual"], ) const metar = metars?.[ad] const taf = tafs?.[ad] @@ -81,7 +84,7 @@ export const AerodromeLayer = () => { > {ad} - {station ? ( + {typeof station === "string" ? ( <> by {station} diff --git a/atciss-frontend/src/components/map/SectorLayer.tsx b/atciss-frontend/src/components/map/SectorLayer.tsx index 7b6a4ae5..7ee835d5 100644 --- a/atciss-frontend/src/components/map/SectorLayer.tsx +++ b/atciss-frontend/src/components/map/SectorLayer.tsx @@ -8,6 +8,7 @@ import { selectLevel, selectSectors } from "../../services/mapSlice" import { Sector, sectorApi } from "../../services/airspaceApi" import { usePollControllers } from "../../services/controllerApi" import { SectorPolygon } from "./SectorPolygon" +import { usePollAtisByIcaoCodes } from "../../services/atisApi" export const SectorLayer = () => { const { data } = sectorApi.useGetByRegionQuery() @@ -18,8 +19,22 @@ export const SectorLayer = () => { const level = useAppSelector(selectLevel) const displaySectors = useAppSelector(selectSectors) + + const { data: atis } = usePollAtisByIcaoCodes( + Object.keys(data?.airports ?? {}), + ) + const levelFilter = (s: Sector) => (s.min ?? 0) <= level && level < (s.max ?? 999) + const rwyFilter = (s: Sector) => + s.runways.length === 0 || + s.runways.some((rwy) => { + const rwysInUse = atis?.[rwy.icao]?.runways_in_use ?? [] + const rwyPriority = data?.airports?.[rwy.icao].runways ?? [] + return rwysInUse.length > 0 + ? rwysInUse.some((activeRwy) => activeRwy.startsWith(rwy.runway)) + : rwyPriority[0] === rwy.runway + }) const sectors = data?.airspace .filter((a) => a.sectors.some(levelFilter)) @@ -27,7 +42,7 @@ export const SectorLayer = () => { name, remark, owner, - sectors: sectors.filter(levelFilter), + sectors: sectors.filter(levelFilter).filter(rwyFilter), })) return ( diff --git a/atciss-frontend/src/services/airspaceApi.ts b/atciss-frontend/src/services/airspaceApi.ts index 5b6091a9..9615fe0e 100644 --- a/atciss-frontend/src/services/airspaceApi.ts +++ b/atciss-frontend/src/services/airspaceApi.ts @@ -2,10 +2,16 @@ import { createApi } from "@reduxjs/toolkit/query/react" import { fetchWithAuth } from "../app/auth" import { LatLngExpression } from "leaflet" +export type Runway = { + icao: string + runway: string +} + export type Sector = { points: LatLngExpression[] min: number | null max: number | null + runways: Runway[] } export type Airspace = { @@ -16,10 +22,16 @@ export type Airspace = { sectors: Sector[] } +export type RwyDependentTopDown = { + runway: Runway + topdown: string[] +} + export type Airport = { callsign: string coord: LatLngExpression - topdown: string[] + topdown: (string | RwyDependentTopDown)[] + runways: string[] } export type Position = { diff --git a/atciss/app/views/sector.py b/atciss/app/views/sector.py index 20b03222..02ebc473 100644 --- a/atciss/app/views/sector.py +++ b/atciss/app/views/sector.py @@ -22,10 +22,17 @@ def convert_point(point: Tuple[str, str] | Coordinate) -> Coordinate: return cast(Coordinate, [convert_coordinate(cast(str, coord)) for coord in point]) +@dataclass +class Runway: + icao: str + runway: str + + class Sector(BaseModel): points: list[Coordinate] min: Optional[int] = None max: Optional[int] = None + runways: list[Runway] = field(default_factory=list) @field_validator("points", mode="before") @classmethod @@ -64,13 +71,7 @@ class Position: @dataclass -class Runway: - icao: str - runway: str - - -@dataclass -class RwyDependantTopDown: +class RwyDependentTopDown: runway: Runway topdown: list[str] @@ -79,7 +80,8 @@ class RwyDependantTopDown: class Airport: callsign: str coord: Coordinate - topdown: list[str | RwyDependantTopDown] = field(default_factory=list) + runways: list[str] = field(default_factory=list) + topdown: list[str | RwyDependentTopDown] = field(default_factory=list) @dataclass