fix(map): 해저케이블 시인성 개선
- MapLibre 중첩 interpolate 표현식 에러 수정 - 6레이어 구조: hitarea, casing, line, glow, points, label - 호버 시 flat value 사용 (case 내 interpolate 제거) - Globe/Mercator 양쪽 프로젝션 레이어 순서 지원 - 진한 색상, 굵은 라인, 포인트 마커로 시인성 향상 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
부모
ca5560aff2
커밋
7eff97afd4
@ -505,7 +505,7 @@ export function Map3D({
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const noopCable = useCallback((_: string | null) => {}, []);
|
const noopCable = useCallback((_: string | null) => {}, []);
|
||||||
|
|
||||||
useSubcablesLayer(
|
useSubcablesLayer(
|
||||||
|
|||||||
@ -91,6 +91,12 @@ export function useProjectionToggle(
|
|||||||
if (!map.isStyleLoaded()) return;
|
if (!map.isStyleLoaded()) return;
|
||||||
|
|
||||||
const ordering = [
|
const ordering = [
|
||||||
|
'subcables-hitarea',
|
||||||
|
'subcables-casing',
|
||||||
|
'subcables-line',
|
||||||
|
'subcables-glow',
|
||||||
|
'subcables-points',
|
||||||
|
'subcables-label',
|
||||||
'zones-fill',
|
'zones-fill',
|
||||||
'zones-line',
|
'zones-line',
|
||||||
'zones-label',
|
'zones-label',
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useRef, type MutableRefObject } from 'react';
|
import { useEffect, useMemo, useRef, type MutableRefObject } from 'react';
|
||||||
import maplibregl, { type LayerSpecification } from 'maplibre-gl';
|
import maplibregl, { type LayerSpecification } from 'maplibre-gl';
|
||||||
import type { SubcableGeoJson } from '../../../entities/subcable/model/types';
|
import type { SubcableGeoJson } from '../../../entities/subcable/model/types';
|
||||||
import type { MapToggleState } from '../../../features/mapToggles/MapToggles';
|
import type { MapToggleState } from '../../../features/mapToggles/MapToggles';
|
||||||
@ -6,11 +6,20 @@ import type { MapProjectionId } from '../types';
|
|||||||
import { ensureGeoJsonSource, ensureLayer, setLayerVisibility, cleanupLayers } from '../lib/layerHelpers';
|
import { ensureGeoJsonSource, ensureLayer, setLayerVisibility, cleanupLayers } from '../lib/layerHelpers';
|
||||||
import { kickRepaint, onMapStyleReady } from '../lib/mapCore';
|
import { kickRepaint, onMapStyleReady } from '../lib/mapCore';
|
||||||
|
|
||||||
|
/* ── Layer / Source IDs ─────────────────────────────────────────────── */
|
||||||
const SRC_ID = 'subcables-src';
|
const SRC_ID = 'subcables-src';
|
||||||
|
const POINTS_SRC_ID = 'subcables-pts-src';
|
||||||
|
|
||||||
|
const HITAREA_ID = 'subcables-hitarea';
|
||||||
|
const CASING_ID = 'subcables-casing';
|
||||||
const LINE_ID = 'subcables-line';
|
const LINE_ID = 'subcables-line';
|
||||||
const LINE_HOVER_ID = 'subcables-line-hover';
|
const GLOW_ID = 'subcables-glow';
|
||||||
|
const POINTS_ID = 'subcables-points';
|
||||||
const LABEL_ID = 'subcables-label';
|
const LABEL_ID = 'subcables-label';
|
||||||
|
|
||||||
|
const ALL_LAYER_IDS = [LABEL_ID, POINTS_ID, GLOW_ID, LINE_ID, CASING_ID, HITAREA_ID];
|
||||||
|
const ALL_SOURCE_IDS = [POINTS_SRC_ID, SRC_ID];
|
||||||
|
|
||||||
export function useSubcablesLayer(
|
export function useSubcablesLayer(
|
||||||
mapRef: MutableRefObject<maplibregl.Map | null>,
|
mapRef: MutableRefObject<maplibregl.Map | null>,
|
||||||
projectionBusyRef: MutableRefObject<boolean>,
|
projectionBusyRef: MutableRefObject<boolean>,
|
||||||
@ -32,6 +41,23 @@ export function useSubcablesLayer(
|
|||||||
onHoverRef.current = onHoverCable;
|
onHoverRef.current = onHoverCable;
|
||||||
onClickRef.current = onClickCable;
|
onClickRef.current = onClickCable;
|
||||||
|
|
||||||
|
/* ── Derived point features (cable midpoints for circle markers) ── */
|
||||||
|
const pointsGeoJson = useMemo<GeoJSON.FeatureCollection>(() => {
|
||||||
|
if (!subcableGeo) return { type: 'FeatureCollection', features: [] };
|
||||||
|
const features: GeoJSON.Feature<GeoJSON.Point>[] = [];
|
||||||
|
for (const f of subcableGeo.features) {
|
||||||
|
const coords = f.properties.coordinates;
|
||||||
|
if (!coords || coords.length < 2) continue;
|
||||||
|
features.push({
|
||||||
|
type: 'Feature',
|
||||||
|
properties: { id: f.properties.id, color: f.properties.color },
|
||||||
|
geometry: { type: 'Point', coordinates: coords },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return { type: 'FeatureCollection', features };
|
||||||
|
}, [subcableGeo]);
|
||||||
|
|
||||||
|
/* ── Main layer setup effect ──────────────────────────────────────── */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const map = mapRef.current;
|
const map = mapRef.current;
|
||||||
if (!map) return;
|
if (!map) return;
|
||||||
@ -39,16 +65,17 @@ export function useSubcablesLayer(
|
|||||||
const ensure = () => {
|
const ensure = () => {
|
||||||
if (projectionBusyRef.current) return;
|
if (projectionBusyRef.current) return;
|
||||||
|
|
||||||
const visibility = overlays.subcables ? 'visible' : 'none';
|
const visible = overlays.subcables;
|
||||||
setLayerVisibility(map, LINE_ID, overlays.subcables);
|
for (const id of ALL_LAYER_IDS) {
|
||||||
setLayerVisibility(map, LINE_HOVER_ID, overlays.subcables);
|
setLayerVisibility(map, id, visible);
|
||||||
setLayerVisibility(map, LABEL_ID, overlays.subcables);
|
}
|
||||||
|
|
||||||
if (!subcableGeo) return;
|
if (!subcableGeo) return;
|
||||||
if (!map.isStyleLoaded()) return;
|
if (!map.isStyleLoaded()) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ensureGeoJsonSource(map, SRC_ID, subcableGeo);
|
ensureGeoJsonSource(map, SRC_ID, subcableGeo);
|
||||||
|
ensureGeoJsonSource(map, POINTS_SRC_ID, pointsGeoJson);
|
||||||
|
|
||||||
const before = map.getLayer('zones-fill')
|
const before = map.getLayer('zones-fill')
|
||||||
? 'zones-fill'
|
? 'zones-fill'
|
||||||
@ -56,6 +83,43 @@ export function useSubcablesLayer(
|
|||||||
? 'deck-globe'
|
? 'deck-globe'
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
const vis = visible ? 'visible' : 'none';
|
||||||
|
|
||||||
|
/* 1) Hit-area — invisible wide line for easy hover detection */
|
||||||
|
ensureLayer(
|
||||||
|
map,
|
||||||
|
{
|
||||||
|
id: HITAREA_ID,
|
||||||
|
type: 'line',
|
||||||
|
source: SRC_ID,
|
||||||
|
paint: {
|
||||||
|
'line-color': 'rgba(0,0,0,0)',
|
||||||
|
'line-width': 14,
|
||||||
|
'line-opacity': 0,
|
||||||
|
},
|
||||||
|
layout: { visibility: vis, 'line-cap': 'round', 'line-join': 'round' },
|
||||||
|
} as unknown as LayerSpecification,
|
||||||
|
{ before },
|
||||||
|
);
|
||||||
|
|
||||||
|
/* 2) Dark casing behind cable for contrast */
|
||||||
|
ensureLayer(
|
||||||
|
map,
|
||||||
|
{
|
||||||
|
id: CASING_ID,
|
||||||
|
type: 'line',
|
||||||
|
source: SRC_ID,
|
||||||
|
paint: {
|
||||||
|
'line-color': 'rgba(0,0,0,0.55)',
|
||||||
|
'line-width': ['interpolate', ['linear'], ['zoom'], 2, 2.4, 6, 3.6, 10, 5.5],
|
||||||
|
'line-opacity': ['interpolate', ['linear'], ['zoom'], 2, 0.3, 5, 0.5, 8, 0.65],
|
||||||
|
},
|
||||||
|
layout: { visibility: vis, 'line-cap': 'round', 'line-join': 'round' },
|
||||||
|
} as unknown as LayerSpecification,
|
||||||
|
{ before },
|
||||||
|
);
|
||||||
|
|
||||||
|
/* 3) Main cable line — vivid color */
|
||||||
ensureLayer(
|
ensureLayer(
|
||||||
map,
|
map,
|
||||||
{
|
{
|
||||||
@ -64,31 +128,53 @@ export function useSubcablesLayer(
|
|||||||
source: SRC_ID,
|
source: SRC_ID,
|
||||||
paint: {
|
paint: {
|
||||||
'line-color': ['get', 'color'],
|
'line-color': ['get', 'color'],
|
||||||
'line-opacity': ['interpolate', ['linear'], ['zoom'], 2, 0.4, 6, 0.55, 10, 0.7],
|
'line-opacity': ['interpolate', ['linear'], ['zoom'], 2, 0.7, 6, 0.82, 10, 0.92],
|
||||||
'line-width': ['interpolate', ['linear'], ['zoom'], 2, 0.8, 6, 1.2, 10, 1.8],
|
'line-width': ['interpolate', ['linear'], ['zoom'], 2, 1.6, 6, 2.5, 10, 4.0],
|
||||||
},
|
},
|
||||||
layout: { visibility, 'line-cap': 'round', 'line-join': 'round' },
|
layout: { visibility: vis, 'line-cap': 'round', 'line-join': 'round' },
|
||||||
} as unknown as LayerSpecification,
|
} as unknown as LayerSpecification,
|
||||||
{ before },
|
{ before },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/* 4) Glow — visible only on hover */
|
||||||
ensureLayer(
|
ensureLayer(
|
||||||
map,
|
map,
|
||||||
{
|
{
|
||||||
id: LINE_HOVER_ID,
|
id: GLOW_ID,
|
||||||
type: 'line',
|
type: 'line',
|
||||||
source: SRC_ID,
|
source: SRC_ID,
|
||||||
paint: {
|
paint: {
|
||||||
'line-color': ['get', 'color'],
|
'line-color': ['get', 'color'],
|
||||||
'line-opacity': 0,
|
'line-opacity': 0,
|
||||||
'line-width': ['interpolate', ['linear'], ['zoom'], 2, 3, 6, 5, 10, 8],
|
'line-width': ['interpolate', ['linear'], ['zoom'], 2, 5, 6, 8, 10, 12],
|
||||||
|
'line-blur': ['interpolate', ['linear'], ['zoom'], 2, 3, 6, 5, 10, 7],
|
||||||
},
|
},
|
||||||
filter: ['==', ['get', 'id'], ''],
|
filter: ['==', ['get', 'id'], ''],
|
||||||
layout: { visibility, 'line-cap': 'round', 'line-join': 'round' },
|
layout: { visibility: vis, 'line-cap': 'round', 'line-join': 'round' },
|
||||||
} as unknown as LayerSpecification,
|
} as unknown as LayerSpecification,
|
||||||
{ before },
|
{ before },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/* 5) Point markers at cable representative coordinates */
|
||||||
|
ensureLayer(
|
||||||
|
map,
|
||||||
|
{
|
||||||
|
id: POINTS_ID,
|
||||||
|
type: 'circle',
|
||||||
|
source: POINTS_SRC_ID,
|
||||||
|
paint: {
|
||||||
|
'circle-radius': ['interpolate', ['linear'], ['zoom'], 2, 1.5, 6, 2.5, 10, 4],
|
||||||
|
'circle-color': ['get', 'color'],
|
||||||
|
'circle-opacity': ['interpolate', ['linear'], ['zoom'], 2, 0.5, 5, 0.7, 8, 0.85],
|
||||||
|
'circle-stroke-color': 'rgba(0,0,0,0.5)',
|
||||||
|
'circle-stroke-width': 0.5,
|
||||||
|
},
|
||||||
|
layout: { visibility: vis },
|
||||||
|
minzoom: 3,
|
||||||
|
} as unknown as LayerSpecification,
|
||||||
|
);
|
||||||
|
|
||||||
|
/* 6) Cable name label along line */
|
||||||
ensureLayer(
|
ensureLayer(
|
||||||
map,
|
map,
|
||||||
{
|
{
|
||||||
@ -96,7 +182,7 @@ export function useSubcablesLayer(
|
|||||||
type: 'symbol',
|
type: 'symbol',
|
||||||
source: SRC_ID,
|
source: SRC_ID,
|
||||||
layout: {
|
layout: {
|
||||||
visibility,
|
visibility: vis,
|
||||||
'symbol-placement': 'line',
|
'symbol-placement': 'line',
|
||||||
'text-field': ['get', 'name'],
|
'text-field': ['get', 'name'],
|
||||||
'text-size': ['interpolate', ['linear'], ['zoom'], 4, 9, 8, 11, 12, 13],
|
'text-size': ['interpolate', ['linear'], ['zoom'], 4, 9, 8, 11, 12, 13],
|
||||||
@ -106,52 +192,75 @@ export function useSubcablesLayer(
|
|||||||
'text-rotation-alignment': 'map',
|
'text-rotation-alignment': 'map',
|
||||||
},
|
},
|
||||||
paint: {
|
paint: {
|
||||||
'text-color': 'rgba(210,225,240,0.78)',
|
'text-color': 'rgba(220,232,245,0.82)',
|
||||||
'text-halo-color': 'rgba(2,6,23,0.85)',
|
'text-halo-color': 'rgba(2,6,23,0.9)',
|
||||||
'text-halo-width': 1.0,
|
'text-halo-width': 1.2,
|
||||||
'text-halo-blur': 0.6,
|
'text-halo-blur': 0.5,
|
||||||
'text-opacity': ['interpolate', ['linear'], ['zoom'], 4, 0, 5, 0.7, 8, 0.85],
|
'text-opacity': ['interpolate', ['linear'], ['zoom'], 4, 0, 5, 0.7, 8, 0.88],
|
||||||
},
|
},
|
||||||
minzoom: 4,
|
minzoom: 4,
|
||||||
} as unknown as LayerSpecification,
|
} as unknown as LayerSpecification,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update hover highlight
|
/* ── Hover highlight (flat values — no nested interpolate) ── */
|
||||||
if (hoveredCableId) {
|
if (hoveredCableId) {
|
||||||
|
const matchExpr = ['==', ['get', 'id'], hoveredCableId];
|
||||||
|
|
||||||
|
// Main line: hovered=bright+thick, rest=dimmed+thin
|
||||||
if (map.getLayer(LINE_ID)) {
|
if (map.getLayer(LINE_ID)) {
|
||||||
map.setPaintProperty(LINE_ID, 'line-opacity', [
|
map.setPaintProperty(LINE_ID, 'line-opacity', ['case', matchExpr, 1.0, 0.2] as never);
|
||||||
'case',
|
map.setPaintProperty(LINE_ID, 'line-width', ['case', matchExpr, 4.5, 1.2] as never);
|
||||||
['==', ['get', 'id'], hoveredCableId],
|
|
||||||
0.95,
|
|
||||||
['interpolate', ['linear'], ['zoom'], 2, 0.25, 6, 0.35, 10, 0.45],
|
|
||||||
] as never);
|
|
||||||
map.setPaintProperty(LINE_ID, 'line-width', [
|
|
||||||
'case',
|
|
||||||
['==', ['get', 'id'], hoveredCableId],
|
|
||||||
['interpolate', ['linear'], ['zoom'], 2, 2.0, 6, 2.8, 10, 3.5],
|
|
||||||
['interpolate', ['linear'], ['zoom'], 2, 0.6, 6, 0.9, 10, 1.4],
|
|
||||||
] as never);
|
|
||||||
}
|
}
|
||||||
if (map.getLayer(LINE_HOVER_ID)) {
|
// Casing: dim non-hovered
|
||||||
map.setFilter(LINE_HOVER_ID, ['==', ['get', 'id'], hoveredCableId] as never);
|
if (map.getLayer(CASING_ID)) {
|
||||||
map.setPaintProperty(LINE_HOVER_ID, 'line-opacity', 0.25);
|
map.setPaintProperty(CASING_ID, 'line-opacity', ['case', matchExpr, 0.7, 0.12] as never);
|
||||||
|
map.setPaintProperty(CASING_ID, 'line-width', ['case', matchExpr, 6.5, 2.0] as never);
|
||||||
|
}
|
||||||
|
// Glow: show only on hovered cable
|
||||||
|
if (map.getLayer(GLOW_ID)) {
|
||||||
|
map.setFilter(GLOW_ID, matchExpr as never);
|
||||||
|
map.setPaintProperty(GLOW_ID, 'line-opacity', 0.35);
|
||||||
|
}
|
||||||
|
// Points: dim non-hovered
|
||||||
|
if (map.getLayer(POINTS_ID)) {
|
||||||
|
map.setPaintProperty(POINTS_ID, 'circle-opacity', ['case', matchExpr, 1.0, 0.15] as never);
|
||||||
|
map.setPaintProperty(POINTS_ID, 'circle-radius', ['case', matchExpr, 4, 1.5] as never);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Restore zoom-based interpolation defaults
|
||||||
if (map.getLayer(LINE_ID)) {
|
if (map.getLayer(LINE_ID)) {
|
||||||
map.setPaintProperty(
|
map.setPaintProperty(
|
||||||
LINE_ID,
|
LINE_ID, 'line-opacity',
|
||||||
'line-opacity',
|
['interpolate', ['linear'], ['zoom'], 2, 0.7, 6, 0.82, 10, 0.92] as never,
|
||||||
['interpolate', ['linear'], ['zoom'], 2, 0.4, 6, 0.55, 10, 0.7] as never,
|
|
||||||
);
|
);
|
||||||
map.setPaintProperty(
|
map.setPaintProperty(
|
||||||
LINE_ID,
|
LINE_ID, 'line-width',
|
||||||
'line-width',
|
['interpolate', ['linear'], ['zoom'], 2, 1.6, 6, 2.5, 10, 4.0] as never,
|
||||||
['interpolate', ['linear'], ['zoom'], 2, 0.8, 6, 1.2, 10, 1.8] as never,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (map.getLayer(LINE_HOVER_ID)) {
|
if (map.getLayer(CASING_ID)) {
|
||||||
map.setFilter(LINE_HOVER_ID, ['==', ['get', 'id'], ''] as never);
|
map.setPaintProperty(
|
||||||
map.setPaintProperty(LINE_HOVER_ID, 'line-opacity', 0);
|
CASING_ID, 'line-opacity',
|
||||||
|
['interpolate', ['linear'], ['zoom'], 2, 0.3, 5, 0.5, 8, 0.65] as never,
|
||||||
|
);
|
||||||
|
map.setPaintProperty(
|
||||||
|
CASING_ID, 'line-width',
|
||||||
|
['interpolate', ['linear'], ['zoom'], 2, 2.4, 6, 3.6, 10, 5.5] as never,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (map.getLayer(GLOW_ID)) {
|
||||||
|
map.setFilter(GLOW_ID, ['==', ['get', 'id'], ''] as never);
|
||||||
|
map.setPaintProperty(GLOW_ID, 'line-opacity', 0);
|
||||||
|
}
|
||||||
|
if (map.getLayer(POINTS_ID)) {
|
||||||
|
map.setPaintProperty(
|
||||||
|
POINTS_ID, 'circle-opacity',
|
||||||
|
['interpolate', ['linear'], ['zoom'], 2, 0.5, 5, 0.7, 8, 0.85] as never,
|
||||||
|
);
|
||||||
|
map.setPaintProperty(
|
||||||
|
POINTS_ID, 'circle-radius',
|
||||||
|
['interpolate', ['linear'], ['zoom'], 2, 1.5, 6, 2.5, 10, 4] as never,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -166,15 +275,15 @@ export function useSubcablesLayer(
|
|||||||
return () => {
|
return () => {
|
||||||
stop();
|
stop();
|
||||||
};
|
};
|
||||||
}, [subcableGeo, overlays.subcables, projection, mapSyncEpoch, hoveredCableId, reorderGlobeFeatureLayers]);
|
}, [subcableGeo, pointsGeoJson, overlays.subcables, projection, mapSyncEpoch, hoveredCableId, reorderGlobeFeatureLayers]);
|
||||||
|
|
||||||
// Mouse events
|
/* ── Mouse events (bind to hit-area layer for easy hovering) ───── */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const map = mapRef.current;
|
const map = mapRef.current;
|
||||||
if (!map) return;
|
if (!map) return;
|
||||||
if (!overlays.subcables) return;
|
if (!overlays.subcables) return;
|
||||||
|
|
||||||
const onMouseEnter = (e: maplibregl.MapMouseEvent & { features?: maplibregl.MapGeoJSONFeature[] }) => {
|
const onMouseMove = (e: maplibregl.MapMouseEvent & { features?: maplibregl.MapGeoJSONFeature[] }) => {
|
||||||
const cableId = e.features?.[0]?.properties?.id;
|
const cableId = e.features?.[0]?.properties?.id;
|
||||||
if (typeof cableId === 'string' && cableId) {
|
if (typeof cableId === 'string' && cableId) {
|
||||||
map.getCanvas().style.cursor = 'pointer';
|
map.getCanvas().style.cursor = 'pointer';
|
||||||
@ -195,13 +304,15 @@ export function useSubcablesLayer(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const addEvents = () => {
|
const addEvents = () => {
|
||||||
if (!map.getLayer(LINE_ID)) return;
|
// Bind to hit-area for wider hover target, fallback to main line
|
||||||
map.on('mouseenter', LINE_ID, onMouseEnter);
|
const targetLayer = map.getLayer(HITAREA_ID) ? HITAREA_ID : LINE_ID;
|
||||||
map.on('mouseleave', LINE_ID, onMouseLeave);
|
if (!map.getLayer(targetLayer)) return;
|
||||||
map.on('click', LINE_ID, onClick);
|
map.on('mousemove', targetLayer, onMouseMove);
|
||||||
|
map.on('mouseleave', targetLayer, onMouseLeave);
|
||||||
|
map.on('click', targetLayer, onClick);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (map.isStyleLoaded() && map.getLayer(LINE_ID)) {
|
if (map.isStyleLoaded() && (map.getLayer(HITAREA_ID) || map.getLayer(LINE_ID))) {
|
||||||
addEvents();
|
addEvents();
|
||||||
} else {
|
} else {
|
||||||
map.once('idle', addEvents);
|
map.once('idle', addEvents);
|
||||||
@ -209,7 +320,10 @@ export function useSubcablesLayer(
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
try {
|
try {
|
||||||
map.off('mouseenter', LINE_ID, onMouseEnter);
|
map.off('mousemove', HITAREA_ID, onMouseMove);
|
||||||
|
map.off('mouseleave', HITAREA_ID, onMouseLeave);
|
||||||
|
map.off('click', HITAREA_ID, onClick);
|
||||||
|
map.off('mousemove', LINE_ID, onMouseMove);
|
||||||
map.off('mouseleave', LINE_ID, onMouseLeave);
|
map.off('mouseleave', LINE_ID, onMouseLeave);
|
||||||
map.off('click', LINE_ID, onClick);
|
map.off('click', LINE_ID, onClick);
|
||||||
} catch {
|
} catch {
|
||||||
@ -218,12 +332,12 @@ export function useSubcablesLayer(
|
|||||||
};
|
};
|
||||||
}, [overlays.subcables, mapSyncEpoch]);
|
}, [overlays.subcables, mapSyncEpoch]);
|
||||||
|
|
||||||
// Cleanup on unmount
|
/* ── Cleanup on unmount ───────────────────────────────────────────── */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const mapInstance = mapRef.current;
|
const mapInstance = mapRef.current;
|
||||||
return () => {
|
return () => {
|
||||||
if (!mapInstance) return;
|
if (!mapInstance) return;
|
||||||
cleanupLayers(mapInstance, [LABEL_ID, LINE_HOVER_ID, LINE_ID], [SRC_ID]);
|
cleanupLayers(mapInstance, ALL_LAYER_IDS, ALL_SOURCE_IDS);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,8 +37,11 @@ const GLOBE_NATIVE_LAYER_IDS = [
|
|||||||
'fleet-circles-ml-fill',
|
'fleet-circles-ml-fill',
|
||||||
'fleet-circles-ml',
|
'fleet-circles-ml',
|
||||||
'pair-range-ml',
|
'pair-range-ml',
|
||||||
|
'subcables-hitarea',
|
||||||
|
'subcables-casing',
|
||||||
'subcables-line',
|
'subcables-line',
|
||||||
'subcables-line-hover',
|
'subcables-glow',
|
||||||
|
'subcables-points',
|
||||||
'subcables-label',
|
'subcables-label',
|
||||||
'deck-globe',
|
'deck-globe',
|
||||||
];
|
];
|
||||||
@ -52,6 +55,7 @@ const GLOBE_NATIVE_SOURCE_IDS = [
|
|||||||
'fleet-circles-ml-fill-src',
|
'fleet-circles-ml-fill-src',
|
||||||
'pair-range-ml-src',
|
'pair-range-ml-src',
|
||||||
'subcables-src',
|
'subcables-src',
|
||||||
|
'subcables-pts-src',
|
||||||
];
|
];
|
||||||
|
|
||||||
export function clearGlobeNativeLayers(map: maplibregl.Map) {
|
export function clearGlobeNativeLayers(map: maplibregl.Map) {
|
||||||
|
|||||||
불러오는 중...
Reference in New Issue
Block a user