Skip to content

Commit ae92cec

Browse files
optimize amdb
1 parent c80db90 commit ae92cec

File tree

3 files changed

+87
-94
lines changed

3 files changed

+87
-94
lines changed
Lines changed: 84 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,99 @@
11
import { useRecoilValue } from "recoil";
22
import { amdbLayersState } from "../../state/amdb";
33
import { AmdbLayerName, getAmdbAPI } from "@navigraph/amdb";
4-
import { userState } from "../../state/user";
5-
import { Scope } from "@navigraph/app";
6-
import { forwardRef, memo, useCallback, useRef } from "react";
7-
import { useQuery } from "@tanstack/react-query";
8-
import { GeoJSON } from "react-leaflet";
9-
import { circleMarker, GeoJSON as LeafletGeoJson } from 'leaflet';
4+
import { memo, useEffect, useRef } from "react";
5+
import { useQueries } from "@tanstack/react-query";
6+
import { useMap } from "react-leaflet";
7+
import { circleMarker, geoJson, GeoJSON } from 'leaflet';
108
import { renderToString } from "react-dom/server";
119
import JsonView from "../JsonView";
1210
import amdbStyle, { layerOrder } from "./amdb_styles";
1311

14-
const AmdbLayer = memo(forwardRef<LeafletGeoJson, { idarpt: string, layer: AmdbLayerName, onClick?: (e: LeafletGeoJson) => void }>(({ idarpt, layer, onClick }, ref) => {
15-
const { data } = useQuery({
16-
queryKey: ['amdb-data', idarpt, layer],
17-
queryFn: async () => {
18-
return amdb.getAmdbLayer({ icao: idarpt, layer })
19-
}
20-
})
21-
12+
const AmdbManager = memo(() => {
2213
const amdb = getAmdbAPI();
2314

24-
if (!data) return null;
25-
26-
return (
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-
54-
)
55-
}));
56-
57-
export default function AmdbManager() {
15+
const map = useMap();
16+
5817
const amdbLayers = useRecoilValue(amdbLayersState);
5918

60-
const user = useRecoilValue(userState);
19+
const data = useQueries({
20+
queries: amdbLayers.flatMap(([idarpt, layers]) => layers.map((layer) => ({
21+
queryKey: ['amdb-data', idarpt, layer],
22+
queryFn: async () => ({ idarpt, layer, data: await amdb.getAmdbLayer({ icao: idarpt, layer }) }),
23+
})))
24+
})
25+
26+
const layers = useRef<{ idarpt: string, layerName: AmdbLayerName, layer: GeoJSON }[]>([]);
6127

62-
const refs = useRef<Record<string, Partial<Record<AmdbLayerName, LeafletGeoJson>>>>({});
28+
useEffect(() => {
29+
const toRemove = layers.current.filter(({ idarpt, layerName }) => !data.some(({ data }) => data?.idarpt === idarpt && data?.layer === layerName));
6330

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())
31+
toRemove.forEach(({ layer }) => {
32+
map.removeLayer(layer);
33+
layers.current.splice(layers.current.findIndex((x) => x.layer === layer), 1);
6734
});
68-
}, [refs.current]);
69-
70-
if (!user?.scope.includes(Scope.AMDB)) return;
71-
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);
89-
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();
35+
36+
const newLayers = data.flatMap(({ data }) => {
37+
if (data?.data && !layers.current.some(({ idarpt, layerName }) => idarpt === data.idarpt && layerName === data.layer)) {
38+
return [{
39+
idarpt: data.idarpt,
40+
layerName: data.layer,
41+
layer: geoJson(data.data, {
42+
style: amdbStyle(data.layer),
43+
44+
pointToLayer: (feature, latlng) => {
45+
const marker = circleMarker(latlng, amdbStyle(data.layer)(feature));
46+
47+
marker.feature = feature;
48+
49+
return marker;
50+
},
51+
52+
onEachFeature: (feature, _layer) => {
53+
_layer.on('click', (e) => {
54+
const target = e.target as GeoJSON;
55+
56+
const feature = target.feature;
57+
58+
if (feature?.type === 'Feature') {
59+
const style = amdbStyle(data.layer)(feature);
60+
61+
style.stroke = true;
62+
style.color = 'blue';
63+
64+
layers.current.forEach(({ layer }) => {
65+
layer.resetStyle();
66+
})
67+
68+
target.setStyle(style);
69+
}
9670
});
97-
})
98-
99-
_layer.setStyle(style);
100-
}
101-
}}
102-
idarpt={idarpt}
103-
layer={layer}
104-
/>
105-
))
106-
)
107-
}
71+
72+
if (feature.properties) {
73+
_layer.bindPopup(renderToString(
74+
<div className="flex flex-col items-center gap-2">
75+
<span className="text-ng-background-200">{data.layer}</span>
76+
<JsonView content={feature.properties} />
77+
</div>
78+
))
79+
}
80+
}
81+
})
82+
}];
83+
}
84+
85+
return [];
86+
});
87+
88+
newLayers.forEach(({ idarpt, layerName, layer }) => {
89+
map.addLayer(layer);
90+
layers.current.push({ idarpt, layerName, layer });
91+
});
92+
93+
layers.current.sort((a, b) => layerOrder[b.layerName] - layerOrder[a.layerName]).forEach(({ layer }) => layer.bringToFront())
94+
}, [data]);
95+
96+
return null;
97+
});
98+
99+
export default AmdbManager;

examples/playground/src/components/map/amdb_styles.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export const layerOrder: Record<AmdbLayerName, number> = {
77
'verticalpointstructure': 0,
88
'runwaythreshold': 0,
99
'parkingstandlocation': 0,
10+
'hotspot': 0,
1011
'frequencyarea': 1,
1112
'taxiwayholdingposition': 1,
1213
'taxiwayintersectionmarking': 1,
@@ -28,7 +29,6 @@ export const layerOrder: Record<AmdbLayerName, number> = {
2829
'constructionarea': 10,
2930
'finalapproachandtakeoffarea': 11,
3031
'helipadthreshold': 12,
31-
'hotspot': 13,
3232
'landandholdshortoperationlocation': 14,
3333
'paintedcenterline': 15,
3434
'runwayshoulder': 17,

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { useQuery } from "@tanstack/react-query";
1616
import { TbCircleX } from "react-icons/tb";
1717
import Button from "../Button";
1818
import AmdbManager from "./amdb";
19+
import { getAmdbAPI } from "@navigraph/amdb";
1920

2021
export function createPreset(source: NavigraphRasterSource, theme: NavigraphTheme, faa: boolean, tac: boolean): PresetConfig {
2122
if (source === 'WORLD') {
@@ -155,7 +156,7 @@ export default function MapPane() {
155156
/>
156157
))}
157158
{charts && <ChartOverlay charts={charts} />}
158-
<AmdbManager />
159+
{user?.scope.includes(Scope.AMDB) && <AmdbManager />}
159160
<Button selected={mapVisible} className="absolute top-5 right-5 z-[999]" onClick={() => setMapVisible(!mapVisible)}>Map Visible</Button>
160161
</MapContainer>
161162
</div>

0 commit comments

Comments
 (0)