Files
Aislo/B06_Section/B06_Section_Engine_Revetment.py
T
eomsangdonandClaude Opus 5 13965e468f fix(B06): 그룹 이름표에 고른 구조물을 적고 형태 누락을 막는다
- 유입구·유출구 이름표에 지금 만지는 구조물을 괄호로 단다 — "유입구 (기슭막이)".
  추가 기슭막이 칸은 "추가 기슭막이 (2단)". 선택이 풀리면 원래 문구로 돌아간다.
- 추가 기슭막이 칸이 설 때 그 자리로 스크롤한다(칸이 화면 밖에서 생겼다).
- 독립 기슭막이 형태·높이가 그림에 안 실리던 두 자리를 고친다:
  ① 정본 읽기가 옛 전용 키(form·height_m)만 봐서 배관 키 한 벌로 저장된
     값을 놓쳤다 → 배관 키 우선, 옛 키는 폴백.
  ② 다단 전개에 소유 벽 형태를 안 넘겨 단들이 늘 메쌓기로 그려졌다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-29 18:24:15 +09:00

91 lines
4.2 KiB
Python

"""독립 기슭막이(구조물 정본 D군) 제원을 횡단 측점에 얹는다.
배관 유입·유출에 딸린 기슭막이는 관 정본이 관리하지만(`B06_Section_Engine_Culvert`),
배관과 무관한 **독립 기슭막이**는 구조물 정본(`structures.json`)이 정본이다. 여기서는
구간(시작~종료) 안의 측점에 형태·높이·설치 측을 붙이기만 한다 — 치수 결정·도형은
화면(`B06_Section_UI_Cross_Revetment`)과 3D가 같은 산식으로 그린다(2026-08-28 사용자 확정).
측점 자체는 `B05_Profile_Engine_Sections.resolve_extra_stations`가 시작·기준·종료에 심는다.
"""
import logging
from pathlib import Path
from typing import Any
from B05_Profile.B05_Profile_Structures_Repository import load_structures
from B05_Profile.B05_Profile_Structures_Schema import structure_type_map
logger = logging.getLogger(__name__)
# 구간 경계 측점을 구간 안으로 볼 허용 오차(m) — 정본이 누가거리를 0.01m로 끊어 쓴다.
_EDGE_TOLERANCE_M = 0.02
def load_revetments(project_root: Path) -> list[dict[str, Any]]:
"""구조물 정본에서 독립 기슭막이(D군 구간형) 목록을 읽는다. 실패하면 빈 목록."""
try:
types = structure_type_map()
found: list[dict[str, Any]] = []
for structure in load_structures(str(project_root))[1]:
definition = types.get(structure.type_id)
if definition is None or definition.group != "D" or definition.placement != "interval":
continue
start, end = structure.start_m, structure.end_m
if start is None or end is None:
continue
options = structure.options or {}
found.append(
{
"structure_id": structure.structure_id,
"type_id": structure.type_id,
"name": definition.name,
"start_m": float(min(start, end)),
"end_m": float(max(start, end)),
"anchor_m": float(structure.anchor_m()),
# 형태·높이는 **배관 유출 키 한 벌**이 정본이다(B05 폼 2026-08-28
# 이관). 옛 전용 키(form·height_m)는 읽을 때만 폴백으로 본다 —
# 이걸 안 보면 형태가 비어 도형이 늘 메쌓기로 그려졌다(2026-08-30).
"form": options.get("outlet_revet_form") or options.get("form"),
"height_m": (
options.get("outlet_revet_height_m")
if options.get("outlet_revet_height_m") is not None
else options.get("height_m")
),
"side": options.get("side"),
# 다단 — 1이면 단일 벽. 전개 규칙은 화면·3D가 같은 산식으로 푼다.
"tiers": options.get("tiers"),
# 자리 이동(사용자 조작) — 사면 위로 올림 / 좌우 이동.
"lift_m": options.get("lift_m"),
"shift_m": options.get("shift_m"),
}
)
return found
except Exception: # noqa: BLE001 — 정본을 못 읽어도 횡단 조회는 이어 간다
logger.exception("B06 독립 기슭막이 정본을 읽지 못했습니다 (없는 것으로 본다)")
return []
def attach_revetments(project_root: Path, cross_sections: list[dict[str, Any]]) -> int:
"""구간 안 측점의 횡단 dict에 `revetment` 키를 얹는다. 얹은 개수를 돌려준다.
한 측점에 여러 개가 겹치면 **먼저 시작한 것**을 쓴다 — 겹침 정리는 사용자 몫이다.
"""
revetments = load_revetments(project_root)
if not revetments:
return 0
attached = 0
for section in cross_sections:
chainage = section.get("chainage_m")
if not isinstance(chainage, (int, float)):
continue
for spec in revetments:
if (
spec["start_m"] - _EDGE_TOLERANCE_M
<= float(chainage)
<= spec["end_m"] + _EDGE_TOLERANCE_M
):
section["revetment"] = spec
attached += 1
break
return attached