import { create } from 'zustand' import { api } from '../services/api' import { haversineDistance, polygonAreaKm2 } from '../utils/geo' export interface MapTypeItem { mapKey: string; mapNm: string; mapLevelCd: string | null; } export type MeasureMode = 'distance' | 'area' | null; export interface MeasurePoint { lat: number; lon: number; } export interface MeasureResult { id: string; mode: 'distance' | 'area'; points: MeasurePoint[]; value: number; // distance(m) or area(km²) } interface MapToggles { s57: boolean; s101: boolean; threeD: boolean; satellite: boolean; } interface MapState { mapToggles: MapToggles; mapTypes: MapTypeItem[]; toggleMap: (key: keyof MapToggles) => void; loadMapTypes: () => Promise; // 측정 measureMode: MeasureMode; measureInProgress: MeasurePoint[]; measurements: MeasureResult[]; setMeasureMode: (mode: MeasureMode) => void; addMeasurePoint: (pt: MeasurePoint) => void; commitAreaMeasurement: () => void; removeMeasurement: (id: string) => void; clearAllMeasurements: () => void; } const DEFAULT_MAP_TYPES: MapTypeItem[] = [ { mapKey: 's57', mapNm: 'S-57 전자해도', mapLevelCd: 'S-57' }, { mapKey: 's101', mapNm: 'S-101 전자해도', mapLevelCd: 'S-101' }, { mapKey: 'threeD', mapNm: '3D 지도', mapLevelCd: '3D' }, { mapKey: 'satellite', mapNm: '위성 영상', mapLevelCd: 'SAT' }, ] let measureIdCounter = 0; export const useMapStore = create((set, get) => ({ mapToggles: { s57: false, s101: false, threeD: false, satellite: false }, mapTypes: DEFAULT_MAP_TYPES, toggleMap: (key) => set((s) => { const isCurrentlyOn = s.mapToggles[key]; const allOff: MapToggles = { s57: false, s101: false, threeD: false, satellite: false }; return { mapToggles: isCurrentlyOn ? allOff : { ...allOff, [key]: true }, }; }), loadMapTypes: async () => { try { const res = await api.get('/map-base/active') const types = res.data const current = get().mapToggles const newToggles: Partial = {} for (const t of types) { if (t.mapKey in current) { newToggles[t.mapKey as keyof MapToggles] = current[t.mapKey as keyof MapToggles] ?? false } } // 모든 토글 기본 off (기본지도 표시) set({ mapTypes: types, mapToggles: { ...current, ...newToggles } }) } catch { // API 실패 시 fallback 유지 } }, // 측정 measureMode: null, measureInProgress: [], measurements: [], setMeasureMode: (mode) => set({ measureMode: mode, measureInProgress: [] }), addMeasurePoint: (pt) => { const { measureMode, measureInProgress } = get(); if (measureMode === 'distance') { const next = [...measureInProgress, pt]; if (next.length >= 2) { const dist = haversineDistance(next[0], next[1]); const id = `measure-${++measureIdCounter}`; set((s) => ({ measurements: [...s.measurements, { id, mode: 'distance', points: [next[0], next[1]], value: dist }], measureInProgress: [], })); } else { set({ measureInProgress: next }); } } else if (measureMode === 'area') { set({ measureInProgress: [...measureInProgress, pt] }); } }, commitAreaMeasurement: () => { const { measureInProgress } = get(); if (measureInProgress.length < 3) return; const area = polygonAreaKm2(measureInProgress); const id = `measure-${++measureIdCounter}`; set((s) => ({ measurements: [...s.measurements, { id, mode: 'area', points: [...measureInProgress], value: area }], measureInProgress: [], })); }, removeMeasurement: (id) => set((s) => ({ measurements: s.measurements.filter((m) => m.id !== id) })), clearAllMeasurements: () => set({ measurements: [], measureInProgress: [], measureMode: null }), }))