Skip to content

Commit c80db90

Browse files
feat: improved amdb
1 parent b58e130 commit c80db90

File tree

7 files changed

+344
-30
lines changed

7 files changed

+344
-30
lines changed

examples/playground/src/Root.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import MainWindow from "./MainWindow"
22
import SideBar from "./components/SideBar"
3-
import { Outlet, Route, Routes } from "react-router-dom"
3+
import { Route, Routes } from "react-router-dom"
44
import useUserUpdater from "./hooks/useUserUpdater"
55
import useAppConfigLoader from "./hooks/useAppConfigLoader"
66
import App from "./pages/App"
@@ -10,7 +10,7 @@ import Charts from "./pages/Charts"
1010
import Amdb from "./pages/Amdb"
1111
import Packages from "./pages/Packages"
1212

13-
function Root() {
13+
export default function Root() {
1414
useAppConfigLoader();
1515
useUserUpdater();
1616

@@ -25,10 +25,7 @@ function Root() {
2525
<Route path="/amdb/*" element={<Amdb />} />
2626
<Route path="/packages" element={<Packages />} />
2727
</Routes>
28-
<Outlet />
2928
<MainWindow />
3029
</main>
3130
)
3231
}
33-
34-
export default Root

examples/playground/src/components/map/amdb.tsx

Lines changed: 79 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,105 @@ import { amdbLayersState } from "../../state/amdb";
33
import { AmdbLayerName, getAmdbAPI } from "@navigraph/amdb";
44
import { userState } from "../../state/user";
55
import { Scope } from "@navigraph/app";
6-
import { memo } from "react";
6+
import { forwardRef, memo, useCallback, useRef } from "react";
77
import { useQuery } from "@tanstack/react-query";
8-
import { GeoJSON, Popup } from "react-leaflet";
8+
import { GeoJSON } from "react-leaflet";
9+
import { circleMarker, GeoJSON as LeafletGeoJson } from 'leaflet';
10+
import { renderToString } from "react-dom/server";
11+
import JsonView from "../JsonView";
12+
import amdbStyle, { layerOrder } from "./amdb_styles";
913

10-
const AmdbLayer = memo(({ idarpt, layer, amdb }: { idarpt: string, layer: AmdbLayerName, amdb: ReturnType<typeof getAmdbAPI> }) => {
14+
const AmdbLayer = memo(forwardRef<LeafletGeoJson, { idarpt: string, layer: AmdbLayerName, onClick?: (e: LeafletGeoJson) => void }>(({ idarpt, layer, onClick }, ref) => {
1115
const { data } = useQuery({
1216
queryKey: ['amdb-data', idarpt, layer],
1317
queryFn: async () => {
1418
return amdb.getAmdbLayer({ icao: idarpt, layer })
1519
}
1620
})
1721

22+
const amdb = getAmdbAPI();
23+
1824
if (!data) return null;
1925

2026
return (
21-
<GeoJSON data={data}>
22-
<Popup>
23-
{layer}
24-
</Popup>
25-
</GeoJSON>
27+
<GeoJSON
28+
ref={ref}
29+
data={data ?? {}}
30+
style={amdbStyle(layer)}
31+
pointToLayer={(feature, latlng) => {
32+
const marker = circleMarker(latlng, amdbStyle(layer)(feature));
33+
34+
marker.feature = feature;
35+
36+
return marker;
37+
}}
38+
onEachFeature={(feature, _layer) => {
39+
_layer.on('click', (e) => {
40+
onClick?.(e.target);
41+
});
42+
43+
if (feature.properties) {
44+
_layer.bindPopup(renderToString(
45+
<div className="flex flex-col items-center gap-2">
46+
<span className="text-ng-background-200">{layer}</span>
47+
<JsonView content={feature.properties} />
48+
</div>
49+
))
50+
}
51+
}}
52+
/>
53+
2654
)
27-
});
55+
}));
2856

2957
export default function AmdbManager() {
3058
const amdbLayers = useRecoilValue(amdbLayersState);
3159

3260
const user = useRecoilValue(userState);
3361

62+
const refs = useRef<Record<string, Partial<Record<AmdbLayerName, LeafletGeoJson>>>>({});
63+
64+
const updateOrder = useCallback(() => {
65+
Object.values(refs.current).forEach((layer) => {
66+
[...Object.entries(layer)].sort((a, b) => layerOrder[b[0] as AmdbLayerName] - layerOrder[a[0] as AmdbLayerName]).forEach((layer) => layer[1]?.bringToFront())
67+
});
68+
}, [refs.current]);
69+
3470
if (!user?.scope.includes(Scope.AMDB)) return;
3571

36-
const amdb = getAmdbAPI();
72+
return amdbLayers.flatMap(([idarpt, layers]) =>
73+
[...layers, 'aerodromereferencepoint' as const satisfies AmdbLayerName].map((layer) => (
74+
<AmdbLayer
75+
ref={(_layer) => {
76+
if (!refs.current[idarpt]) {
77+
refs.current[idarpt] = {};
78+
}
79+
refs.current[idarpt][layer] = _layer ?? undefined
80+
81+
updateOrder();
82+
}}
83+
key={`${idarpt}/${layer}`}
84+
onClick={(_layer) => {
85+
const feature = _layer.feature;
86+
87+
if (feature?.type === 'Feature') {
88+
const style = amdbStyle(layer)(feature);
3789

38-
return amdbLayers.map(([idarpt, layers]) => <>
39-
<AmdbLayer key={`${idarpt}/aerodromereferencepoint`} amdb={amdb} idarpt={idarpt} layer="aerodromereferencepoint" />
40-
{layers.map((layer) => <AmdbLayer key={`${idarpt}/${layer}`} amdb={amdb} idarpt={idarpt} layer={layer} />)}
41-
</>)
90+
style.stroke = true;
91+
style.color = 'blue';
92+
93+
Object.values(refs.current).forEach((layer) => {
94+
Object.values(layer).forEach((x) => {
95+
x?.resetStyle();
96+
});
97+
})
98+
99+
_layer.setStyle(style);
100+
}
101+
}}
102+
idarpt={idarpt}
103+
layer={layer}
104+
/>
105+
))
106+
)
42107
}
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
import { AmdbLayerName, LineColor, LineStyle, PolygonalStructureType, StopbarCategory } from "@navigraph/amdb";
2+
import { CircleOptions, PathOptions, StyleFunction } from "leaflet";
3+
4+
// Top to bottom like rendering, not render order
5+
export const layerOrder: Record<AmdbLayerName, number> = {
6+
'aerodromereferencepoint': 0,
7+
'verticalpointstructure': 0,
8+
'runwaythreshold': 0,
9+
'parkingstandlocation': 0,
10+
'frequencyarea': 1,
11+
'taxiwayholdingposition': 1,
12+
'taxiwayintersectionmarking': 1,
13+
'taxiwayguidanceline': 2,
14+
'standguidanceline': 2,
15+
'runwayexitline': 2,
16+
'serviceroad': 3,
17+
'deicingarea': 4,
18+
'parkingstandarea': 5,
19+
'taxiwayelement': 5,
20+
'apronelement': 6,
21+
'runwaymarking': 7,
22+
'runwayelement': 8,
23+
'runwayintersection': 8,
24+
'runwaydisplacedarea': 8,
25+
'stopway': 8,
26+
'blastpad': 8,
27+
'arrestinggearlocation': 9,
28+
'constructionarea': 10,
29+
'finalapproachandtakeoffarea': 11,
30+
'helipadthreshold': 12,
31+
'hotspot': 13,
32+
'landandholdshortoperationlocation': 14,
33+
'paintedcenterline': 15,
34+
'runwayshoulder': 17,
35+
'taxiwayshoulder': 17,
36+
'touchdownliftoffarea': 19,
37+
'verticallinestructure': 20,
38+
'verticalpolygonalstructure': 21,
39+
'water': 22
40+
}
41+
42+
export default function amdbStyle(layerName: AmdbLayerName): StyleFunction {
43+
return (feature): PathOptions | CircleOptions => {
44+
switch (layerName) {
45+
case "apronelement":
46+
return {
47+
fillOpacity: 1,
48+
fillColor: 'hsl(0, 0%, 52%)',
49+
stroke: false
50+
}
51+
case "stopway":
52+
case "blastpad":
53+
return {
54+
fillOpacity: 1,
55+
fillColor: 'hsl(0, 0%, 40%)',
56+
stroke: false
57+
}
58+
case "deicingarea":
59+
return {
60+
fillOpacity: 1,
61+
stroke: false,
62+
fillColor: 'hsl(200, 60%, 75%)'
63+
}
64+
case "parkingstandarea":
65+
return {
66+
fillOpacity: 1,
67+
fillColor: 'hsl(0, 0%, 47%)',
68+
stroke: false,
69+
}
70+
case "runwaydisplacedarea":
71+
case "runwayintersection":
72+
case "runwayelement":
73+
return {
74+
fillOpacity: 1,
75+
fillColor: 'hsl(0, 0%, 33%)',
76+
stroke: false
77+
}
78+
case "runwaymarking":
79+
return {
80+
fillOpacity: 1,
81+
fillColor: '#fefefe',
82+
stroke: false,
83+
}
84+
case "serviceroad":
85+
return {
86+
fillOpacity: 1,
87+
fillColor: 'hsl(240, 5%, 55%)',
88+
stroke: false,
89+
}
90+
case "taxiwayelement":
91+
return {
92+
fillOpacity: 1,
93+
fillColor: 'hsl(0, 0%, 50%)',
94+
stroke: false,
95+
}
96+
case 'taxiwayshoulder':
97+
case "runwayshoulder":
98+
return {
99+
fillOpacity: 0.5,
100+
fillColor: 'hsl(0, 0%, 65%)',
101+
stroke: false,
102+
}
103+
case "standguidanceline":
104+
case "taxiwayguidanceline":
105+
case "runwayexitline":
106+
return {
107+
stroke: true,
108+
fill: false,
109+
weight: 1.8,
110+
dashArray: {
111+
[LineStyle.Solid]: undefined,
112+
[LineStyle.Unknown]: undefined,
113+
[LineStyle.Dashed]: [3, 3],
114+
[LineStyle.Dotted]: [3, 1],
115+
}[feature?.properties.style as LineStyle],
116+
color: {
117+
[LineColor.Yellow]: 'yellow',
118+
[LineColor.Unknown]: 'yellow',
119+
[LineColor.Orange]: 'orange',
120+
[LineColor.Blue]: 'white',
121+
[LineColor.White]: 'white',
122+
}[feature?.properties.color as LineColor]
123+
}
124+
case "arrestinggearlocation":
125+
return {
126+
stroke: true,
127+
fill: false,
128+
color: '#ff00ff'
129+
}
130+
case "landandholdshortoperationlocation":
131+
return {
132+
stroke: true,
133+
fill: false,
134+
color: '#00ff00'
135+
}
136+
case "verticalpolygonalstructure":
137+
return {
138+
stroke: false,
139+
fill: true,
140+
fillOpacity: 1,
141+
fillColor: feature?.properties.plysttyp === PolygonalStructureType.TerminalBuilding ? 'hsl(240, 25%, 30%)' : 'hsl(0, 0%, 30%)'
142+
}
143+
case "taxiwayholdingposition":
144+
return {
145+
color: {
146+
[StopbarCategory.None]: 'yellow',
147+
[StopbarCategory.NotApplicable]: 'yellow',
148+
[StopbarCategory.Unknown]: 'yellow',
149+
[StopbarCategory.Cat1]: 'hsl(25, 100%, 50%)',
150+
[StopbarCategory.Cat2Or3]: 'hsl(0, 100%, 50%)',
151+
}[feature?.properties.catstop as StopbarCategory]
152+
}
153+
case "taxiwayintersectionmarking":
154+
return {
155+
color: '#fefefe',
156+
dashArray: [2, 2]
157+
}
158+
case "hotspot":
159+
return {
160+
fill: true,
161+
stroke: true,
162+
color: '#ff00ff',
163+
fillColor: '#89259D',
164+
fillOpacity: 0.3
165+
}
166+
case "constructionarea":
167+
return {
168+
stroke: false,
169+
fillColor: '#ff0000',
170+
fillOpacity: 0.1
171+
}
172+
case "frequencyarea":
173+
return {
174+
fill: false,
175+
color: 'green'
176+
}
177+
case "water":
178+
return {
179+
color: '#004a61',
180+
fillColor: '#003264',
181+
fillOpacity: 1,
182+
}
183+
case "aerodromereferencepoint": return {
184+
radius: 15,
185+
color: 'white',
186+
}
187+
case "runwaythreshold":
188+
case "finalapproachandtakeoffarea":
189+
case "helipadthreshold":
190+
case "paintedcenterline":
191+
case "parkingstandlocation":
192+
case "touchdownliftoffarea":
193+
case "verticallinestructure":
194+
case "verticalpointstructure":
195+
return {
196+
radius: 10
197+
}
198+
}
199+
200+
}
201+
}

0 commit comments

Comments
 (0)