Files
Aislo/D03_PlanAnalysis/D03_PlanAnalysis_Api.ts
T
eomsangdonandClaude Opus 5.5 50e9c1b015 feat(530-3): D03 예정노선 분석 화면 — 평면 · 지점 끌기 · 더 받기 · 갈래 카드 · 측점 · 지반 횡단 · 3D
- 평면 = 위성 + 도엽 등고선 + 다른 갈래(옅게) + 고른 노선 + 측점(누가 = 등록 시작부터) + 지점 끌기 겹
- 끈 지점 = 캐시 · 받기 · 분석 · 보기는 points 로 실어 보냄 · [저장] 때만 D02 규칙으로 정본
- 연도별 갈래 넷 카드(연장 · 최대 기울기 · 위반 수) · 고름 = 캐시 · [저장] = 서버 저장본으로 다시 셈
- 측점 누름 = 하 패널 지반선 횡단(sectionsGround · buildCrossSvg 지반만 인자 · 기본 그대로)
- 3D 모달 = 연도 DEM + 노선 + 측점 막대(코리도 · 구조물 안 부름)
- 화면 계산 logic 짝(pickMode · chainRows · joinedLine ↔ 파이썬) 거울 시험

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 20:11:23 +09:00

113 lines
4.0 KiB
TypeScript

/* =============================================================================
* D03_PlanAnalysis_Api.ts
* D03 분석 화면 서버 요청(PLAN 530-3) — 지점 정본(D02) · 지형 받기 · 분석 · 진행 · 저장본 · [저장] ·
* 연도별 지형면(모델 행 · 격자). 끈 지점은 `points`(작업 좌표계 m)로 실어 보냄.
* ========================================================================== */
import { API_BASE_URL } from "@config/config_frontend";
import { fetchCachedBytes } from "../A00_Common/b_asset_cache";
import { listSurfaceModels } from "../B04_PreProcess/B04_PreProcess_Api_Fetch";
import { type DtmGrid, parseDtmGrid } from "../common_util/common_util_surface_sampler";
import type { ChainRow, Vertex, YearAnalysis } from "./logic/D03_PlanAnalysis_Plan";
export interface PlanPointsView {
year_start: number;
year_end: number;
epsg: number | null;
years: Array<{
year: number;
start: { x: number; y: number } | null;
end: { x: number; y: number } | null;
}>;
}
export interface PlanJob {
running: boolean;
percent: number;
message: string | null;
error: string | null;
}
export interface PlanAnalysisView {
years: Array<{ year: number; start: Vertex; end: Vertex; analysis: YearAnalysis | null }>;
chain: ChainRow[] | null;
/** 등록 노선 시작 누가(m). */
start_chainage_m: number;
}
export type PlanPoints = Array<{ year: number; start: Vertex; end: Vertex }>;
async function request<T>(path: string, init: RequestInit = {}): Promise<T> {
const response = await fetch(`${API_BASE_URL}${path}`, {
...init,
credentials: "include",
headers: { "Content-Type": "application/json", ...(init.headers ?? {}) },
});
const body = (await response.json().catch(() => ({}))) as T & { detail?: unknown };
if (!response.ok) {
const detail = typeof body.detail === "string" ? body.detail : `HTTP ${response.status}`;
throw new Error(detail);
}
return body;
}
const base = (projectId: string): string => `/projects/${projectId}/plan`;
const pointsBody = (points: PlanPoints | null): string => JSON.stringify(points ? { points } : {});
export const fetchPlanPoints = (projectId: string): Promise<PlanPointsView> =>
request(`${base(projectId)}/points`);
export const startPlanJob = (
projectId: string,
kind: "terrain" | "analyze",
points: PlanPoints | null,
): Promise<PlanJob> =>
request(`${base(projectId)}/${kind}`, { method: "POST", body: pointsBody(points) });
export const fetchPlanProgress = (
projectId: string,
): Promise<{ terrain: PlanJob; analyze: PlanJob }> => request(`${base(projectId)}/progress`);
export const fetchPlanAnalysis = (
projectId: string,
points: PlanPoints | null,
): Promise<PlanAnalysisView> =>
request(
`${base(projectId)}/analysis` +
(points ? `?points=${encodeURIComponent(JSON.stringify(points))}` : ""),
);
export const savePlanAnalysis = (
projectId: string,
choices: Record<number, string>,
points: PlanPoints | null,
): Promise<{ chain: ChainRow[] }> =>
request(`${base(projectId)}/analysis`, {
method: "PUT",
body: JSON.stringify(points ? { choices, points } : { choices }),
});
export interface YearSurface {
modelId: number;
grid: DtmGrid;
}
/** 연도별 지형면 — 모델 행 경로 `…/models_<연도>/…` · 격자 = 분석과 같은 평활본. */
export async function fetchYearSurfaces(projectId: string): Promise<Map<number, YearSurface>> {
const { models } = await listSurfaceModels(projectId);
const out = new Map<number, YearSurface>();
for (const model of models) {
const year = Number(/models_(\d+)\//.exec(model.model_file_path ?? "")?.[1]);
if (!year || out.has(year)) continue;
const stamp = String(model.generation_params?.file_stamp ?? "");
const url =
`${API_BASE_URL}/projects/${projectId}/surface/models/${model.id}/grid` +
`?smooth=true&sig=${encodeURIComponent(stamp)}`;
out.set(year, {
modelId: model.id,
grid: parseDtmGrid(await fetchCachedBytes(projectId, url)),
});
}
return out;
}