Keep globe overlays stable and reuse globe layer IDs
This commit is contained in:
부모
e504dbebca
커밋
70dc651230
@ -121,6 +121,26 @@ function getZoneDisplayNameFromProps(props: Record<string, unknown> | null | und
|
||||
return ZONE_META[(zoneId as ZoneId)]?.name || `수역 ${zoneId}`;
|
||||
}
|
||||
|
||||
function makeOrderedPairKey(a: number, b: number) {
|
||||
const left = Math.trunc(Math.min(a, b));
|
||||
const right = Math.trunc(Math.max(a, b));
|
||||
return `${left}-${right}`;
|
||||
}
|
||||
|
||||
function makePairLinkFeatureId(a: number, b: number, suffix?: string) {
|
||||
const pair = makeOrderedPairKey(a, b);
|
||||
return suffix ? `pair-${pair}-${suffix}` : `pair-${pair}`;
|
||||
}
|
||||
|
||||
function makeFcSegmentFeatureId(a: number, b: number, segmentIndex: number) {
|
||||
const pair = makeOrderedPairKey(a, b);
|
||||
return `fc-${pair}-${segmentIndex}`;
|
||||
}
|
||||
|
||||
function makeFleetCircleFeatureId(ownerKey: string) {
|
||||
return `fleet-${ownerKey}`;
|
||||
}
|
||||
|
||||
const SHIP_ICON_MAPPING = {
|
||||
ship: {
|
||||
x: 0,
|
||||
@ -972,20 +992,21 @@ export function Map3D({
|
||||
return keys;
|
||||
}, [hoveredFleetOwnerKey, hoveredDeckFleetOwnerKey]);
|
||||
|
||||
const reorderGlobeFeatureLayers = useCallback(() => {
|
||||
const reorderGlobeFeatureLayers = useCallback((options?: { shipTop?: boolean }) => {
|
||||
const map = mapRef.current;
|
||||
if (!map || projectionRef.current !== "globe") return;
|
||||
if (projectionBusyRef.current) return;
|
||||
|
||||
const shipTop = options?.shipTop === true;
|
||||
const ordering = [
|
||||
"zones-fill",
|
||||
"zones-line",
|
||||
"zones-label",
|
||||
"pair-lines-ml",
|
||||
"fc-lines-ml",
|
||||
"pair-range-ml",
|
||||
"fleet-circles-ml-fill",
|
||||
"fleet-circles-ml",
|
||||
"zones-fill",
|
||||
"zones-line",
|
||||
"zones-label",
|
||||
"ships-globe-halo",
|
||||
"ships-globe-outline",
|
||||
"ships-globe",
|
||||
@ -999,6 +1020,17 @@ export function Map3D({
|
||||
}
|
||||
}
|
||||
|
||||
if (!shipTop) return;
|
||||
|
||||
const shipOrdering = ["ships-globe-halo", "ships-globe-outline", "ships-globe"];
|
||||
for (const layerId of shipOrdering) {
|
||||
try {
|
||||
if (map.getLayer(layerId)) map.moveLayer(layerId);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
kickRepaint(map);
|
||||
}, []);
|
||||
|
||||
@ -2405,7 +2437,7 @@ export function Map3D({
|
||||
|
||||
// Selection and highlight are now source-data driven.
|
||||
bringShipLayersToFront();
|
||||
reorderGlobeFeatureLayers();
|
||||
reorderGlobeFeatureLayers({ shipTop: true });
|
||||
kickRepaint(map);
|
||||
};
|
||||
|
||||
@ -2514,12 +2546,7 @@ export function Map3D({
|
||||
|
||||
const remove = () => {
|
||||
try {
|
||||
if (map.getLayer(layerId)) map.removeLayer(layerId);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
if (map.getSource(srcId)) map.removeSource(srcId);
|
||||
if (map.getLayer(layerId)) map.setLayoutProperty(layerId, "visibility", "none");
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
@ -2535,9 +2562,9 @@ export function Map3D({
|
||||
|
||||
const fc: GeoJSON.FeatureCollection<GeoJSON.LineString> = {
|
||||
type: "FeatureCollection",
|
||||
features: (pairLinks || []).map((p, idx) => ({
|
||||
features: (pairLinks || []).map((p) => ({
|
||||
type: "Feature",
|
||||
id: `${p.aMmsi}-${p.bMmsi}-${idx}`,
|
||||
id: makePairLinkFeatureId(p.aMmsi, p.bMmsi),
|
||||
geometry: { type: "LineString", coordinates: [p.from, p.to] },
|
||||
properties: {
|
||||
type: "pair",
|
||||
@ -2594,6 +2621,12 @@ export function Map3D({
|
||||
} catch (e) {
|
||||
console.warn("Pair lines layer add failed:", e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
map.setLayoutProperty(layerId, "visibility", "visible");
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
reorderGlobeFeatureLayers();
|
||||
@ -2604,7 +2637,6 @@ export function Map3D({
|
||||
ensure();
|
||||
return () => {
|
||||
stop();
|
||||
remove();
|
||||
};
|
||||
}, [
|
||||
projection,
|
||||
@ -2626,12 +2658,7 @@ export function Map3D({
|
||||
|
||||
const remove = () => {
|
||||
try {
|
||||
if (map.getLayer(layerId)) map.removeLayer(layerId);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
if (map.getSource(srcId)) map.removeSource(srcId);
|
||||
if (map.getLayer(layerId)) map.setLayoutProperty(layerId, "visibility", "none");
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
@ -2658,7 +2685,7 @@ export function Map3D({
|
||||
type: "FeatureCollection",
|
||||
features: segs.map((s, idx) => ({
|
||||
type: "Feature",
|
||||
id: `fc-${idx}`,
|
||||
id: makeFcSegmentFeatureId(s.fromMmsi ?? -1, s.toMmsi ?? -1, idx),
|
||||
geometry: { type: "LineString", coordinates: [s.from, s.to] },
|
||||
properties: {
|
||||
type: "fc",
|
||||
@ -2708,6 +2735,12 @@ export function Map3D({
|
||||
} catch (e) {
|
||||
console.warn("FC lines layer add failed:", e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
map.setLayoutProperty(layerId, "visibility", "visible");
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
reorderGlobeFeatureLayers();
|
||||
@ -2718,7 +2751,6 @@ export function Map3D({
|
||||
ensure();
|
||||
return () => {
|
||||
stop();
|
||||
remove();
|
||||
};
|
||||
}, [
|
||||
projection,
|
||||
@ -2741,22 +2773,12 @@ export function Map3D({
|
||||
|
||||
const remove = () => {
|
||||
try {
|
||||
if (map.getLayer(fillLayerId)) map.removeLayer(fillLayerId);
|
||||
if (map.getLayer(fillLayerId)) map.setLayoutProperty(fillLayerId, "visibility", "none");
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
if (map.getLayer(layerId)) map.removeLayer(layerId);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
if (map.getSource(srcId)) map.removeSource(srcId);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
if (map.getSource(fillSrcId)) map.removeSource(fillSrcId);
|
||||
if (map.getLayer(layerId)) map.setLayoutProperty(layerId, "visibility", "none");
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
@ -2772,11 +2794,11 @@ export function Map3D({
|
||||
|
||||
const fcLine: GeoJSON.FeatureCollection<GeoJSON.LineString> = {
|
||||
type: "FeatureCollection",
|
||||
features: (fleetCircles || []).map((c, idx) => {
|
||||
features: (fleetCircles || []).map((c) => {
|
||||
const ring = circleRingLngLat(c.center, c.radiusNm * 1852);
|
||||
return {
|
||||
type: "Feature",
|
||||
id: `fleet-${c.ownerKey}-${idx}`,
|
||||
id: makeFleetCircleFeatureId(c.ownerKey),
|
||||
geometry: { type: "LineString", coordinates: ring },
|
||||
properties: {
|
||||
type: "fleet",
|
||||
@ -2795,11 +2817,11 @@ export function Map3D({
|
||||
|
||||
const fcFill: GeoJSON.FeatureCollection<GeoJSON.Polygon> = {
|
||||
type: "FeatureCollection",
|
||||
features: (fleetCircles || []).map((c, idx) => {
|
||||
features: (fleetCircles || []).map((c) => {
|
||||
const ring = circleRingLngLat(c.center, c.radiusNm * 1852);
|
||||
return {
|
||||
type: "Feature",
|
||||
id: `fleet-fill-${c.ownerKey}-${idx}`,
|
||||
id: `${makeFleetCircleFeatureId(c.ownerKey)}-fill`,
|
||||
geometry: { type: "Polygon", coordinates: [ring] },
|
||||
properties: {
|
||||
type: "fleet-fill",
|
||||
@ -2859,6 +2881,12 @@ export function Map3D({
|
||||
} catch (e) {
|
||||
console.warn("Fleet circles fill layer add failed:", e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
map.setLayoutProperty(fillLayerId, "visibility", "visible");
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
if (!map.getLayer(layerId)) {
|
||||
@ -2880,6 +2908,12 @@ export function Map3D({
|
||||
} catch (e) {
|
||||
console.warn("Fleet circles layer add failed:", e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
map.setLayoutProperty(layerId, "visibility", "visible");
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
reorderGlobeFeatureLayers();
|
||||
@ -2890,7 +2924,6 @@ export function Map3D({
|
||||
ensure();
|
||||
return () => {
|
||||
stop();
|
||||
remove();
|
||||
};
|
||||
}, [
|
||||
projection,
|
||||
@ -2914,12 +2947,7 @@ export function Map3D({
|
||||
|
||||
const remove = () => {
|
||||
try {
|
||||
if (map.getLayer(layerId)) map.removeLayer(layerId);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
if (map.getSource(srcId)) map.removeSource(srcId);
|
||||
if (map.getLayer(layerId)) map.setLayoutProperty(layerId, "visibility", "none");
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
@ -2952,11 +2980,11 @@ export function Map3D({
|
||||
|
||||
const fc: GeoJSON.FeatureCollection<GeoJSON.LineString> = {
|
||||
type: "FeatureCollection",
|
||||
features: ranges.map((c, idx) => {
|
||||
features: ranges.map((c) => {
|
||||
const ring = circleRingLngLat(c.center, c.radiusNm * 1852);
|
||||
return {
|
||||
type: "Feature",
|
||||
id: `pair-range-${idx}`,
|
||||
id: makePairLinkFeatureId(c.aMmsi, c.bMmsi),
|
||||
geometry: { type: "LineString", coordinates: ring },
|
||||
properties: {
|
||||
type: "pair-range",
|
||||
@ -3007,6 +3035,12 @@ export function Map3D({
|
||||
} catch (e) {
|
||||
console.warn("Pair range layer add failed:", e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
map.setLayoutProperty(layerId, "visibility", "visible");
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
kickRepaint(map);
|
||||
@ -3016,7 +3050,6 @@ export function Map3D({
|
||||
ensure();
|
||||
return () => {
|
||||
stop();
|
||||
remove();
|
||||
};
|
||||
}, [
|
||||
projection,
|
||||
|
||||
불러오는 중...
Reference in New Issue
Block a user