245 lines
6.1 KiB
TypeScript
245 lines
6.1 KiB
TypeScript
import { api } from '@common/services/api';
|
|
|
|
export interface PredictionAnalysis {
|
|
acdntSn: number;
|
|
acdntNm: string;
|
|
occurredAt: string;
|
|
analysisDate: string;
|
|
requestor: string;
|
|
duration: string;
|
|
oilType: string;
|
|
volume: number | null;
|
|
location: string;
|
|
lat: number | null;
|
|
lon: number | null;
|
|
kospsStatus: string;
|
|
poseidonStatus: string;
|
|
opendriftStatus: string;
|
|
backtrackStatus: string;
|
|
analyst: string;
|
|
officeName: string;
|
|
acdntSttsCd: string;
|
|
}
|
|
|
|
export interface PredictionDetail {
|
|
acdnt: {
|
|
acdntSn: number;
|
|
acdntNm: string;
|
|
occurredAt: string;
|
|
lat: number | null;
|
|
lon: number | null;
|
|
location: string;
|
|
analyst: string;
|
|
officeName: string;
|
|
};
|
|
spill: {
|
|
oilType: string;
|
|
volume: number | null;
|
|
unit: string;
|
|
fcstHr: number | null;
|
|
} | null;
|
|
vessels: Array<{
|
|
vesselInfoSn: number;
|
|
imoNo: string;
|
|
vesselNm: string;
|
|
vesselTp: string;
|
|
loaM: number | null;
|
|
breadthM: number | null;
|
|
draftM: number | null;
|
|
gt: number | null;
|
|
dwt: number | null;
|
|
builtYr: number | null;
|
|
flagCd: string;
|
|
callsign: string;
|
|
engineDc: string;
|
|
insuranceData: unknown;
|
|
}>;
|
|
weather: Array<{
|
|
obsDtm: string;
|
|
locNm: string;
|
|
temp: string;
|
|
weatherDc: string;
|
|
wind: string;
|
|
wave: string;
|
|
humid: string;
|
|
vis: string;
|
|
sst: string;
|
|
}>;
|
|
}
|
|
|
|
export interface BacktrackResult {
|
|
backtrackSn: number;
|
|
acdntSn: number;
|
|
estSpilDtm: string | null;
|
|
anlysRange: string | null;
|
|
lon: number | null;
|
|
lat: number | null;
|
|
srchRadiusNm: number | null;
|
|
totalVessels: number | null;
|
|
execSttsCd: string;
|
|
rsltData: Record<string, unknown> | null;
|
|
}
|
|
|
|
export const fetchPredictionAnalyses = async (params?: {
|
|
search?: string;
|
|
}): Promise<PredictionAnalysis[]> => {
|
|
const response = await api.get<PredictionAnalysis[]>('/prediction/analyses', { params });
|
|
return response.data;
|
|
};
|
|
|
|
export const fetchPredictionDetail = async (acdntSn: number): Promise<PredictionDetail> => {
|
|
const response = await api.get<PredictionDetail>(`/prediction/analyses/${acdntSn}`);
|
|
return response.data;
|
|
};
|
|
|
|
export const fetchBacktrack = async (sn: number): Promise<BacktrackResult> => {
|
|
const response = await api.get<BacktrackResult>(`/prediction/backtrack/${sn}`);
|
|
return response.data;
|
|
};
|
|
|
|
export const fetchBacktrackByAcdnt = async (
|
|
acdntSn: number,
|
|
): Promise<BacktrackResult | null> => {
|
|
const response = await api.get<BacktrackResult[]>('/prediction/backtrack', {
|
|
params: { acdntSn },
|
|
});
|
|
return response.data.length > 0 ? response.data[0] : null;
|
|
};
|
|
|
|
export const createBacktrack = async (input: {
|
|
acdntSn: number;
|
|
lon: number;
|
|
lat: number;
|
|
srchRadiusNm?: number;
|
|
anlysRange?: string;
|
|
}): Promise<{ backtrackSn: number }> => {
|
|
const response = await api.post<{ backtrackSn: number }>('/prediction/backtrack', input);
|
|
return response.data;
|
|
};
|
|
|
|
// ============================================================
|
|
// 확산 예측 시뮬레이션 (OpenDrift 연동)
|
|
// ============================================================
|
|
|
|
export interface SimulationRunResponse {
|
|
success: boolean;
|
|
execSn: number; // 하위 호환 유지 (첫 번째 모델의 execSn)
|
|
execSns: Array<{ model: string; execSn: number }>;
|
|
acdntSn: number | null;
|
|
status: 'RUNNING';
|
|
}
|
|
|
|
export interface WindPoint {
|
|
lat: number;
|
|
lon: number;
|
|
wind_speed: number;
|
|
wind_direction: number;
|
|
}
|
|
|
|
export interface HydrGrid {
|
|
lonInterval: number[];
|
|
boundLonLat: { top: number; bottom: number; left: number; right: number };
|
|
rows: number;
|
|
cols: number;
|
|
latInterval: number[];
|
|
}
|
|
|
|
export interface HydrDataStep {
|
|
value: [number[][], number[][]]; // [u_2d, v_2d]
|
|
grid: HydrGrid;
|
|
}
|
|
|
|
export interface CenterPoint {
|
|
lat: number;
|
|
lon: number;
|
|
time: number;
|
|
model?: string;
|
|
}
|
|
|
|
export interface OilParticle {
|
|
lat: number;
|
|
lon: number;
|
|
time: number;
|
|
particle?: number;
|
|
stranded?: 0 | 1;
|
|
model?: string;
|
|
}
|
|
|
|
export interface SimulationSummary {
|
|
remainingVolume: number;
|
|
weatheredVolume: number;
|
|
pollutionArea: number;
|
|
beachedVolume: number;
|
|
pollutionCoastLength: number;
|
|
}
|
|
|
|
export interface SimulationStatusResponse {
|
|
status: 'PENDING' | 'RUNNING' | 'DONE' | 'ERROR';
|
|
progress?: number;
|
|
trajectory?: OilParticle[];
|
|
summary?: SimulationSummary;
|
|
centerPoints?: CenterPoint[];
|
|
windData?: WindPoint[][];
|
|
hydrData?: (HydrDataStep | null)[];
|
|
error?: string;
|
|
}
|
|
|
|
export interface RunModelSyncResult {
|
|
model: string;
|
|
execSn: number;
|
|
status: 'DONE' | 'ERROR';
|
|
trajectory?: OilParticle[];
|
|
summary?: SimulationSummary;
|
|
centerPoints?: CenterPoint[];
|
|
windData?: WindPoint[][];
|
|
hydrData?: (HydrDataStep | null)[];
|
|
error?: string;
|
|
}
|
|
|
|
export interface RunModelSyncResponse {
|
|
success: boolean;
|
|
acdntSn: number | null;
|
|
execSns: Array<{ model: string; execSn: number }>;
|
|
results: RunModelSyncResult[];
|
|
}
|
|
|
|
export interface TrajectoryResponse {
|
|
trajectory: OilParticle[] | null;
|
|
summary: SimulationSummary | null;
|
|
centerPoints?: CenterPoint[];
|
|
windDataByModel?: Record<string, WindPoint[][]>;
|
|
hydrDataByModel?: Record<string, (HydrDataStep | null)[]>;
|
|
summaryByModel?: Record<string, SimulationSummary>;
|
|
stepSummariesByModel?: Record<string, SimulationSummary[]>;
|
|
}
|
|
|
|
export const fetchAnalysisTrajectory = async (acdntSn: number): Promise<TrajectoryResponse> => {
|
|
const response = await api.get<TrajectoryResponse>(`/prediction/analyses/${acdntSn}/trajectory`);
|
|
return response.data;
|
|
};
|
|
|
|
// ============================================================
|
|
// 이미지 업로드 분석
|
|
// ============================================================
|
|
|
|
export interface ImageAnalyzeResult {
|
|
acdntSn: number;
|
|
lat: number;
|
|
lon: number;
|
|
oilType: string;
|
|
area: number;
|
|
volume: number;
|
|
fileId: string;
|
|
occurredAt: string;
|
|
}
|
|
|
|
export const analyzeImage = async (file: File): Promise<ImageAnalyzeResult> => {
|
|
const formData = new FormData();
|
|
formData.append('image', file);
|
|
const response = await api.post<ImageAnalyzeResult>('/prediction/image-analyze', formData, {
|
|
headers: { 'Content-Type': 'multipart/form-data' },
|
|
timeout: 330_000,
|
|
});
|
|
return response.data;
|
|
};
|