데이터 흐름 문제 - B04 해석 산출물 7개(7.2MB)가 B05로 전량 복사되고 있었고, 관 지점 정본 (pipe_points.json)은 B05가 아예 참조하지 않았다. B04에서 확정한 관이 B05에 보이지 않고 진입할 때마다 자동 배치로 되돌아갔다. - 사본은 "B05 편집이 원본을 덮어쓴다"를 막으려던 것인데, 외곽선 편집을 제거한 뒤로 B05는 아무것도 저장하지 않아 존재 이유가 사라졌다. 일원화 - B05 전용 모듈 3개 삭제: Engine_Drainage_Store / Engine_Drainage_Basin / Router_Drainage. 두 화면이 B04_wf1_Surface/drainage/ 한 폴더를 직접 읽는다. - POST /drainage/basins 폐기 → GET/PUT /drainage/pipe-points · POST /drainage/detail-basins 한 벌로 통합. 응답에 route_lonlat · main_polygon_lonlat · flow_arrows · upstream_lonlat · inflow_hotspots를 추가해 B05가 그리는 데 필요한 값을 같은 응답으로 받는다. - 경로 확정 시 관 지점을 B04와 같은 저장소에 커밋한다. B05 화면 - 유역 폴리곤을 눌러 고르기(지도에서도 선택), [집중유역] 토글, 관 개수·유역 수· 종단 Z 출처 요약 줄 추가. - 관 마커는 좌클릭으로만 잡는다(우클릭은 메뉴, 가운데 버튼은 팬). - 유역 목록은 3행까지 보이고 나머지는 스크롤. 종단 테이블 구조물 라인 - 관 매설 지점을 "배관" 구조물로 실체화(origin: "pipe"). 정본은 관 지점 파일이며 목록은 그 투영이라, 어디서 옮기든 pipeEditor 한 경로로 전파된다. - 테이블 위 세로선을 끌어 이동(배관은 세부유역까지 재산정), 우클릭으로 배관 추가/구조물 삭제. 정리 - B05_wf2_Route/drainage/ 사본과 debug/(삭제된 Subdivide 엔진 검증 스크립트) 삭제. - config DRAINAGE_B05_DIRNAME 제거. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
79 lines
2.9 KiB
TypeScript
79 lines
2.9 KiB
TypeScript
/* =============================================================================
|
|
* 종단면 패널 자료 어댑터 (B05)
|
|
*
|
|
* 저장된 종단면 자료(`longitudinal_sections.data`)를 화면이 쓰는 모양으로 바꾸는 순수 함수들.
|
|
* 상태를 갖지 않으며 DOM도 만들지 않는다 — 패널 본체가 700줄 한계에 닿아 분리했다.
|
|
* ========================================================================== */
|
|
|
|
import type {
|
|
DesignProfile,
|
|
LongitudinalSection,
|
|
} from "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_Api_Fetch";
|
|
import type { ProfileAlignment } from "./B05_wf2_Route_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,
|
|
})),
|
|
};
|
|
}
|