fix(map3d): sync mercator restore on globe toggle
This commit is contained in:
부모
6f7a82af4c
커밋
1225d5c54c
@ -9,7 +9,7 @@ import maplibregl, {
|
||||
type StyleSpecification,
|
||||
type VectorSourceSpecification,
|
||||
} from "maplibre-gl";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import type { AisTarget } from "../../entities/aisTarget/model/types";
|
||||
import type { LegacyVesselInfo } from "../../entities/legacyVessel/model/types";
|
||||
import type { ZonesGeoJson } from "../../entities/zone/api/useZones";
|
||||
@ -580,6 +580,67 @@ export function Map3D({
|
||||
projectionRef.current = projection;
|
||||
}, [projection]);
|
||||
|
||||
const removeLayerIfExists = useCallback((map: maplibregl.Map, layerId: string) => {
|
||||
try {
|
||||
if (map.getLayer(layerId)) {
|
||||
map.removeLayer(layerId);
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}, []);
|
||||
|
||||
const removeSourceIfExists = useCallback((map: maplibregl.Map, sourceId: string) => {
|
||||
try {
|
||||
if (map.getSource(sourceId)) {
|
||||
map.removeSource(sourceId);
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}, []);
|
||||
|
||||
const ensureMercatorOverlay = useCallback(() => {
|
||||
const map = mapRef.current;
|
||||
if (!map) return null;
|
||||
if (overlayRef.current) return overlayRef.current;
|
||||
|
||||
try {
|
||||
const next = new MapboxOverlay({ interleaved: true, layers: [] } as unknown as never);
|
||||
map.addControl(next);
|
||||
overlayRef.current = next;
|
||||
return next;
|
||||
} catch (e) {
|
||||
console.warn("Deck overlay create failed:", e);
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const clearGlobeNativeLayers = useCallback(() => {
|
||||
const map = mapRef.current;
|
||||
if (!map) return;
|
||||
|
||||
const layerIds = [
|
||||
"ships-globe-halo",
|
||||
"ships-globe-outline",
|
||||
"ships-globe",
|
||||
"pair-lines-ml",
|
||||
"fc-lines-ml",
|
||||
"fleet-circles-ml",
|
||||
"pair-range-ml",
|
||||
"deck-globe",
|
||||
];
|
||||
|
||||
for (const id of layerIds) {
|
||||
removeLayerIfExists(map, id);
|
||||
}
|
||||
|
||||
const sourceIds = ["ships-globe-src", "pair-lines-ml-src", "fc-lines-ml-src", "fleet-circles-ml-src", "pair-range-ml-src"];
|
||||
for (const id of sourceIds) {
|
||||
removeSourceIfExists(map, id);
|
||||
}
|
||||
}, [removeLayerIfExists, removeSourceIfExists]);
|
||||
|
||||
// Init MapLibre + Deck.gl (single WebGL context via MapboxOverlay)
|
||||
useEffect(() => {
|
||||
if (!containerRef.current || mapRef.current) return;
|
||||
@ -758,13 +819,7 @@ export function Map3D({
|
||||
const disposeGlobeDeckLayer = () => {
|
||||
const current = globeDeckLayerRef.current;
|
||||
if (!current) return;
|
||||
if (map.getLayer(current.id)) {
|
||||
try {
|
||||
map.removeLayer(current.id);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
removeLayerIfExists(map, current.id);
|
||||
try {
|
||||
current.requestFinalize();
|
||||
} catch {
|
||||
@ -790,8 +845,10 @@ export function Map3D({
|
||||
|
||||
if (projection === "globe") {
|
||||
disposeMercatorOverlay();
|
||||
clearGlobeNativeLayers();
|
||||
} else {
|
||||
disposeGlobeDeckLayer();
|
||||
clearGlobeNativeLayers();
|
||||
}
|
||||
|
||||
try {
|
||||
@ -842,15 +899,7 @@ export function Map3D({
|
||||
// Tear down globe custom layer (if present), restore MapboxOverlay interleaved.
|
||||
disposeGlobeDeckLayer();
|
||||
|
||||
if (!overlayRef.current) {
|
||||
try {
|
||||
const next = new MapboxOverlay({ interleaved: true, layers: [] } as unknown as never);
|
||||
map.addControl(next);
|
||||
overlayRef.current = next;
|
||||
} catch (e) {
|
||||
console.warn("Deck overlay create failed:", e);
|
||||
}
|
||||
}
|
||||
ensureMercatorOverlay();
|
||||
}
|
||||
|
||||
// MapLibre may not schedule a frame immediately after projection swaps if the map is idle.
|
||||
@ -876,7 +925,7 @@ export function Map3D({
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [projection]);
|
||||
}, [projection, clearGlobeNativeLayers, ensureMercatorOverlay, removeLayerIfExists]);
|
||||
|
||||
// Base map toggle
|
||||
useEffect(() => {
|
||||
@ -1333,7 +1382,7 @@ export function Map3D({
|
||||
"icon-rotate": ["to-number", ["get", "cog"], 0],
|
||||
// Keep the icon on the sea surface.
|
||||
"icon-rotation-alignment": "map",
|
||||
"icon-pitch-alignment": "viewport",
|
||||
"icon-pitch-alignment": "map",
|
||||
},
|
||||
paint: {
|
||||
"icon-color": ["coalesce", ["get", "shipColor"], "#64748b"] as never,
|
||||
@ -1830,7 +1879,19 @@ export function Map3D({
|
||||
useEffect(() => {
|
||||
const map = mapRef.current;
|
||||
if (!map) return;
|
||||
const deckTarget = projection === "globe" ? globeDeckLayerRef.current : overlayRef.current;
|
||||
let deckTarget = projection === "globe" ? globeDeckLayerRef.current : overlayRef.current;
|
||||
|
||||
if (projection === "mercator") {
|
||||
if (!deckTarget) deckTarget = ensureMercatorOverlay();
|
||||
if (!deckTarget) return;
|
||||
try {
|
||||
deckTarget.setProps({ layers: [] } as never);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
} else if (!deckTarget && projection === "globe") {
|
||||
return;
|
||||
}
|
||||
if (!deckTarget) return;
|
||||
|
||||
const overlayParams = projection === "globe" ? GLOBE_OVERLAY_PARAMS : DEPTH_DISABLED_PARAMS;
|
||||
@ -2068,6 +2129,7 @@ export function Map3D({
|
||||
fcDashed,
|
||||
fleetCircles,
|
||||
mapSyncEpoch,
|
||||
ensureMercatorOverlay,
|
||||
]);
|
||||
|
||||
return <div ref={containerRef} style={{ width: "100%", height: "100%" }} />;
|
||||
|
||||
불러오는 중...
Reference in New Issue
Block a user