Files
Aislo/B05_wf2_Route/B05_wf2_Route_Api_Fetch.ts
T
2026-07-19 00:19:59 +09:00

160 lines
4.7 KiB
TypeScript

/* =============================================================================
* B05_wf2_Route_Api_Fetch.ts
* 2차 워크플로우(경로 설계) API 클라이언트
*
* 백엔드 계약 (B05_wf2_Route_Router.py):
* POST /api/projects/{project_id}/route/solve → 경로 탐색 + DB 기록
* POST /api/projects/{project_id}/route/confirm → 최신 경로 확정
*
* 규칙:
* - 모든 제어 상수는 config_frontend에서 참조 (하드코딩 금지).
* - 오류 응답 형식 {status:"error", message:"..."}을 Error로 변환.
* ========================================================================== */
import { API_BASE_URL, API_TIMEOUT_MS } from "@config/config_frontend";
/** 경로 제어점 (BP/EP/CP) */
export interface RoutePoint {
x: number;
y: number;
z?: number;
order?: number;
}
export interface CirclePoint extends RoutePoint {
radius_m: number;
}
/** 경로 탐색 실행 요청 (RouteSolveRequest) */
export interface RouteSolveRequest {
filter_key: string;
method?: string;
smooth?: boolean;
surface_model_id?: number | null;
algorithm?: string;
bp: RoutePoint;
ep: RoutePoint;
cp?: RoutePoint[];
ap?: CirclePoint[];
fp?: CirclePoint[];
grade_class?: string;
paved?: boolean;
min_curve_radius_m?: number | null;
max_uphill_grade?: number | null;
max_downhill_grade?: number | null;
min_uphill_grade?: number | null;
min_downhill_grade?: number | null;
allow_avoid_pass_through?: boolean;
station_interval_m?: number | null;
cross_half_width_m?: number | null;
cross_sample_interval_m?: number | null;
long_sample_interval_m?: number | null;
}
/** 경로 탐색 실행 결과 (RouteSolveResponse) */
export interface RouteSolveResponse {
status: string;
project_id: string;
route_id: number;
total_length_m: number;
metrics: Record<string, unknown>;
required_points_ok: boolean;
route_data_path: string;
longitudinal_length_m: number | null;
cross_section_count: number | null;
}
/** 경로 확정 결과 (RouteConfirmResponse) */
export interface RouteConfirmResponse {
status: string;
project_id: string;
route_id: number;
confirmed: boolean;
}
export interface RouteLatestResponse {
status: string;
project_id: string;
route: {
id: number;
status: string;
surface_model_id: number | null;
total_length_m: number | null;
min_slope: number | null;
max_slope: number | null;
mean_slope: number | null;
cost_score: number | null;
algorithm_params?: Record<string, unknown> | null;
} | null;
route_points: Array<RoutePoint & { chainage_m?: number; slope_percent?: number }>;
surface_params: {
source_filter: string;
method: string;
smooth: boolean;
contour_interval_m: number;
};
route_params: {
points?: {
bp?: RoutePoint | null;
ep?: RoutePoint | null;
cp?: RoutePoint[];
ap?: CirclePoint[];
fp?: CirclePoint[];
};
options?: Record<string, unknown>;
algorithm?: string;
station_interval_m?: number | null;
cross_half_width_m?: number | null;
cross_sample_interval_m?: number | null;
long_sample_interval_m?: number | null;
} | null;
}
/** 공통 fetch 헬퍼: 타임아웃 + 인증 헤더 + 오류 응답 변환. */
async function requestJson<T>(path: string, init: RequestInit): Promise<T> {
const controller = new AbortController();
const timeoutId = window.setTimeout(() => controller.abort(), API_TIMEOUT_MS);
try {
const response = await fetch(`${API_BASE_URL}${path}`, {
...init,
credentials: "include",
headers: {
"Content-Type": "application/json",
...(init.headers ?? {}),
},
signal: controller.signal,
});
const payload = (await response.json()) as T & { message?: string };
if (!response.ok) {
throw new Error(payload.message ?? `HTTP ${response.status}`);
}
return payload;
} finally {
window.clearTimeout(timeoutId);
}
}
/** 경로 탐색을 실행한다 (비용면 생성 → Dijkstra/ridge-valley → GeoJSON 저장). */
export async function solveRoute(
projectId: string,
request: RouteSolveRequest,
): Promise<RouteSolveResponse> {
return requestJson<RouteSolveResponse>(`/projects/${projectId}/route/solve`, {
method: "POST",
body: JSON.stringify(request),
});
}
/** 프로젝트의 최신 경로를 확정한다. */
export async function confirmRoute(projectId: string): Promise<RouteConfirmResponse> {
return requestJson<RouteConfirmResponse>(`/projects/${projectId}/route/confirm`, {
method: "POST",
});
}
export async function fetchLatestRoute(projectId: string): Promise<RouteLatestResponse> {
return requestJson<RouteLatestResponse>(`/projects/${projectId}/route/latest`, {
method: "GET",
});
}