fix(map): Globe 사진 인디케이터 네이티브 레이어 전환
- Globe Deck.gl ScatterplotLayer 아티팩트(파란 막대) 수정 - MapLibre 네이티브 circle 레이어로 사진 인디케이터 구현
This commit is contained in:
부모
e72e2f14f6
커밋
d5a8be3b96
@ -347,32 +347,10 @@ export function useDeckLayers(
|
||||
if (!deckTarget) return;
|
||||
|
||||
if (!ENABLE_GLOBE_DECK_OVERLAYS) {
|
||||
// Ship photo indicator: 사진 유무 표시 (ScatterplotLayer)
|
||||
const photoLayers: unknown[] = [];
|
||||
if (settings.showShips && overlays.shipPhotos && shipPhotoTargets.length > 0) {
|
||||
photoLayers.push(
|
||||
new ScatterplotLayer<AisTarget>({
|
||||
id: 'ship-photo-indicator',
|
||||
data: shipPhotoTargets,
|
||||
pickable: true,
|
||||
billboard: false,
|
||||
filled: true,
|
||||
stroked: true,
|
||||
radiusUnits: 'pixels',
|
||||
getRadius: 5,
|
||||
getFillColor: [0, 188, 212, 180],
|
||||
getLineColor: [255, 255, 255, 200],
|
||||
lineWidthUnits: 'pixels',
|
||||
getLineWidth: 1,
|
||||
getPosition: (d) => [d.lon, d.lat] as [number, number],
|
||||
onClick: (info: PickingInfo) => {
|
||||
if (info.object) onClickShipPhoto?.((info.object as AisTarget).mmsi);
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
// Globe에서는 Deck.gl ScatterplotLayer가 프로젝션 공간 아티팩트(막대)를 유발하므로
|
||||
// 빈 레이어만 설정. 사진 인디케이터는 Mercator에서만 동작.
|
||||
try {
|
||||
deckTarget.setProps({ layers: sanitizeDeckLayerList(photoLayers), getTooltip: undefined, onClick: undefined } as never);
|
||||
deckTarget.setProps({ layers: [], getTooltip: undefined, onClick: undefined } as never);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
@ -439,8 +417,5 @@ export function useDeckLayers(
|
||||
toFleetMmsiList,
|
||||
touchDeckHoverState,
|
||||
legacyHits,
|
||||
shipPhotoTargets,
|
||||
onClickShipPhoto,
|
||||
overlays.shipPhotos,
|
||||
]);
|
||||
}
|
||||
|
||||
@ -120,6 +120,7 @@ export function useGlobeShipLayers(
|
||||
alarmKind: alarmKind ?? '',
|
||||
alarmBadgeLabel: alarmKind ? ALARM_BADGE[alarmKind].label : '',
|
||||
alarmBadgeColor: alarmKind ? ALARM_BADGE[alarmKind].color : '#000',
|
||||
hasPhoto: t.shipImagePath ? 1 : 0,
|
||||
},
|
||||
};
|
||||
}),
|
||||
@ -167,13 +168,14 @@ export function useGlobeShipLayers(
|
||||
const symbolLiteId = 'ships-globe-lite';
|
||||
const symbolId = 'ships-globe';
|
||||
const labelId = 'ships-globe-label';
|
||||
const photoId = 'ships-globe-photo';
|
||||
const pulseId = 'ships-globe-alarm-pulse';
|
||||
const badgeId = 'ships-globe-alarm-badge';
|
||||
|
||||
// 레이어를 제거하지 않고 visibility만 'none'으로 설정
|
||||
// guardedSetVisibility로 현재 값과 동일하면 호출 생략 (style._changed 방지)
|
||||
const hide = () => {
|
||||
for (const id of [badgeId, labelId, symbolId, symbolLiteId, pulseId, outlineId, haloId]) {
|
||||
for (const id of [badgeId, photoId, labelId, symbolId, symbolLiteId, pulseId, outlineId, haloId]) {
|
||||
guardedSetVisibility(map, id, 'none');
|
||||
}
|
||||
};
|
||||
@ -197,6 +199,7 @@ export function useGlobeShipLayers(
|
||||
// → style._changed 방지 → 불필요한 symbol placement 재계산 방지 → 라벨 사라짐 방지
|
||||
const visibility: 'visible' | 'none' = projection === 'globe' ? 'visible' : 'none';
|
||||
const labelVisibility: 'visible' | 'none' = projection === 'globe' && overlays.shipLabels ? 'visible' : 'none';
|
||||
const photoVisibility: 'visible' | 'none' = projection === 'globe' && overlays.shipPhotos ? 'visible' : 'none';
|
||||
if (map.getLayer(symbolId) || map.getLayer(symbolLiteId)) {
|
||||
const changed =
|
||||
map.getLayoutProperty(symbolId, 'visibility') !== visibility ||
|
||||
@ -208,6 +211,7 @@ export function useGlobeShipLayers(
|
||||
if (projection === 'globe') kickRepaint(map);
|
||||
}
|
||||
guardedSetVisibility(map, labelId, labelVisibility);
|
||||
guardedSetVisibility(map, photoId, photoVisibility);
|
||||
}
|
||||
|
||||
// 데이터 업데이트는 projectionBusy 중에는 차단
|
||||
@ -512,6 +516,35 @@ export function useGlobeShipLayers(
|
||||
}
|
||||
}
|
||||
|
||||
// Photo indicator circle (above ship icons, below labels)
|
||||
if (!map.getLayer(photoId)) {
|
||||
needReorder = true;
|
||||
try {
|
||||
map.addLayer(
|
||||
{
|
||||
id: photoId,
|
||||
type: 'circle',
|
||||
source: srcId,
|
||||
filter: ['==', ['get', 'hasPhoto'], 1] as never,
|
||||
layout: { visibility: photoVisibility },
|
||||
paint: {
|
||||
'circle-radius': [
|
||||
'interpolate', ['linear'], ['zoom'],
|
||||
3, 3, 7, 4, 10, 5, 14, 6,
|
||||
] as never,
|
||||
'circle-color': 'rgba(0, 188, 212, 0.7)',
|
||||
'circle-stroke-color': 'rgba(255, 255, 255, 0.8)',
|
||||
'circle-stroke-width': 1,
|
||||
'circle-translate': [8, -8],
|
||||
},
|
||||
} as unknown as LayerSpecification,
|
||||
before,
|
||||
);
|
||||
} catch (e) {
|
||||
console.warn('Ship photo indicator layer add failed:', e);
|
||||
}
|
||||
}
|
||||
|
||||
const labelFilter = [
|
||||
'all',
|
||||
['!=', ['to-string', ['coalesce', ['get', 'labelName'], '']], ''],
|
||||
@ -611,6 +644,7 @@ export function useGlobeShipLayers(
|
||||
projection,
|
||||
settings.showShips,
|
||||
overlays.shipLabels,
|
||||
overlays.shipPhotos,
|
||||
globeShipGeoJson,
|
||||
alarmGeoJson,
|
||||
mapSyncEpoch,
|
||||
|
||||
불러오는 중...
Reference in New Issue
Block a user