import { useState } from 'react'; import { DndContext } from '@dnd-kit/core'; import { useDraggable } from '@dnd-kit/core'; import type { DragEndEvent } from '@dnd-kit/core'; /** * 해양환경관리법 제22조 기반 선박 발생 오염물 배출 규정 * 영해기선으로부터의 거리에 따라 배출 가능 여부 결정 * * 법률 근거: * https://lbox.kr/v2/statute/%ED%95%B4%EC%96%91%ED%99%98%EA%B2%BD%EA%B4%80%EB%A6%AC%EB%B2%95/%EB%B3%B8%EB%AC%B8%20%3E%20%EC%A0%9C3%EC%9E%A5%20%3E%20%EC%A0%9C1%EC%A0%88%20%3E%20%EC%A0%9C22%EC%A1%B0 * 선박에서의 오염방지에 관한 규칙 제8조[별표 2] 및 제14조 */ type Status = 'forbidden' | 'allowed' | 'conditional'; interface DischargeRule { category: string; item: string; zones: [Status, Status, Status, Status, Status]; // [~3NM, 3~12NM, 12~25NM, 25~50NM, 50NM+] condition?: string; } const RULES: DischargeRule[] = [ // 분뇨 { category: '분뇨', item: '분뇨마쇄소독장치', zones: ['forbidden', 'conditional', 'conditional', 'conditional', 'conditional'], condition: '항속 4노트 이상시 서서히 배출 / 400톤 미만 국내항해 선박은 3해리 이내 가능', }, { category: '분뇨', item: '분뇨저장탱크', zones: ['forbidden', 'forbidden', 'conditional', 'conditional', 'conditional'], condition: '항속 4노트 이상시 서서히 배출', }, { category: '분뇨', item: '분뇨처리장치', zones: ['allowed', 'allowed', 'allowed', 'allowed', 'allowed'], condition: '수산자원보호구역, 보호수면 및 육성수면은 불가', }, // 음식물찌꺼기 { category: '음식물찌꺼기', item: '미분쇄 음식물', zones: ['forbidden', 'forbidden', 'allowed', 'allowed', 'allowed'], }, { category: '음식물찌꺼기', item: '분쇄·연마 음식물 (25mm 이하)', zones: ['forbidden', 'conditional', 'allowed', 'allowed', 'allowed'], condition: '25mm 이하 개구 스크린 통과 가능시', }, // 화물잔류물 { category: '화물잔류물', item: '부유성 화물잔류물', zones: ['forbidden', 'forbidden', 'forbidden', 'allowed', 'allowed'], }, { category: '화물잔류물', item: '침강성 화물잔류물', zones: ['forbidden', 'forbidden', 'allowed', 'allowed', 'allowed'], }, { category: '화물잔류물', item: '화물창 세정수', zones: ['forbidden', 'forbidden', 'conditional', 'conditional', 'conditional'], condition: '해양환경에 해롭지 않은 일반세제 사용시', }, // 화물유 { category: '화물유', item: '화물유 섞인 평형수·세정수·선저폐수', zones: ['forbidden', 'forbidden', 'forbidden', 'forbidden', 'conditional'], condition: '항해 중, 순간배출률 1해리당 30L 이하, 기름오염방지설비 작동 중', }, // 유해액체물질 { category: '유해액체물질', item: '유해액체물질 섞인 세정수', zones: ['forbidden', 'forbidden', 'conditional', 'conditional', 'conditional'], condition: '자항선 7노트/비자항선 4노트 이상, 수심 25m 이상, 수면하 배출구 사용', }, // 폐기물 { category: '폐기물', item: '플라스틱 제품', zones: ['forbidden', 'forbidden', 'forbidden', 'forbidden', 'forbidden'], }, { category: '폐기물', item: '포장유해물질·용기', zones: ['forbidden', 'forbidden', 'forbidden', 'forbidden', 'forbidden'], }, { category: '폐기물', item: '중금속 포함 쓰레기', zones: ['forbidden', 'forbidden', 'forbidden', 'forbidden', 'forbidden'], }, ]; const ZONE_LABELS = ['~3해리', '3~12해리', '12~25해리', '25~50해리', '50해리+']; const ZONE_COLORS = [ 'var(--color-danger)', 'var(--color-warning)', 'var(--color-caution)', 'var(--color-success)', 'var(--fg-disabled)', ]; function StatusBadge({ status }: { status: Status }) { if (status === 'forbidden') return ( 배출불가 ); return ( {status === 'allowed' ? '배출가능' : '조건부'} ); } interface DischargeZonePanelProps { lat: number; lon: number; distanceNm: number; zoneIndex: number; onClose: () => void; } export function DischargeZonePanel(props: DischargeZonePanelProps) { const [offset, setOffset] = useState({ x: 0, y: 0 }); function handleDragEnd(event: DragEndEvent) { setOffset((prev) => ({ x: prev.x + event.delta.x, y: prev.y + event.delta.y })); } return ( ); } function DraggablePanel({ lat, lon, distanceNm, zoneIndex, onClose, offset, }: DischargeZonePanelProps & { offset: { x: number; y: number } }) { const zoneIdx = zoneIndex; const [expandedCat, setExpandedCat] = useState(null); const { attributes, listeners, setNodeRef, transform } = useDraggable({ id: 'discharge-panel', }); const tx = offset.x + (transform?.x ?? 0); const ty = offset.y + (transform?.y ?? 0); const categories = [...new Set(RULES.map((r) => r.category))]; return (
{/* Header — drag handle */}
🚢 오염물 배출 규정
해양환경관리법 제22조
e.stopPropagation()} className="text-title-3 cursor-pointer text-fg-sub hover:text-fg" style={{ pointerEvents: 'all' }} > ✕
{/* Location Info */}
선택 위치 {lat.toFixed(4)}°N, {lon.toFixed(4)}°E
영해기선 거리 {distanceNm.toFixed(1)} NM
{/* Zone indicator */}
{ZONE_LABELS.map((label, i) => (
{label}
))}
{/* Rules */}
{categories.map((cat) => { const catRules = RULES.filter((r) => r.category === cat); const isExpanded = expandedCat === cat; const allForbidden = catRules.every((r) => r.zones[zoneIdx] === 'forbidden'); const summaryColor = allForbidden ? 'var(--color-danger)' : 'var(--fg-sub)'; return (
setExpandedCat(isExpanded ? null : cat)} style={{ padding: '8px 14px' }} >
{cat}
{allForbidden ? '전체 불가' : '허용'} {isExpanded ? '▾' : '▸'}
{isExpanded && (
{catRules.map((rule, i) => (
{rule.item}
))} {catRules.some((r) => r.condition && r.zones[zoneIdx] !== 'forbidden') && (
{catRules .filter((r) => r.condition && r.zones[zoneIdx] !== 'forbidden') .map((r, i) => (
💡 {r.item}: {r.condition}
))}
)}
)}
); })}
{/* Footer */}
※ 거리는 영해기선 폴리곤 기준입니다. 구역은 버퍼 폴리곤 포함 여부로 판별됩니다.
); }