랩탑 메인이 측구를 갈라 냈으므로(`6a7c339f`) 그 값을 그대로 옮김. 여기서 다시 가르지 않음 — 근거가 없는 측점은 B06 이 나누지 않고 사유를 함께 냄. `split_basis()` 를 함께 둠 — `ditch_split_basis` 를 사람이 읽는 말로 바꿔 준다. ⚠ 표 칸은 숫자만 담으므로 이 말은 표 밖(주기·화면)에 실어야 함. 「전량 암」이 경계선을 못 찾아서인지 정말 암 지반이어서인지 숫자만으로는 구별 안 됨. ⚠ 층따기 밑수가 바뀐 것이 이 표에도 그대로 왔음 — 랩탑 메인이 「성토 사면길이」에서 「B06 이 낸 원지반 깎는 길이(`bench_cut_length_m`)」로 고쳤고, 이 모듈이 B08 `_length_of` 를 빌려 쓰므로 **같이 움직였음**. 값이 없는 옛 저장분에서는 0 으로 섬 — 성토 사면길이로 대신 채우지 않는 것이 맞음(다른 면을 세게 됨). 실측(936be972, 옛 저장분) — 여전히 **14칸**. `ditch_split_basis`·`bench_cut_length_m` 둘 다 저장된 설계에 없음(B06 확정 전 자료). 새 키가 있는 자료에서 16칸이 되는 것은 시험으로 확인함. 자체검증 — 시험 11건(측구 두 칸 · 못 가른 측점 사유 · 층따기 밑수 따라가기 추가), 회귀 573 통과 · 0 실패. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
121 lines
6.3 KiB
Python
121 lines
6.3 KiB
Python
"""횡단도 아래 수량 산출표 — **저장된 횡단 설계에서 칸을 채운다**.
|
|
|
|
왜 필요한가 (법정 요구)
|
|
별표2 Ⅰ.1.나.(5) 가 횡단면도에 「지반고·계획고·절토고·성토고·**단면적·지장목 제거·
|
|
측구터파기 단면적·사면보호공**」 여덟을 요구한다. 지금 나가는 것은 **앞 넷뿐**이고
|
|
뒤 넷이 빈칸으로 나갔다. 그 구멍을 메우는 자리다.
|
|
|
|
⚠ 빈칸의 까닭은 「값이 없어서」가 아니었다
|
|
표가 `source["quantities"]` 를 보는데 **그 키가 원본 파일에 아예 없다**(실측:
|
|
`cross_00960m.json` 에 `samples`·`center_z`·`frame` 뿐). 반면 **저장된 횡단 설계에는
|
|
단면적이 그대로 있고**(`cut_soil_area_m2` 등), **사면길이도 그 설계선에서 유도된다**
|
|
(`B08_Quantity_Engine_SlopeLength.station_slope`). 즉 **통로만 없었다.**
|
|
|
|
⚠ 계산을 새로 짜지 않는다 (CLAUDE.md 5장)
|
|
단면적은 B06 이 낸 저장값을 **그대로 읽고**, 사면 계열은 **B08 이 쓰는 그 함수**를 부른다.
|
|
계열 이름과 「어느 면을 쓰나」도 `B08_Quantity_Engine_SlopeArea` 의 정의를 빌려 쓴다 —
|
|
거기서 밑수가 바뀌면 이 표도 같이 움직여야 하기 때문이다.
|
|
|
|
⚠ 사면 계열 칸의 **단위**
|
|
B08 은 측점 사이를 평균단면적법으로 적분해 **면적(㎡)** 을 내지만, 그것은 두 측점이 있어야
|
|
나오는 값이라 **한 장짜리 횡단도에는 못 쓴다.** 횡단도 칸에 들어가는 것은 그 측점의
|
|
**사면길이(m)** 이고, 이는 **1m 폭 조각의 면적(㎡/m)과 수치가 같다.**
|
|
⇒ 표에 「m 로 볼 것인가 ㎡/m 로 볼 것인가」는 표기 문제이고 **값은 하나다.**
|
|
|
|
아직 안 채우는 칸 — 근거가 없다(임의로 넣지 않는다)
|
|
· 측구 토사/암석 — 저장값이 `ditch_area_m2` **한 값뿐**이라 토사·암석으로 못 가른다
|
|
· 표토제거 성토/절토 — 법은 「전량 제거」인데 **두께 칸이 없어** 물량이 안 선다
|
|
· 편책 — 별도 일위대가를 만들어 잇기로 확정(2026-09-09 ⑧-4). 밑수는 그때 붙는다
|
|
· 제근 — 입목 본수를 안 든다
|
|
· 노면다짐 — 밑수(노면 폭)는 있으나 이 표의 다른 칸과 축이 달라 뒤로 미룸
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from B08_Quantity.B08_Quantity_Engine_SlopeArea import PROTECTION_SOURCE, _key, _length_of
|
|
from B08_Quantity.B08_Quantity_Engine_SlopeLength import station_slope
|
|
|
|
#: 저장된 설계 단면적을 그대로 옮기는 칸 — `표 키 → 설계 키`.
|
|
#: ⚠ 측구 둘은 B06 이 **절토 분리와 같은 근거**(지반 유형 + 암반 경계선)로 갈라 낸 값이다
|
|
#: (2026-09-09 `6a7c339f`). 여기서 다시 가르지 않는다 — 근거가 없는 측점은 그쪽이
|
|
#: **나누지 않고** 사유(`ditch_split_basis`)를 함께 낸다. `split_basis()` 로 읽는다.
|
|
AREA_KEYS: tuple[tuple[str, str], ...] = (
|
|
("cut_soil", "cut_soil_area_m2"),
|
|
("cut_rock", "cut_rock_area_m2"),
|
|
("embankment", "fill_area_m2"),
|
|
("ditch_soil", "ditch_soil_area_m2"),
|
|
("ditch_rock", "ditch_rock_area_m2"),
|
|
)
|
|
|
|
#: `ditch_split_basis` 를 사람이 읽는 말로. 도면·화면이 「왜 이렇게 갈렸나」를 보일 때 쓴다.
|
|
#: ⚠ `rock_ground_no_boundary` 는 **못 가른 것**이다 — 절반씩 임의로 나누는 대신 전량 암으로
|
|
#: 두고 그 사실을 남긴다. 그 측점의 표에는 「암」 한 칸만 서는 것이 정상이다.
|
|
DITCH_SPLIT_LABEL: dict[str, str] = {
|
|
"no_ditch": "측구 없음",
|
|
"soil_ground": "토사 지반 — 전량 토사",
|
|
"rock_ground_no_boundary": "암 지반인데 암반 경계선이 없어 **못 가름** — 전량 암",
|
|
"rock_boundary": "암반 경계선으로 가름",
|
|
}
|
|
|
|
#: 사면 계열에서 오는 칸 — `표 키 → (B08 계열, 면)`.
|
|
#: 성토파종·절토살포는 **법면보호공**이고, 그것은 B08 에서 면고르기를 참조한다
|
|
#: (`PROTECTION_SOURCE`). 참조를 끊으면 그쪽 한 곳만 고치면 이 표도 따라온다.
|
|
SLOPE_KEYS: tuple[tuple[str, str, str], ...] = (
|
|
("benching", "bench_cut", "fill"),
|
|
("grading_fill", "face_dressing", "fill"),
|
|
("grading_cut", "face_dressing", "cut"),
|
|
("tree_removal_fill", "tree_removal", "fill"),
|
|
("tree_removal_cut", "tree_removal", "cut"),
|
|
("fill_seeding", PROTECTION_SOURCE, "fill"),
|
|
("cut_spraying", PROTECTION_SOURCE, "cut"),
|
|
)
|
|
|
|
|
|
def _num(value: Any) -> float | None:
|
|
return float(value) if isinstance(value, (int, float)) else None
|
|
|
|
|
|
def split_basis(design: dict[str, Any] | None) -> tuple[str, str] | None:
|
|
"""측구를 무슨 근거로 갈랐나 — `(코드, 사람이 읽는 말)`. 값이 없으면 `None`.
|
|
|
|
⚠ 표 칸은 숫자만 담으므로 이 말은 **표 밖**(주기·화면)에 실어야 한다. 「전량 암」이
|
|
경계선을 못 찾아서인지 정말 암 지반이어서인지는 숫자만 봐서는 알 수 없다.
|
|
"""
|
|
if not isinstance(design, dict):
|
|
return None
|
|
code = design.get("ditch_split_basis")
|
|
if not isinstance(code, str) or not code:
|
|
return None
|
|
return code, DITCH_SPLIT_LABEL.get(code, code)
|
|
|
|
|
|
def derived_cells(chainage_m: float, design: dict[str, Any] | None) -> dict[str, float | None]:
|
|
"""저장된 횡단 설계 하나에서 **채울 수 있는 칸**만 낸다.
|
|
|
|
설계가 없거나 설계선이 없으면 **빈 dict** 를 낸다 — 0 으로 때우지 않는다.
|
|
도면에 0 이 찍히면 「없다」와 「안 쟀다」를 구별할 수 없다.
|
|
"""
|
|
if not isinstance(design, dict) or not design:
|
|
return {}
|
|
|
|
cells: dict[str, float | None] = {}
|
|
for table_key, design_key in AREA_KEYS:
|
|
value = _num(design.get(design_key))
|
|
if value is not None:
|
|
cells[table_key] = value
|
|
|
|
# 사면길이는 설계선에서 유도한다 — 설계선이 없으면 유도할 것이 없다.
|
|
if not design.get("design_line"):
|
|
return cells
|
|
|
|
slope = station_slope(float(chainage_m), design)
|
|
lengths = {
|
|
_key(series, face): _length_of(slope, series, face)
|
|
for _table_key, series, face in SLOPE_KEYS
|
|
}
|
|
for table_key, series, face in SLOPE_KEYS:
|
|
cells[table_key] = lengths[_key(series, face)]
|
|
return cells
|