fix(B05): 노선 추천 균형 종단을 본 설계 횡단과 맞춤 — 곡선표 확폭 · 비탈 안 닫힘 넓힘 · 암반 경계 · 누가토량 ±500㎥(78)

- 확폭은 노선 곡선표로(후보 · 편집 띠 · [확인]) · 없으면 종전 실측 반경
- 반폭에서 비탈이 안 닫히면 지표면 끝까지 넓힌 샘플로 다시 셈(본 설계 extend_unclosed 와 같은 뜻)
- 암반 경계 기본 오프셋(본 설계 기본 설계와 같음)
- 균형 목표 = 누가토량 끝값(절토 다짐 − 성토) ±500㎥ · 고른 선은 보간 말고 바로 셈해 λ 를 더 좁힘
- 후보 줄 세우기는 보간값 그대로(빠름) · 화면에 낼 선만 바로 셈
- 추천 창 한 줄: 불균형 % → 누가토량 ±㎥

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EMfGERKUg5nMMNQsyVZ2j
This commit is contained in:
2026-10-07 23:08:43 +09:00
co-authored by Claude Opus 5.5
parent 1c30986a73
commit 0deeb52e9a
9 changed files with 291 additions and 78 deletions
+11 -3
View File
@@ -192,8 +192,12 @@ async def balanced_target(
"""확정할 노선의 균형 종단(누가거리 · 계획고) — 추천 창과 같은 코드 · 같은 상한(PLAN 74-4).
화면 값을 받지 않고 노선 정점으로 다시 셈한다(CLAUDE.md 5). 못 세우면 None(지반고 종단).
확폭은 측점 생성과 같은 곡선표(`_load_design_curves`)로.
"""
profile = await balanced_profile_of(project_id, project_root, points, criteria)
from B05_Profile.B05_Profile_Engine_Sections import _load_design_curves
curves = await asyncio.to_thread(_load_design_curves, project_root)
profile = await balanced_profile_of(project_id, project_root, points, criteria, curves)
return None if profile is None else (profile["stations_m"], profile["plan_m"])
@@ -202,6 +206,7 @@ async def balanced_profile_of(
project_root: Path,
points: list[dict[str, float]],
criteria: Any,
design_curves: list[dict[str, Any]] | None = None,
) -> dict[str, Any] | None:
"""노선 정점의 균형 종단 한 벌(`route_profile`) — [확인] 체인 · 편집 창 종단 띠가 함께 씀."""
from B05_Profile.B05_Profile_Engine_Grade import legal_grade_limit_pct
@@ -227,17 +232,19 @@ async def balanced_profile_of(
terrain,
max_grade=max_grade,
design_speed=design_speed or 20,
design_curves=design_curves,
)
)
except (FileNotFoundError, ValueError):
logger.exception("균형 종단 셈 실패(지반고 종단으로): project_id=%s", project_id)
return None
logger.info(
"균형 종단 셈: project_id=%s 측점 %d 절 %.0f 성 %.0f %.1fs",
"균형 종단 셈: project_id=%s 측점 %d 절 %.0f 성 %.0f 누가 %.0f %.1fs",
project_id,
len(profile["stations_m"]),
profile["cut_m3"],
profile["fill_m3"],
profile["mass_end_m3"],
profile["seconds"],
)
return profile
@@ -300,7 +307,8 @@ async def profile_preview(
)
# [확인]은 CSV(소수 4자리)로 적은 뒤 읽는다 — 같은 점을 셈하게 같은 자릿수로.
points = [{"x": round(float(x), 4), "y": round(float(y), 4)} for x, y in built.vertices]
profile = await balanced_profile_of(project_id, project_root, points, request.criteria)
curves = [curve.as_dict() for curve in built.curves]
profile = await balanced_profile_of(project_id, project_root, points, request.criteria, curves)
if profile is None:
return JSONResponse(
status_code=409,