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