- 지도 스타일 상수를 mapStyles.ts로 추출 - useBaseMapStyle 훅 생성 (mapToggles 기반 스타일 반환) - 9개 탭 컴포넌트의 하드코딩 스타일을 공유 훅으로 교체 - 각 Map에 S57EncOverlay 추가 - 초기 mapToggles를 모두 false로 변경 (기본지도 표시)
130 lines
3.9 KiB
TypeScript
130 lines
3.9 KiB
TypeScript
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<void>;
|
|
// 측정
|
|
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<MapState>((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<MapTypeItem[]>('/map-base/active')
|
|
const types = res.data
|
|
const current = get().mapToggles
|
|
const newToggles: Partial<MapToggles> = {}
|
|
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 }),
|
|
}))
|