121 lines
4.6 KiB
TypeScript
121 lines
4.6 KiB
TypeScript
import { useState } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { Marker, Popup } from 'react-map-gl/maplibre';
|
|
import { COAST_GUARD_FACILITIES, CG_TYPE_LABEL } from '../../services/coastGuard';
|
|
import type { CoastGuardFacility, CoastGuardType } from '../../services/coastGuard';
|
|
|
|
const TYPE_COLOR: Record<CoastGuardType, string> = {
|
|
hq: '#ff6b6b',
|
|
regional: '#ffa94d',
|
|
station: '#4dabf7',
|
|
substation: '#69db7c',
|
|
vts: '#da77f2',
|
|
};
|
|
|
|
const TYPE_SIZE: Record<CoastGuardType, number> = {
|
|
hq: 24,
|
|
regional: 20,
|
|
station: 16,
|
|
substation: 13,
|
|
vts: 14,
|
|
};
|
|
|
|
/** 해경 로고 SVG — 작은 방패+앵커 심볼 */
|
|
function CoastGuardIcon({ type, size }: { type: CoastGuardType; size: number }) {
|
|
const color = TYPE_COLOR[type];
|
|
const isVts = type === 'vts';
|
|
|
|
if (isVts) {
|
|
return (
|
|
<svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
<circle cx="12" cy="12" r="10" fill="rgba(0,0,0,0.5)" stroke={color} strokeWidth="1.2" />
|
|
<line x1="12" y1="18" x2="12" y2="10" stroke={color} strokeWidth="1.5" />
|
|
<circle cx="12" cy="9" r="2" fill="none" stroke={color} strokeWidth="1" />
|
|
<path d="M7 7 Q12 3 17 7" fill="none" stroke={color} strokeWidth="0.8" opacity="0.6" />
|
|
<path d="M9 5.5 Q12 3 15 5.5" fill="none" stroke={color} strokeWidth="0.8" opacity="0.4" />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
<path d="M12 2 L20 6 L20 13 Q20 19 12 22 Q4 19 4 13 L4 6 Z"
|
|
fill="rgba(0,0,0,0.6)" stroke={color} strokeWidth="1.2" />
|
|
<circle cx="12" cy="9" r="2" fill="none" stroke="#fff" strokeWidth="1" />
|
|
<line x1="12" y1="11" x2="12" y2="18" stroke="#fff" strokeWidth="1" />
|
|
<path d="M8 16 Q12 20 16 16" fill="none" stroke="#fff" strokeWidth="1" />
|
|
<line x1="9" y1="13" x2="15" y2="13" stroke="#fff" strokeWidth="0.8" />
|
|
{(type === 'hq' || type === 'regional') && (
|
|
<circle cx="12" cy="9" r="1" fill={color} />
|
|
)}
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
export function CoastGuardLayer() {
|
|
const [selected, setSelected] = useState<CoastGuardFacility | null>(null);
|
|
const { t } = useTranslation();
|
|
|
|
return (
|
|
<>
|
|
{COAST_GUARD_FACILITIES.map(f => {
|
|
const size = TYPE_SIZE[f.type];
|
|
return (
|
|
<Marker key={f.id} longitude={f.lng} latitude={f.lat} anchor="center"
|
|
onClick={(e) => { e.originalEvent.stopPropagation(); setSelected(f); }}>
|
|
<div style={{
|
|
cursor: 'pointer',
|
|
filter: `drop-shadow(0 0 3px ${TYPE_COLOR[f.type]}66)`,
|
|
}} className="flex flex-col items-center">
|
|
<CoastGuardIcon type={f.type} size={size} />
|
|
{(f.type === 'hq' || f.type === 'regional') && (
|
|
<div style={{
|
|
fontSize: 6,
|
|
textShadow: `0 0 3px ${TYPE_COLOR[f.type]}, 0 0 2px #000`,
|
|
}} className="mt-px whitespace-nowrap font-bold text-white">
|
|
{f.name.replace('해양경찰청', '').replace('지방', '').trim() || '본청'}
|
|
</div>
|
|
)}
|
|
{f.type === 'vts' && (
|
|
<div style={{
|
|
fontSize: 5,
|
|
textShadow: '0 0 3px #da77f2, 0 0 2px #000',
|
|
}} className="whitespace-nowrap font-bold tracking-wider text-[#da77f2]">
|
|
VTS
|
|
</div>
|
|
)}
|
|
</div>
|
|
</Marker>
|
|
);
|
|
})}
|
|
|
|
{selected && (
|
|
<Popup longitude={selected.lng} latitude={selected.lat}
|
|
onClose={() => setSelected(null)} closeOnClick={false}
|
|
anchor="bottom" maxWidth="280px" className="gl-popup">
|
|
<div className="min-w-[200px] font-mono text-xs">
|
|
<div style={{
|
|
background: TYPE_COLOR[selected.type],
|
|
}} className="-mx-2.5 -mt-2.5 mb-2 flex items-center gap-1.5 rounded-t px-2 py-1 text-[13px] font-bold text-black">
|
|
{selected.name}
|
|
</div>
|
|
<div className="mb-1.5 flex flex-wrap gap-1">
|
|
<span style={{
|
|
background: TYPE_COLOR[selected.type],
|
|
}} className="rounded-sm px-1.5 py-px text-[10px] font-bold text-black">
|
|
{CG_TYPE_LABEL[selected.type]}
|
|
</span>
|
|
<span className="rounded-sm bg-kcg-card px-1.5 py-px text-[10px] font-bold text-[#4dabf7]">
|
|
{t('coastGuard.agency')}
|
|
</span>
|
|
</div>
|
|
<div className="text-[9px] text-kcg-dim">
|
|
{selected.lat.toFixed(4)}°N, {selected.lng.toFixed(4)}°E
|
|
</div>
|
|
</div>
|
|
</Popup>
|
|
)}
|
|
</>
|
|
);
|
|
}
|