feat(b06): 물넘이포장 자리 횡단 = 측구 없음 · 노면은 표고 낮은 쪽으로 기움 (286-2)

- 설계 계산 ford_paved(파이썬) · fordPaved(TS 짝) — 물넘이 범위 안 측점은 측구가 안 섬(사용자 측구 선택보다 먼저)
- 서버 정본 — ford_pavement_ranges 로 기본 설계 · [저장] 재계산 · 포장 범위 맞춤이 판정 · 저장분 재계산은 되실은 값을 이음
- 화면 — 계획선 편집 재계산이 얹힌 물넘이 제원으로 같은 판정
- 노면 기울기 — 유입 = 설계선 양 끝 표고가 높은 쪽(같으면 단면유형) · 경사각도만큼 낮은 쪽(유출)으로 · 도수로 띠도 같은 쪽
- 시험 test_286_1 · 횡단 설계 거울에 물넘이 갈래 · 도수로 띠 시험 기대값 새 규칙으로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TteK8mfYZetk5P3bEZkyVZ
This commit is contained in:
2026-10-12 20:09:41 +09:00
co-authored by Claude Opus 5.5
parent 04d16e0e2f
commit 7e1b5bd27e
15 changed files with 164 additions and 18 deletions
+27 -7
View File
@@ -77,12 +77,20 @@ def pavement_ranges(project_root: Path) -> list[tuple[float, float]]:
ranges.append((min(float(start), float(end)), max(float(start), float(end))))
except Exception: # noqa: BLE001 — 정본을 못 읽어도 설계 계산은 이어 간다
logger.exception("B06 포장 구간을 읽지 못했습니다 (사용자 지정 없음으로 본다)")
for chainage, spec in load_culvert_sets(project_root).items():
if spec.get("type") != "ford_pavement":
continue
half = float(spec.get("span_m") or 0.0) / 2.0
ranges.append((chainage - half, chainage + half))
return ranges
return ranges + ford_pavement_ranges(project_root)
def ford_pavement_ranges(project_root: Path | None) -> list[tuple[float, float]]:
"""물넘이포장 범위(기준 ± 월류 폭/2) — 포장 필수 · 측구 없음(286-2). 판정은 `paved_at`."""
if project_root is None:
return []
sets = load_culvert_sets(project_root).items()
halves = [
(at, float(s.get("span_m") or 0.0) / 2.0)
for at, s in sets
if s.get("type") == "ford_pavement"
]
return [(at - half, at + half) for at, half in halves]
def ford_surface_drops(project_root: Path | None) -> list[tuple[float, float]]:
@@ -207,6 +215,7 @@ def enforce_pavement_ranges(
사용자 조작값(반폭·구조물 조정)은 그대로 승계한다.
"""
ranges = pavement_ranges(project_root)
ford_ranges = ford_pavement_ranges(project_root)
ford_drops = ford_surface_drops(project_root)
changed = 0
for section in cross_sections:
@@ -215,7 +224,8 @@ def enforce_pavement_ranges(
continue
chainage = float(section.get("chainage_m", 0.0))
wanted = paved_at(chainage, ranges)
if bool(design.get("paved")) == wanted:
ford_paved = paved_at(chainage, ford_ranges) # 물넘이 자리 = 측구 없음(286-2)
if bool(design.get("paved")) == wanted and bool(design.get("ford_paved")) == ford_paved:
continue
try:
recomputed = compute_cross_design(
@@ -238,6 +248,7 @@ def enforce_pavement_ranges(
berm=stored_berm(design),
**curve_widening_args(section),
**stored_shoulder_extend(design), # 노견 확장(139-6)
ford_paved=ford_paved,
)
except (ValueError, KeyError):
continue
@@ -266,6 +277,7 @@ def enforce_design_lines(
from B06_Section.B06_Section_Engine_Structures_Wall import berm_span_at, load_berm_spans
ford_drops = ford_surface_drops(project_root)
ford_ranges = ford_pavement_ranges(project_root)
spans = load_berm_spans(project_root)
changed = 0
for section in cross_sections:
@@ -296,6 +308,7 @@ def enforce_design_lines(
berm=stored_berm(design),
**curve_widening_args(section),
**stored_shoulder_extend(design),
ford_paved=paved_at(chainage, ford_ranges),
)
except (ValueError, KeyError):
continue
@@ -351,6 +364,7 @@ def enforce_ford_surface_drops(
berm=stored_berm(design),
**curve_widening_args(section),
**stored_shoulder_extend(design), # 노견 확장(139-6)
ford_paved=bool(design.get("ford_paved")),
)
except (ValueError, KeyError):
continue
@@ -423,6 +437,7 @@ def attach_default_designs(
modes = default_section_modes(longitudinal)
pavement = pavement_suggestions(longitudinal)
paved_ranges = pavement_ranges(project_root) if project_root else []
ford_ranges = ford_pavement_ranges(project_root)
ford_drops = ford_surface_drops(project_root)
for section in cross_sections:
if section.get("design"):
@@ -441,6 +456,7 @@ def attach_default_designs(
rock_boundary_offset_m=STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M,
surface_drop_m=ford_drop_at(chainage, ford_drops),
**curve_widening_args(section),
ford_paved=paved_at(chainage, ford_ranges),
)
design.update(status="provisional", pavement_suggested=suggested)
section["design"] = design
@@ -501,6 +517,7 @@ def recompute_designs_for_alignment(
modes = default_section_modes(longitudinal)
pavement = pavement_suggestions(longitudinal)
paved_ranges = pavement_ranges(project_root) if project_root else []
ford_ranges = ford_pavement_ranges(project_root)
ford_drops = ford_surface_drops(project_root)
stored_by_chainage = {
round(float(record["chainage_m"]), 3): (record.get("design") or {})
@@ -559,6 +576,7 @@ def recompute_designs_for_alignment(
berm=session_berms.get(key) or stored_berm(stored),
**curve_widening_args(section),
**stored_shoulder_extend(stored), # 노견 확장(139-6)
ford_paved=paved_at(chainage, ford_ranges),
)
except (ValueError, KeyError):
continue
@@ -603,6 +621,7 @@ def compute_default_designs(
default_modes = default_section_modes(longitudinal)
pavement = pavement_suggestions(longitudinal)
paved_ranges = pavement_ranges(root)
ford_ranges = ford_pavement_ranges(root)
ford_drops = ford_surface_drops(root)
results: list[tuple[float, dict[str, Any]]] = []
for chainage_m in chainages:
@@ -642,6 +661,7 @@ def compute_default_designs(
surface_drop_m=ford_drop_at(chainage_m, ford_drops),
berm=stored_berm(user),
**curve_widening_args(cross),
ford_paved=paved_at(chainage_m, ford_ranges),
)
for key in ("berm", "cut_slope_ratio_user"):
if user.get(key) is not None: