Skip to content

Commit

Permalink
navaids: show COP aids
Browse files Browse the repository at this point in the history
  • Loading branch information
a3li committed Oct 23, 2023
1 parent 90b2a20 commit aba9abb
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 1 deletion.
5 changes: 4 additions & 1 deletion atciss-frontend/src/app/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ecfmpApi } from "../services/ecfmpApi"
import { eventApi } from "../services/eventApi"
import { areaApi } from "../services/areaApi"
import { bookingApi } from "../services/bookingApi"
import { navaidApi } from "../services/navaidApi"

export const store = configureStore({
reducer: {
Expand All @@ -31,6 +32,7 @@ export const store = configureStore({
[ecfmpApi.reducerPath]: ecfmpApi.reducer,
[eventApi.reducerPath]: eventApi.reducer,
[areaApi.reducerPath]: areaApi.reducer,
[navaidApi.reducerPath]: navaidApi.reducer,
auth: authReducer,
activePositions: activePositionReducer,
config: configReducer,
Expand All @@ -49,7 +51,8 @@ export const store = configureStore({
.concat(controllerApi.middleware)
.concat(ecfmpApi.middleware)
.concat(areaApi.middleware)
.concat(eventApi.middleware),
.concat(eventApi.middleware)
.concat(navaidApi.middleware),
})

setupListeners(store.dispatch)
Expand Down
24 changes: 24 additions & 0 deletions atciss-frontend/src/components/map/NavaidLayer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import L from "leaflet"
import { LayerGroup, Marker } from "react-leaflet"
import { NavaidMarker } from "./NavaidMarker"
import { navaidApi } from "../../services/navaidApi"
import { loaApi, selectLoaCops } from "../../services/loaApi"
import { useAppSelector } from "../../app/hooks"
import { selectOwnedSectors } from "../../services/activePositionSlice"

export const NavaidLayer = () => {
const ownedSectors = useAppSelector(selectOwnedSectors)
loaApi.useGetBySectorsQuery(ownedSectors, {
skip: ownedSectors.length == 0,
})
const cops = useAppSelector(selectLoaCops)
navaidApi.useGetByDesignatorsQuery(cops)

return (
<LayerGroup>
{cops.map((cop) => (
<NavaidMarker key={cop} designator={cop} />
))}
</LayerGroup>
)
}
54 changes: 54 additions & 0 deletions atciss-frontend/src/components/map/NavaidMarker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import L, { LatLngExpression } from "leaflet"
import { Marker } from "react-leaflet"
import { selectLOANavaid } from "../../services/navaidApi"
import { useAppSelector } from "../../app/hooks"

const icons: { [key: string]: string } = {
rnav: `<g><path fill="#FFFFFF" d="M32,27.4l10.5,18.1h-21L32,27.4 M32,9.4L5.9,54.6h52.1L32,9.4L32,9.4z"/><path d="M32,21.4l15.7,27.1H16.3L32,21.4 M32,9.4L5.9,54.6h52.1L32,9.4L32,9.4z"/><path fill="#FFFFFF" d="M32,9.4l26.1,45.1H5.9L32,9.4 M32,3.4l-2.6,4.5L3.3,53.1l-2.6,4.5h5.2h52.1h5.2l-2.6-4.5L34.6,7.9L32,3.4 L32,3.4z"/></g>`,
vor_dme: `
<g><path d="M52.3,16v33H11.7V16H52.3 M58.3,10H5.7v45h52.7V10L58.3,10z"/><path fill="#FFFFFF" d="M58.3,10v45H5.7V10H58.3 M61.3,7h-3H5.7h-3v3v45v3h3h52.7h3v-3V10V7L61.3,7z"/></g>
<g><path fill="#FFFFFF" d="M41.4,15.9l9.5,16.5l-9.5,16.5h-19l-9.5-16.5l9.5-16.5H41.4 M44.9,9.9h-26L6,32.4l13,22.5h26l13-22.5 L44.9,9.9L44.9,9.9z"/><path d="M41.4,15.9l9.5,16.5l-9.5,16.5h-19l-9.5-16.5l9.5-16.5H41.4 M44.9,9.9h-26L6,32.4l13,22.5h26l13-22.5L44.9,9.9L44.9,9.9z" /></g>
<g><path fill="#FFFFFF" d="M31.5,29c1.9,0,3.5,1.6,3.5,3.5c0,1.9-1.6,3.5-3.5,3.5c-1.9,0-3.5-1.6-3.5-3.5C28,30.6,29.6,29,31.5,29 M31.5,26c-3.6,0-6.5,2.9-6.5,6.5s2.9,6.5,6.5,6.5s6.5-2.9,6.5-6.5S35.1,26,31.5,26L31.5,26z"/><circle cx="31.5" cy="32.5" r="3.5"/></g>
`,
ndb: `
<g opacity="0.39"><ellipse fill="#FFFFFF" cx="32" cy="32" rx="30.8" ry="29.8"/></g>
<g><circle cx="32" cy="32" r="10.3"/><path fill="#FFFFFF" d="M32,23.2c4.8,0,8.8,3.9,8.8,8.8s-3.9,8.8-8.8,8.8s-8.8-3.9-8.8-8.8S27.2,23.2,32,23.2 M32,20.2 c-6.5,0-11.8,5.3-11.8,11.8S25.5,43.8,32,43.8S43.8,38.5,43.8,32S38.5,20.2,32,20.2L32,20.2z"/></g>`,
}

const getIcon = (type: string, label: string) => {
let html = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">`

type = type.toLowerCase()

if (Object.keys(icons).includes(type)) {
html += icons[type]
} else {
html += icons["rnav"] // TODO: Fix ICAO, Terminal, etc.
}

html += `</svg><div class="navaid-label">${label}</div>` // TODO: Sanitize

return L.divIcon({
html: html,
className: `navaid navaid-${type}`,
iconSize: [14, 14],
iconAnchor: [7, 7],
})
}

export type NavaidMarkerProps = {
designator: string
}

export const NavaidMarker = ({ designator }: NavaidMarkerProps) => {
const navaid = useAppSelector((store) => selectLOANavaid(store, designator))

return (
navaid && (
<Marker
position={navaid.location as LatLngExpression}
icon={getIcon(navaid.type, navaid.designator)}
/>
)
)
}
44 changes: 44 additions & 0 deletions atciss-frontend/src/services/navaidApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { createApi } from "@reduxjs/toolkit/query/react"
import { fetchWithAuth } from "../app/auth"
import { RootState } from "../app/store"
import { createSelector } from "@reduxjs/toolkit"
import createCachedSelector from "re-reselect"
import { selectLoaCops } from "./loaApi"

export interface Navaid {
id: string
designator: string
name: string
type: string
location: number[]
channel?: string
frequency?: number
aerodrome?: string
remark?: string
operation_remark?: string
}

export const navaidApi = createApi({
reducerPath: "navaid",
baseQuery: fetchWithAuth,
endpoints: (builder) => ({
getByDesignators: builder.query<Navaid[], string[]>({
query: (designatorList) => ({
url: `navaid`,
params: designatorList.map((desig) => ["id", desig]),
}),
}),
}),
})

const selectLOANavaids = createSelector(
(state: RootState) => state,
selectLoaCops,
(state, navaids) =>
navaidApi.endpoints.getByDesignators.select(navaids)(state)?.data ?? [],
)

export const selectLOANavaid = createCachedSelector(
[selectLOANavaids, (_state: RootState, designator: string) => designator],
(navaids, designator) => navaids.find((n) => n.designator == designator),
)((_state, designator) => designator)
2 changes: 2 additions & 0 deletions atciss-frontend/src/views/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { BackgroundTiles } from "../components/map/BackgroundTiles"
import { SectorLayer } from "../components/map/SectorLayer"
import { AerodromeLayer } from "../components/map/AerodromeLayer"
import { AreaLayer } from "../components/map/AreaLayer"
import { NavaidLayer } from "../components/map/NavaidLayer"

const position = [49.2646, 11.4134] as LatLngTuple

Expand All @@ -30,6 +31,7 @@ const Map = ({ sx }: { sx?: ThemeUIStyleObject }) => (
<BackgroundTiles />
<SectorLayer />
<AreaLayer />
<NavaidLayer />
<AerodromeLayer />
</MapContainer>
<Flex
Expand Down
30 changes: 30 additions & 0 deletions atciss/app/controllers/navaid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from typing import Annotated, Sequence
from fastapi import APIRouter, Depends, Query
from sqlmodel import select
from fastapi_async_sqlalchemy import db

from atciss.app.controllers.auth import get_user
from atciss.app.views.navaid import NavaidModel

from ..views.booking import Booking

from ..models import Navaid, User

router = APIRouter()


@router.get("/navaid")
async def get_naviads(
navaids: Annotated[Sequence[str], Query(alias="id")],
_: Annotated[User, Depends(get_user)],
) -> Sequence[NavaidModel]:
aids = []

for aid in navaids:
async with db():
stmt = select(Navaid).where(Navaid.designator == aid)
results = await db.session.execute(stmt)
mapped_results = [NavaidModel.from_db(a) for a in results.scalars().all()]
aids.extend(mapped_results)

return aids
2 changes: 2 additions & 0 deletions atciss/app/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
notam,
taf,
vatsim,
navaid,
)


Expand All @@ -29,6 +30,7 @@
root_api_router.include_router(areas.router, tags=["airspace"])
root_api_router.include_router(atis.router, tags=["aerodrome"])
root_api_router.include_router(booking.router, tags=["vatsim"])
root_api_router.include_router(navaid.router, tags=["navaid"])
root_api_router.include_router(ecfmp.router, tags=["airspace"])
root_api_router.include_router(loa.router, tags=["airspace"])
root_api_router.include_router(metar.router, tags=["wx"])
Expand Down
38 changes: 38 additions & 0 deletions atciss/app/views/navaid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from dataclasses import dataclass
from typing import Optional, Sequence
from uuid import UUID

from geoalchemy2.shape import to_shape

from atciss.app.models import Navaid


@dataclass
class NavaidModel:
id: UUID
designator: str
name: str
type: str
location: Sequence[float]
channel: Optional[str]
frequency: Optional[float]
aerodrome: Optional[str]
remark: Optional[str]
operation_remark: Optional[str]

@classmethod
def from_db(cls, aid: Navaid) -> "NavaidModel":
point = to_shape(aid.location)

return cls(
id=aid.id,
designator=aid.designator,
name=aid.name,
type=aid.type,
location=[point.y, point.x],
channel=aid.channel,
frequency=aid.frequency,
aerodrome=None,
remark=aid.remark,
operation_remark=aid.operation_remark,
)

0 comments on commit aba9abb

Please sign in to comment.