109 lines
4.7 KiB
TypeScript
109 lines
4.7 KiB
TypeScript
import { useState } from 'react';
|
|
import { Marker, Popup } from 'react-map-gl/maplibre';
|
|
import { MILITARY_BASES } from '../../data/militaryBases';
|
|
import type { MilitaryBase } from '../../data/militaryBases';
|
|
|
|
const COUNTRY_STYLE: Record<string, { color: string; flag: string; label: string }> = {
|
|
CN: { color: '#ef4444', flag: '🇨🇳', label: '중국' },
|
|
JP: { color: '#f472b6', flag: '🇯🇵', label: '일본' },
|
|
KP: { color: '#f97316', flag: '🇰🇵', label: '북한' },
|
|
TW: { color: '#10b981', flag: '🇹🇼', label: '대만' },
|
|
};
|
|
|
|
const TYPE_STYLE: Record<string, { icon: string; label: string; color: string }> = {
|
|
naval: { icon: '⚓', label: '해군기지', color: '#3b82f6' },
|
|
airforce: { icon: '✈️', label: '공군기지', color: '#f59e0b' },
|
|
army: { icon: '🪖', label: '육군기지', color: '#22c55e' },
|
|
missile: { icon: '🚀', label: '미사일기지', color: '#ef4444' },
|
|
joint: { icon: '⭐', label: '합동기지', color: '#a78bfa' },
|
|
};
|
|
|
|
function _MilIcon({ type, size = 16 }: { type: string; size?: number }) {
|
|
const ts = TYPE_STYLE[type] || TYPE_STYLE.army;
|
|
return (
|
|
<svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
<polygon points="12,2 22,8 22,16 12,22 2,16 2,8" fill="rgba(0,0,0,0.6)" stroke={ts.color} strokeWidth="1.5" />
|
|
<text x="12" y="14" textAnchor="middle" fontSize="9" fill={ts.color}>{ts.icon}</text>
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
export function MilitaryBaseLayer() {
|
|
const [selected, setSelected] = useState<MilitaryBase | null>(null);
|
|
|
|
return (
|
|
<>
|
|
{MILITARY_BASES.map(base => {
|
|
const _cs = COUNTRY_STYLE[base.country] || COUNTRY_STYLE.CN;
|
|
const ts = TYPE_STYLE[base.type] || TYPE_STYLE.army;
|
|
return (
|
|
<Marker key={base.id} longitude={base.lng} latitude={base.lat} anchor="center"
|
|
onClick={(e) => { e.originalEvent.stopPropagation(); setSelected(base); }}>
|
|
<div
|
|
className="flex flex-col items-center cursor-pointer"
|
|
style={{ filter: `drop-shadow(0 0 3px ${ts.color}88)` }}
|
|
>
|
|
<div style={{
|
|
width: 18, height: 18, borderRadius: 3,
|
|
background: 'rgba(0,0,0,0.7)', border: `1.5px solid ${ts.color}`,
|
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
fontSize: 10,
|
|
}}>
|
|
{ts.icon}
|
|
</div>
|
|
<div style={{
|
|
fontSize: 5, color: ts.color, marginTop: 1,
|
|
textShadow: '0 0 3px #000, 0 0 3px #000',
|
|
whiteSpace: 'nowrap', fontWeight: 700,
|
|
}}>
|
|
{base.nameKo.length > 12 ? base.nameKo.slice(0, 12) + '..' : base.nameKo}
|
|
</div>
|
|
</div>
|
|
</Marker>
|
|
);
|
|
})}
|
|
|
|
{selected && (() => {
|
|
const cs = COUNTRY_STYLE[selected.country] || COUNTRY_STYLE.CN;
|
|
const ts = TYPE_STYLE[selected.type] || TYPE_STYLE.army;
|
|
return (
|
|
<Popup longitude={selected.lng} latitude={selected.lat}
|
|
onClose={() => setSelected(null)} closeOnClick={false}
|
|
anchor="bottom" maxWidth="300px" className="gl-popup">
|
|
<div className="popup-body-sm" style={{ minWidth: 220 }}>
|
|
<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: 'var(--kcg-text)', marginBottom: 6, lineHeight: 1.4 }}>
|
|
{selected.description}
|
|
</div>
|
|
<div className="popup-grid" style={{ gap: '2px 12px' }}>
|
|
<div><span className="popup-label">시설명 : </span><strong>{selected.name}</strong></div>
|
|
<div><span className="popup-label">유형 : </span>{ts.label}</div>
|
|
</div>
|
|
<div style={{ marginTop: 6, fontSize: 10, color: '#999' }}>
|
|
{selected.lat.toFixed(4)}°N, {selected.lng.toFixed(4)}°E
|
|
</div>
|
|
</div>
|
|
</Popup>
|
|
);
|
|
})()}
|
|
</>
|
|
);
|
|
}
|