76 lines
2.9 KiB
TypeScript
76 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,
|
|
})),
|
|
};
|
|
}
|