좌측 「계획노선」 섹션의 [계획노선 편집] 로 큰 모달을 엶(PLAN 0-2). - 등고선 도엽 위에 예상노선(점선)·계획노선(실선)을 함께 그림. 지도 그리기는 배수유역도와 같은 도구(`B04_PreProcess_UI_MapRender`) 재사용. - 노드 끌어 옮기기 · 선 두 번 클릭으로 노드 끼우기 · 오른쪽 클릭으로 지우기, 배경 끌기로 화면 이동, 휠로 확대. - 편집 중에는 계산이 나가지 않음. [확인]에서만 서버가 배수유역부터 재계산하며 그동안 화면을 덮는 안내를 띄움. 끝나면 세션 초안·조회 캐시를 비우고 페이지를 다시 세움. - [예상노선으로] 는 수정본을 지우고 같은 재계산(노선 초기화). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
87 lines
3.3 KiB
TypeScript
87 lines
3.3 KiB
TypeScript
/* =============================================================================
|
|
* B05_Profile_Api_Replan.ts
|
|
* 계획노선 두 벌(예상노선·계획노선) 읽기와 노선 갈아 끼우기 요청.
|
|
*
|
|
* GET /projects/{id}/route/plan → 예상노선·계획노선 정점(사업지 좌표계 m)
|
|
* POST /projects/{id}/route/replan → 고친 계획노선으로 갈아 끼우고 재계산
|
|
* POST /projects/{id}/route/replan/reset → 계획노선을 예상노선으로 되돌리고 재계산
|
|
*
|
|
* 재계산은 배수유역부터 전 단계를 다시 도는 무거운 작업이라(용화 67측점 기준 3분대)
|
|
* 타임아웃을 길게 잡는다 — 기본값으로 두면 중간에 끊긴다.
|
|
* ========================================================================== */
|
|
|
|
import { API_BASE_URL } from "@config/config_frontend";
|
|
|
|
/** 노선 재계산 대기 상한 — 배수유역 분석(90초대)까지 포함해 넉넉히 잡는다. */
|
|
const REPLAN_TIMEOUT_MS = 15 * 60 * 1000;
|
|
|
|
export interface RoutePlanResponse {
|
|
status: string;
|
|
project_id: string;
|
|
/** 예상노선(원본) 정점 [[x, y], …] — 사업지 좌표계(m). */
|
|
expected: Array<[number, number]>;
|
|
/** 계획노선(수정본). 고친 적이 없으면 예상노선과 같은 값. */
|
|
planned: Array<[number, number]>;
|
|
/** 사용자가 고친 계획노선이 저장돼 있으면 true. */
|
|
edited: boolean;
|
|
}
|
|
|
|
export interface RouteReplanResponse {
|
|
status: string;
|
|
project_id: string;
|
|
route_id: number | null;
|
|
total_length_m: number | null;
|
|
vertex_count: number;
|
|
}
|
|
|
|
async function requestJson<T>(path: string, init: RequestInit, timeoutMs: number): Promise<T> {
|
|
const controller = new AbortController();
|
|
const timer = window.setTimeout(() => controller.abort(), timeoutMs);
|
|
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(timer);
|
|
}
|
|
}
|
|
|
|
/** 예상노선·계획노선을 함께 읽는다(편집 모달이 점선·실선으로 그린다). */
|
|
export async function fetchRoutePlan(projectId: string): Promise<RoutePlanResponse> {
|
|
return requestJson<RoutePlanResponse>(
|
|
`/projects/${projectId}/route/plan`,
|
|
{ method: "GET" },
|
|
60000,
|
|
);
|
|
}
|
|
|
|
/** 고친 계획노선으로 갈아 끼우고 배수유역부터 다시 계산한다. */
|
|
export async function replanRoute(
|
|
projectId: string,
|
|
vertices: Array<[number, number]>,
|
|
): Promise<RouteReplanResponse> {
|
|
return requestJson<RouteReplanResponse>(
|
|
`/projects/${projectId}/route/replan`,
|
|
{
|
|
method: "POST",
|
|
body: JSON.stringify({ vertices: vertices.map(([x, y]) => ({ x, y })) }),
|
|
},
|
|
REPLAN_TIMEOUT_MS,
|
|
);
|
|
}
|
|
|
|
/** 계획노선을 예상노선으로 되돌리고 같은 재계산을 돈다(노선 초기화). */
|
|
export async function resetRoutePlan(projectId: string): Promise<RouteReplanResponse> {
|
|
return requestJson<RouteReplanResponse>(
|
|
`/projects/${projectId}/route/replan/reset`,
|
|
{ method: "POST" },
|
|
REPLAN_TIMEOUT_MS,
|
|
);
|
|
}
|