Files
Aislo/B05_Profile/B05_Profile_Api_RouteInitial.ts
T
eomsangdonandClaude Opus 5.5 9f717a6a72 feat(B05): 계획노선 편집 분석 지형 고르기 — 라이다 있는 프로젝트도 수치지형도로 분석(133-1)
- 편집 모달 「분석 지형: 라이다 · 수치지형도」 고르기 · 라이다와 수치지형도 서피스가 둘 다 있을 때만 보임 · 기본 라이다 · 값은 화면 상태 pref(routeedit-terrain)
- 고르면 등고선 · 구간 기울기 · 거리 재기 지반고 · 횡단 미리보기 · 종단 띠를 고른 지형으로 다시 그림 · 노선 추천도 고른 지형으로 요청
- 서버 통로(elevations · cross-preview · profile-preview · initial)가 terrain(las · sheet) 을 받아 sampler 를 고름 · GET route/terrain-sources 새 길
- 확정 params · [확인] 재계산 · 종 · 횡단 · 3D 정본은 안 바뀜
- 지표면 허용 목록에 sheet_* 여섯 넣음 — 54-22 뒤 LAS 없는 프로젝트 sheet_laplace 가 막혀 있던 길도 풀림

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UoSr2sqvYtBf1dtQdus88s
2026-10-09 09:27:57 +09:00

73 lines
3.0 KiB
TypeScript

/* =============================================================================
* B05_Profile_Api_RouteInitial.ts
* 초기노선 갈래 요청(계획서 20) — 로직 하나를 골라 서버가 새 계획노선 노드를 낸다.
*
* POST /projects/{id}/route/initial { mode, weights?, criteria? } → 노드 · 비교값 · 공통 제약 기준값
*
* 계산 자리는 **서버 단독**(CLAUDE.md 5장 ③) — 화면은 부르고, 받은 노드를 편집 캐시에 싣고,
* 받은 비교값·기준값을 보이기만 한다. 모양은 계획서 20 「API」 줄(서버 `initial_route`) —
* 답을 펴는 자리는 `normalizeRouteInitial`(`_RouteEdit_Initial_Model`) 한 곳이다.
* ========================================================================== */
import { analysisTerrain, requestJson } from "./B05_Profile_Api_Replan";
/** 갈래 셈 대기 상한 — 노선 여러 벌을 셈해 견주므로 넉넉히 잡는다. */
const INITIAL_TIMEOUT_MS = 5 * 60 * 1000;
/** 로직 이름 — 서버와 같은 낱말을 쓴다. `follow` 는 예상노선 추적(처음 초기값). */
export type RouteInitialLogic =
"follow" | "gradeline" | "earthwork" | "gentle" | "safe" | "crossings" | "cost" | "weighted";
/** 비중 계산의 세 비중(%) — 합 100. */
export interface RouteInitialWeights {
expected: number;
earthwork: number;
grade: number;
}
/** 공통 제약 겹침 값 — 화면 네 칸(계획서 20 확정). 비우면 서버가 프로젝트 기본값을 쓴다.
* 이름은 서버 몸통과 그대로(snake_case) — 변환 없이 그대로 실어 보낸다. */
export interface RouteInitialCriteria {
max_grade_pct?: number;
min_radius_m?: number;
stream_offset_m?: number;
grade_smooth_m?: number;
/** 영선 노선(`gradeline`)의 목표 종단기울기(%) — 비우면 종단기울기 상한. */
target_grade_pct?: number;
}
export interface RouteInitialRequest {
mode: RouteInitialLogic;
weights?: RouteInitialWeights;
criteria?: RouteInitialCriteria;
/** 추천 점수에 균형 종단(63-4)을 넣을지 — 끄면 원지반만. 안 보내면 켬(서버 기본). */
with_profile?: boolean;
}
/** 서버 길이 아직 없다(404·405) — 화면은 이 글로 알린다. */
export const ROUTE_INITIAL_MISSING =
"초기노선 갈래 서버 길이 아직 없습니다(계획서 20 · sub3 대기).";
/** 로직 하나로 초기노선을 묻는다. 답의 모양은 `normalizeRouteInitial` 이 편다. */
export async function fetchRouteInitial(
projectId: string,
request: RouteInitialRequest,
): Promise<unknown> {
try {
return await requestJson<unknown>(
`/projects/${projectId}/route/initial`,
{
method: "POST",
body: JSON.stringify({ ...request, terrain: analysisTerrain(projectId) }),
},
INITIAL_TIMEOUT_MS,
);
} catch (error) {
const message = error instanceof Error ? error.message : "";
if (/HTTP 40[45]|Not Found|Method Not Allowed/.test(message)) {
throw new Error(ROUTE_INITIAL_MISSING);
}
throw error;
}
}