Files
Aislo/B05_Profile/B05_Profile_UI_Profile_Data.ts
T
eomsangdonandClaude Fable 5 54954a05e5 refactor(B05,B06): B05_wf2_Route -> B05_Profile, B06_wf3_ProfileCross -> B06_Section 동시 개명
- 한몸으로 동작하는 두 페이지라 한 커밋으로 처리 (상호 참조 다수)
- B05 37파일 + B06 20파일 접두사 개명 (git mv, 이력 보존)
- 참조 치환 91파일: import 경로, 라우트 슬러그(b05-profile/b06-section),
  라우트 키(B05_PROFILE/B06_SECTION), B03 자동 체인, storage 상수, pyproject 제외 경로
- 로직 변경 없음. typecheck·백엔드 import 검증 통과

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 10:03:11 +09:00

79 lines
2.9 KiB
TypeScript

/* =============================================================================
* 종단면 패널 자료 어댑터 (B05)
*
* 저장된 종단면 자료(`longitudinal_sections.data`)를 화면이 쓰는 모양으로 바꾸는 순수 함수들.
* 상태를 갖지 않으며 DOM도 만들지 않는다 — 패널 본체가 700줄 한계에 닿아 분리했다.
* ========================================================================== */
import type {
DesignProfile,
LongitudinalSection,
} from "../B06_Section/B06_Section_Api_Fetch";
import type { ProfileAlignment } from "./B05_Profile_UI_Profile_Alignment";
export function readAlignment(data: LongitudinalSection): ProfileAlignment | null {
const candidate = data.profile_alignment as ProfileAlignment | undefined;
if (!candidate?.base_pvi?.length || !candidate.samples?.length) return null;
if (!Number.isFinite(candidate.policy?.default_curve_radius_m)) return null;
if (!candidate.stations || !candidate.segments || !candidate.curves) return null;
candidate.edits = {
station_offsets: candidate.edits?.station_offsets ?? {},
curve_radii: candidate.edits?.curve_radii ?? {},
};
return candidate;
}
/** 저장분이 구버전이라 편집을 붙일 수 없는 상태인가 (재계산 안내용). */
export function hasLegacyAlignment(data: LongitudinalSection): boolean {
return Boolean(data.profile_alignment) && readAlignment(data) === null;
}
/** 편집 결과를 종단면도 렌더러가 받는 계획선 형태로 감싼다. */
export function toDesignProfile(
alignment: ProfileAlignment,
original: DesignProfile | undefined,
): DesignProfile {
const balance = alignment.balance;
return {
id: original?.id ?? "design_grade_line",
name: original?.name ?? "계획선",
basis: original?.basis ?? "station_alignment",
samples: alignment.samples,
balance_segments: [
{
index: 0,
start_chainage_m: alignment.samples[0]?.chainage_m ?? 0,
end_chainage_m: alignment.samples[alignment.samples.length - 1]?.chainage_m ?? 0,
cut_area_m2: balance.cut_area_m2,
fill_area_m2: balance.fill_area_m2,
balance_error_m2: balance.net_area_m2,
},
],
summary: {
...(original?.summary ?? {
max_grade_pct: 0,
vertical_curve_count: 0,
pvi_count: 0,
balance_segment_count: 1,
main_direction: "none",
suggested_elevation_offset_m: null,
warnings: [],
}),
cut_area_m2: balance.cut_area_m2,
fill_area_m2: balance.fill_area_m2,
balance_error_m2: balance.net_area_m2,
balanced: balance.within_tolerance,
},
};
}
export function normalizedLongitudinal(data: LongitudinalSection): LongitudinalSection {
return {
...data,
samples: data.samples.map((sample: LongitudinalSection["samples"][number]) => ({
...sample,
elevation_m: sample.elevation_m ?? sample.z ?? null,
})),
};
}