feat(B06): 횡단 설계가 사토장 단면을 냄 — 노선 성토와 갈라서

사토장이 선 측점은 **노면 끝 바깥이 사토장 몫**이라 노선 성토에서 빼야 함.
안 빼면 같은 흙을 두 번 셈(2026-09-09 확정 ㉠).

- `compute_cross_design(spoil_fill=…)` / `computeCrossDesign({spoilFill})` 신설(짝)
- 새 칸: `spoil_fill_area_m2` · `spoil_fill_side` · `spoil_fill_width_m` ·
  `spoil_fill_max_width_m` · `spoil_fill_line` · `spoil_fill_unclosed` ·
  `spoil_fill_replaced_fill_m2`(노선 성토에서 뺀 몫 — 되짚기용)
- **합쳐서 하나로 내지 않음** — 받는 쪽이 갈라 볼 수 있어야 함
- 기울기가 비면 그 측점의 노선 성토 기울기를 그대로 씀(새 값 안 만듦)
- `fillAreaBeyond`/`_fill_area_beyond` — 경계 종거를 보간해 자름(한 칸도 안 흘림)
- 거울 시험에 사토장 사례 둘 추가 + 값이 0 이면 잡히는 가드

⚠ `B06_Section_Engine_Design.py` 가 924줄 — 700줄 제한 초과 상태임(이번 전에도 881줄).
  기능이 다 선 뒤 분리할 자리.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-09 07:51:41 +09:00
co-authored by Claude Opus 5
parent 1379b7fd69
commit d9b801aeaa
4 changed files with 175 additions and 1 deletions
+43
View File
@@ -33,6 +33,7 @@ from typing import Any
from B06_Section.B06_Section_Engine_Areas import (
_bench_cut_length,
_fill_area_beyond,
_split_cut_areas,
_split_ditch_area,
_trapezoid_areas,
@@ -43,6 +44,7 @@ from common_util.common_util_cross_berm import (
fill_profile_points,
)
from common_util.common_util_cross_berm import elevation_at as berm_elevation_at
from common_util.common_util_spoil_fill import spoil_fill_section
from config.config_system import (
CURVE_WIDENING_MAX_WIDTH_M,
SECTION_DITCH_SIDES,
@@ -581,6 +583,7 @@ def compute_cross_design(
curve_outer_side: str | None = None,
curve_widening_m: float | None = None,
berm: BermSpec | None = None,
spoil_fill: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""측점 하나의 표준횡단 설계선과 절·성토 단면적을 계산한다.
@@ -600,6 +603,10 @@ def compute_cross_design(
surface_drop_m: 노면을 통째로 내리는 양(m) — 세월교 월류 높이. 구체 위 노면은 월류
높이만큼 낮게 앉으므로 계획고를 그만큼 내려 잡는다. 단면 전체가 평행 이동하므로
횡단경사·측구·사면 규칙은 그대로고 절·성토 면적만 따라 바뀐다(2026-08-30 사용자).
spoil_fill: 이 측점에 선 유용토운반작업장(구 사토장) — `{"side", "width_m", "slope_ratio_n"}`.
폭은 **노면 끝**(노견이 시작하는 자리)에서 재고, 그 바깥 성토는 **노선 몫이 아니라
사토장 몫**이라 `fill_area_m2` 에서 뺀다(2026-09-09 확정 ㉠ — 두 번 세지 않기).
`slope_ratio_n` 이 비면 그 측점의 **노선 성토 기울기**를 그대로 쓴다.
"""
if ground_type not in SECTION_GROUND_TYPE_PRESET:
raise ValueError(f"지원하지 않는 지반유형입니다: {ground_type}")
@@ -710,6 +717,26 @@ def compute_cross_design(
abs(diffs[0]) > _SLOPE_CLOSE_TOLERANCE_M or abs(diffs[-1]) > _SLOPE_CLOSE_TOLERANCE_M
)
# 사토장(유용토운반작업장) — 노면 끝 바깥에 쌓는 성토. 짝: TS `computeCrossDesign`.
# ⚠ 그 바깥 성토는 **노선 몫이 아니다** — 빼지 않으면 같은 흙을 두 번 센다(확정 ㉠).
spoil_section = None
spoil_replaced = 0.0
spoil_side = str((spoil_fill or {}).get("side") or "")
spoil_width = _as_float((spoil_fill or {}).get("width_m"), 0.0)
if spoil_side in ("left", "right") and spoil_width > 0:
spoil_x0 = geometry.half_road_left if spoil_side == "left" else -geometry.half_road_right
spoil_ratio = _as_float((spoil_fill or {}).get("slope_ratio_n"), 0.0) or geometry.fill_ratio
spoil_section = spoil_fill_section(
valid,
spoil_x0,
geometry.road_z(spoil_x0),
spoil_side,
spoil_width,
spoil_ratio,
)
spoil_replaced = _fill_area_beyond(offsets, diffs, spoil_x0, spoil_side)
fill_area = max(fill_area - spoil_replaced, 0.0)
# 절토면적 토사/암반 분리 — 지표면~암반 경계선이 토사, 그 아래가 암이다. 경계선 위치가
# 곧 유토곡선 EA/RR/BR 비율을 만들므로, 사용자가 경계선을 올리내리면 이 값이 함께 바뀐다.
# 토사 지반은 암반 경계선 자체가 없어 전량 토사, 암 지반인데 경계선 값이 없으면(구 데이터)
@@ -836,6 +863,22 @@ def compute_cross_design(
"cut_rock_area_m2": round(cut_rock_area, 4),
"cut_rock_kind": cut_rock_kind,
"fill_area_m2": round(fill_area, 4),
# 사토장 몫 — **`fill_area_m2` 와 합치지 않는다**(받는 쪽이 갈라 볼 수 있어야 한다).
"spoil_fill_area_m2": round(spoil_section.area_m2, 4) if spoil_section else 0.0,
"spoil_fill_side": spoil_side if spoil_section else None,
"spoil_fill_width_m": round(spoil_width, 4) if spoil_section else 0.0,
"spoil_fill_max_width_m": round(spoil_section.max_width_m, 4) if spoil_section else 0.0,
"spoil_fill_line": (
[
{"offset_m": offset, "elevation_m": elevation}
for offset, elevation in spoil_section.line
]
if spoil_section
else []
),
"spoil_fill_unclosed": bool(spoil_section.unclosed) if spoil_section else False,
# 사토장이 대신 차지해 노선 성토에서 뺀 몫(㎡) — 되짚기용. 합계에 또 넣지 말 것.
"spoil_fill_replaced_fill_m2": round(spoil_replaced, 4),
# 층따기 밑수 — 성토부 아래 원지반(1:4 보다 급한 구간)의 지표면 길이(m).
# B08 이 측점 사이를 이어 ㎡ 로 만든다. 여기서 ㎥ 로 바꾸지 않는다 —
# 단의 높이·폭이 설계도서 값이라 지어낼 수 없다.