feat(b05,b06): 물넘이포장 콘크리트 윗면 = 설계 노면 · 포장층 노견까지 · 월류 높이 점선은 노면 위 (287-2 · 287-4)

- 설계 계산 tilted · tilt_pct(파이썬 · TS 짝) — 물넘이 범위 노면 = 경사각도로 지대 낮은 쪽 한 경사 · 측구 없음 · 중심점 = 계획고(콘크리트 윗면) · 노면 끝에서 사면이 이어짐(짧으면 성토)
- 서버 tilt_ranges · tilt_args · stored_tilt — 기본 설계 · [저장] 재계산 · 포장 범위 맞춤 · 저장분 재계산이 같은 값(옛 ford_paved 갈음)
- 횡단 그림 — 포장층 = 노면공 포장 형상(appendPavementOverlay)을 노견까지 · 물넘이 표식 · 월류 높이선 = 노면 + 높이 같은 각도 · 옛 파임 · 노면 걷기 그림 걷음
- 3D 노면 파기 걷음(설계 노면이 곧 윗면) · 코리도 빌드 판 94
- 시험 test_287 · 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:57:54 +09:00
co-authored by Claude Opus 5.5
parent 0c7f71d103
commit a5a9a7cec0
23 changed files with 273 additions and 288 deletions
+35 -13
View File
@@ -13,6 +13,7 @@ from B06_Section.B06_Section_Engine_Design import (
compute_cross_design,
curve_widening_args,
stored_shoulder_extend,
stored_tilt,
)
from common_util.common_util_cross_berm import (
BERM_DEFAULT_DEPTH_M,
@@ -93,6 +94,27 @@ def ford_pavement_ranges(project_root: Path | None) -> list[tuple[float, float]]
return [(at - half, at + half) for at, half in halves]
def tilt_ranges(project_root: Path | None) -> list[tuple[float, float, float | None]]:
"""측구 없이 노면을 지대 낮은 쪽 한 경사로 기울이는 자리(287-4) — 물넘이포장 범위 ·
경사 = 경사각도(%)."""
if project_root is None:
return []
out: list[tuple[float, float, float | None]] = []
for at, spec in load_culvert_sets(project_root).items():
if spec.get("type") == "ford_pavement":
half = float(spec.get("span_m") or 0.0) / 2.0
out.append((at - half, at + half, spec.get("slope_pct")))
return out
def tilt_args(chainage_m: float, ranges: list[tuple[float, float, float | None]]) -> dict[str, Any]:
"""그 측점의 구조물 자리 기울기 — `compute_cross_design(**...)` 로 넘김 ·
저장분 짝 `stored_tilt`."""
hit = next((r for r in ranges if paved_at(chainage_m, [(r[0], r[1])])), None)
pct = hit[2] if hit else None
return {"tilted": hit is not None, "tilt_pct": float(pct) if pct is not None else None}
def ford_surface_drops(project_root: Path | None) -> list[tuple[float, float]]:
"""세월교가 앉은 측점의 노면 하강량 — [(누가거리, 월류 높이 m)].
@@ -215,7 +237,7 @@ def enforce_pavement_ranges(
사용자 조작값(반폭·구조물 조정)은 그대로 승계한다.
"""
ranges = pavement_ranges(project_root)
ford_ranges = ford_pavement_ranges(project_root)
tilts = tilt_ranges(project_root)
ford_drops = ford_surface_drops(project_root)
changed = 0
for section in cross_sections:
@@ -224,8 +246,8 @@ def enforce_pavement_ranges(
continue
chainage = float(section.get("chainage_m", 0.0))
wanted = paved_at(chainage, ranges)
ford_paved = paved_at(chainage, ford_ranges) # 물넘이 자리 = 측구 없음(286-2)
if bool(design.get("paved")) == wanted and bool(design.get("ford_paved")) == ford_paved:
tilt = tilt_args(chainage, tilts) # 물넘이 자리 = 측구 없음 · 기울기(286-2 · 287-4)
if bool(design.get("paved")) == wanted and stored_tilt(design) == tilt:
continue
try:
recomputed = compute_cross_design(
@@ -248,7 +270,7 @@ def enforce_pavement_ranges(
berm=stored_berm(design),
**curve_widening_args(section),
**stored_shoulder_extend(design), # 노견 확장(139-6)
ford_paved=ford_paved,
**tilt,
)
except (ValueError, KeyError):
continue
@@ -277,7 +299,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)
tilts = tilt_ranges(project_root)
spans = load_berm_spans(project_root)
changed = 0
for section in cross_sections:
@@ -308,7 +330,7 @@ def enforce_design_lines(
berm=stored_berm(design),
**curve_widening_args(section),
**stored_shoulder_extend(design),
ford_paved=paved_at(chainage, ford_ranges),
**tilt_args(chainage, tilts),
)
except (ValueError, KeyError):
continue
@@ -364,7 +386,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")),
**stored_tilt(design),
)
except (ValueError, KeyError):
continue
@@ -437,7 +459,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)
tilts = tilt_ranges(project_root)
ford_drops = ford_surface_drops(project_root)
for section in cross_sections:
if section.get("design"):
@@ -456,7 +478,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),
**tilt_args(chainage, tilts),
)
design.update(status="provisional", pavement_suggested=suggested)
section["design"] = design
@@ -517,7 +539,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)
tilts = tilt_ranges(project_root)
ford_drops = ford_surface_drops(project_root)
stored_by_chainage = {
round(float(record["chainage_m"]), 3): (record.get("design") or {})
@@ -576,7 +598,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),
**tilt_args(chainage, tilts),
)
except (ValueError, KeyError):
continue
@@ -621,7 +643,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)
tilts = tilt_ranges(root)
ford_drops = ford_surface_drops(root)
results: list[tuple[float, dict[str, Any]]] = []
for chainage_m in chainages:
@@ -661,7 +683,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),
**tilt_args(chainage_m, tilts),
)
for key in ("berm", "cut_slope_ratio_user"):
if user.get(key) is not None: