- deck.gl 9.2 설치 + DeckGLOverlay(MapboxOverlay interleaved) 통합 - 정적 마커 11종 → useStaticDeckLayers (IconLayer/TextLayer, SVG DataURI) - 분석 오버레이 → useAnalysisDeckLayers (ScatterplotLayer/TextLayer) - 불법어선/어구/수역 라벨 → deck.gl ScatterplotLayer/TextLayer - 줌 레벨별 스케일 (0~6: 0.6x, 7~9: 1.0x, 10~12: 1.4x, 13+: 1.8x) - NK 미사일 궤적 PathLayer 추가 + 정적 마커 클릭 Popup - 해저케이블 날짜변경선(180도) 좌표 보정 - 기존 DOM Marker 제거로 렌더링 성능 대폭 개선 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
64 lines
2.6 KiB
TypeScript
64 lines
2.6 KiB
TypeScript
import { Popup } from 'react-map-gl/maplibre';
|
|
import type { GovBuilding } from '../../data/govBuildings';
|
|
|
|
const COUNTRY_STYLE: Record<string, { color: string; flag: string; label: string }> = {
|
|
CN: { color: '#ef4444', flag: '🇨🇳', label: '중국' },
|
|
JP: { color: '#f472b6', flag: '🇯🇵', label: '일본' },
|
|
};
|
|
|
|
const TYPE_STYLE: Record<string, { icon: string; label: string; color: string }> = {
|
|
executive: { icon: '🏛️', label: '행정부', color: '#f59e0b' },
|
|
legislature: { icon: '🏛️', label: '입법부', color: '#a78bfa' },
|
|
military_hq: { icon: '⭐', label: '군사본부', color: '#ef4444' },
|
|
intelligence: { icon: '🔍', label: '정보기관', color: '#6366f1' },
|
|
foreign: { icon: '🌐', label: '외교부', color: '#3b82f6' },
|
|
maritime: { icon: '⚓', label: '해양기관', color: '#06b6d4' },
|
|
defense: { icon: '🛡️', label: '국방부', color: '#dc2626' },
|
|
};
|
|
|
|
interface Props {
|
|
selected: GovBuilding | null;
|
|
onClose: () => void;
|
|
}
|
|
|
|
export function GovBuildingLayer({ selected, onClose }: Props) {
|
|
if (!selected) return null;
|
|
const cs = COUNTRY_STYLE[selected.country] || COUNTRY_STYLE.CN;
|
|
const ts = TYPE_STYLE[selected.type] || TYPE_STYLE.executive;
|
|
return (
|
|
<Popup longitude={selected.lng} latitude={selected.lat}
|
|
onClose={onClose} closeOnClick={false}
|
|
anchor="bottom" maxWidth="320px" className="gl-popup">
|
|
<div className="popup-body-sm" style={{ minWidth: 240 }}>
|
|
<div className="popup-header" style={{ background: ts.color, color: '#fff', gap: 6, padding: '6px 10px' }}>
|
|
<span style={{ fontSize: 16 }}>{cs.flag}</span>
|
|
<strong style={{ fontSize: 13 }}>{ts.icon} {selected.nameKo}</strong>
|
|
</div>
|
|
<div style={{ display: 'flex', gap: 4, marginBottom: 6 }}>
|
|
<span style={{
|
|
background: ts.color, color: '#fff',
|
|
padding: '2px 8px', borderRadius: 3, fontSize: 10, fontWeight: 700,
|
|
}}>
|
|
{ts.label}
|
|
</span>
|
|
<span style={{
|
|
background: cs.color, color: '#fff',
|
|
padding: '2px 8px', borderRadius: 3, fontSize: 10, fontWeight: 700,
|
|
}}>
|
|
{cs.label}
|
|
</span>
|
|
</div>
|
|
<div style={{ fontSize: 11, color: '#ccc', lineHeight: 1.4, marginBottom: 6 }}>
|
|
{selected.description}
|
|
</div>
|
|
<div style={{ fontSize: 11, color: 'var(--kcg-muted)', marginBottom: 4 }}>
|
|
{selected.name}
|
|
</div>
|
|
<div style={{ fontSize: 10, color: '#999' }}>
|
|
{selected.lat.toFixed(4)}°N, {selected.lng.toFixed(4)}°E
|
|
</div>
|
|
</div>
|
|
</Popup>
|
|
);
|
|
}
|