fix(map): harden globe projection switch and overlay teardown
This commit is contained in:
부모
15378ed7ff
커밋
0ffadb2e66
@ -126,6 +126,16 @@ function onMapStyleReady(map: maplibregl.Map, callback: () => void) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractProjectionType(map: maplibregl.Map): MapProjectionId | undefined {
|
||||||
|
const projection = map.getProjection?.();
|
||||||
|
if (!projection || typeof projection !== "object") return undefined;
|
||||||
|
|
||||||
|
const rawType = (projection as { type?: unknown; name?: unknown }).type ?? (projection as { type?: unknown; name?: unknown }).name;
|
||||||
|
if (rawType === "globe") return "globe";
|
||||||
|
if (rawType === "mercator") return "mercator";
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
const DEG2RAD = Math.PI / 180;
|
const DEG2RAD = Math.PI / 180;
|
||||||
|
|
||||||
const clampNumber = (value: number, minValue: number, maxValue: number) => Math.max(minValue, Math.min(maxValue, value));
|
const clampNumber = (value: number, minValue: number, maxValue: number) => Math.max(minValue, Math.min(maxValue, value));
|
||||||
@ -724,9 +734,38 @@ export function Map3D({
|
|||||||
let retries = 0;
|
let retries = 0;
|
||||||
const maxRetries = 18;
|
const maxRetries = 18;
|
||||||
|
|
||||||
const getCurrentProjection = () => {
|
const disposeMercatorOverlay = () => {
|
||||||
const projectionConfig = map.getProjection?.();
|
const current = overlayRef.current;
|
||||||
return projectionConfig && "type" in projectionConfig ? (projectionConfig.type as MapProjectionId | undefined) : undefined;
|
if (!current) return;
|
||||||
|
try {
|
||||||
|
map.removeControl(current as never);
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
current.finalize();
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
overlayRef.current = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const disposeGlobeDeckLayer = () => {
|
||||||
|
const current = globeDeckLayerRef.current;
|
||||||
|
if (!current) return;
|
||||||
|
if (map.getLayer(current.id)) {
|
||||||
|
try {
|
||||||
|
map.removeLayer(current.id);
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
current.requestFinalize();
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
globeDeckLayerRef.current = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const syncProjectionAndDeck = () => {
|
const syncProjectionAndDeck = () => {
|
||||||
@ -741,11 +780,21 @@ export function Map3D({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const next = projection;
|
const next = projection;
|
||||||
|
const currentProjection = extractProjectionType(map);
|
||||||
|
const shouldSwitchProjection = currentProjection !== next;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (getCurrentProjection() !== next) {
|
if (shouldSwitchProjection) {
|
||||||
map.setProjection({ type: next });
|
map.setProjection({ type: next });
|
||||||
}
|
}
|
||||||
map.setRenderWorldCopies(next !== "globe");
|
map.setRenderWorldCopies(next !== "globe");
|
||||||
|
if (shouldSwitchProjection) {
|
||||||
|
if (!cancelled && retries < maxRetries) {
|
||||||
|
retries += 1;
|
||||||
|
window.requestAnimationFrame(() => syncProjectionAndDeck());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!cancelled && retries < maxRetries) {
|
if (!cancelled && retries < maxRetries) {
|
||||||
retries += 1;
|
retries += 1;
|
||||||
@ -758,25 +807,12 @@ export function Map3D({
|
|||||||
const oldOverlay = overlayRef.current;
|
const oldOverlay = overlayRef.current;
|
||||||
if (projection === "globe" && oldOverlay) {
|
if (projection === "globe" && oldOverlay) {
|
||||||
// Globe mode uses custom MapLibre deck layers and should fully replace Mercator overlays.
|
// Globe mode uses custom MapLibre deck layers and should fully replace Mercator overlays.
|
||||||
try {
|
disposeMercatorOverlay();
|
||||||
oldOverlay.finalize();
|
|
||||||
} catch {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
overlayRef.current = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (projection === "globe") {
|
if (projection === "globe") {
|
||||||
// Ensure any stale layer from old mode is dropped then re-added on this projection.
|
// Start with a clean globe Deck layer state to avoid partially torn-down renders.
|
||||||
if (globeDeckLayerRef.current) {
|
disposeGlobeDeckLayer();
|
||||||
try {
|
|
||||||
if (map.getLayer(globeDeckLayerRef.current.id)) {
|
|
||||||
map.removeLayer(globeDeckLayerRef.current.id);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!globeDeckLayerRef.current) {
|
if (!globeDeckLayerRef.current) {
|
||||||
globeDeckLayerRef.current = new MaplibreDeckCustomLayer({
|
globeDeckLayerRef.current = new MaplibreDeckCustomLayer({
|
||||||
@ -801,15 +837,7 @@ export function Map3D({
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Tear down globe custom layer (if present), restore MapboxOverlay interleaved.
|
// Tear down globe custom layer (if present), restore MapboxOverlay interleaved.
|
||||||
const globeLayer = globeDeckLayerRef.current;
|
disposeGlobeDeckLayer();
|
||||||
if (globeLayer && map.getLayer(globeLayer.id)) {
|
|
||||||
try {
|
|
||||||
globeLayer.requestFinalize();
|
|
||||||
map.removeLayer(globeLayer.id);
|
|
||||||
} catch {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!overlayRef.current) {
|
if (!overlayRef.current) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
불러오는 중...
Reference in New Issue
Block a user