/* ============================================================================= * D03_PlanAnalysis_UI_Cross.ts * D03 하 패널 — 누른 측점의 지반선 + 예상 횡단(PLAN 530-3 · 544-2). 계산 = 화면 TS 즉시(① 짝 `sectionsGround` ↔ * `generate_sections` · 격자 = 그 연도 DEM 평활본) · 예상 = `logic/D03_PlanAnalysis_Expect`(표준 횡단 기본값 · * 계획고 = 중심 지반고 · 표준값 = `sections/context` 세션 캐시) · 그림 = B05 `buildCrossSvg`. * ========================================================================== */ import "../B05_Profile/B05_Profile_UI_Style_RouteEdit.css"; import { CURRENT_PROJECT_ID_KEY } from "@config/config_frontend"; import { createWorkflowPanelHandle } from "@ui/ui_template_overlay"; import { stationLabel } from "../common_util/common_util_station"; import type { DtmGrid } from "../common_util/common_util_surface_sampler"; import { buildCrossSvg } from "../B05_Profile/B05_Profile_UI_RouteEdit_Cross_Draw"; import { sectionsGround, type StationGround, } from "../B05_Profile/logic/B05_Profile_Sections_Ground"; import { fetchSectionContext } from "../B06_Section/B06_Section_Api_Context"; import type { Vertex } from "./logic/D03_PlanAnalysis_Plan"; import { expectCross, type ExpectStandard } from "./logic/D03_PlanAnalysis_Expect"; import { PLAN_STATION_INTERVAL_M } from "./D03_PlanAnalysis_UI_Map"; export interface PlanCross { root: HTMLElement; /** `chainage` = 등록 누가 · `localM` = 그 연도 노선 위 누가 · 돌려줌 = 그린 측점 지반(못 읽으면 null). */ show: ( chainage: number, year: number, line: Vertex[], localM: number, grid: DtmGrid, ) => StationGround | null; clear: (text: string) => void; } /** 표준 횡단 기본값(config · 서버 한 번 · 세션 캐시) — 못 받으면 null(지반선만). */ export function loadExpectStandard(projectId: string): Promise { return fetchSectionContext(projectId).then( (context) => ({ standard: context.standard_cross_section as ExpectStandard["standard"], rockBoundaryM: context.rock_boundary_default_offset_m, }), () => null, ); } export function createPlanCross(): PlanCross { const root = document.createElement("section"); root.className = "d03-plan__cross"; const handle = createWorkflowPanelHandle("bottom", "down", "횡단"); const head = document.createElement("div"); head.className = "d03-plan__cross-head"; const body = document.createElement("div"); body.className = "d03-plan__cross-body"; root.append(handle.root, head, body); let open = true; handle.setOpen(open); handle.root.addEventListener("click", () => { open = !open; root.classList.toggle("is-collapsed", !open); handle.setOpen(open); }); const clear = (text: string): void => { head.textContent = ""; body.replaceChildren(Object.assign(document.createElement("p"), { textContent: text })); delete root.dataset.chainage; }; clear("평면에서 측점을 누르면 그 자리 지반선 · 예상 횡단이 보입니다."); let standard: ExpectStandard | null = null; let asked: Promise | null = null; return { root, clear, show(chainage, year, line, localM, grid) { const station = sectionsGround(line, [localM], grid).stations[0]; if (!station) { clear("이 측점의 지반을 읽지 못했습니다."); return null; } const name = stationLabel(chainage, PLAN_STATION_INTERVAL_M, 1); const z = station.center_z === null ? "—" : `${station.center_z.toFixed(2)} m`; root.dataset.chainage = String(chainage); const draw = (): void => { const expect = standard ? expectCross(station, standard, grid) : null; const area = expect ? ` · 예상(표준 횡단 기본값 · 계획고 = 지반고) 절토 ${expect.design.cut_area_m2.toFixed(2)}㎡ · 성토 ${expect.design.fill_area_m2.toFixed(2)}㎡` : ""; head.textContent = `${year}년 · ${name} · 중심 지반고 ${z}${area}`; const rect = body.getBoundingClientRect(); const plot = buildCrossSvg( { status: "success", chainage_m: chainage, label: name, uphill_side: station.uphill_side, plan_radius_m: null, curve_widening_m: null, cross_half_width_m: 20, samples: expect?.samples ?? station.samples, design: expect ? { ...expect.design } : null, }, Math.max(rect.width, 320), Math.max(rect.height, 160), !expect, ); body.replaceChildren(plot.svg); }; draw(); if (!standard) { const projectId = localStorage.getItem(CURRENT_PROJECT_ID_KEY) ?? ""; asked ??= loadExpectStandard(projectId); void asked.then((value) => { standard = value; // 그사이 다른 측점을 눌렀으면 그 측점이 다시 그림 if (value && root.dataset.chainage === String(chainage)) draw(); }); } return station; }, }; }