refactor(472-4 ② 나눔): B06_Section_Router_Design 699줄 — 포장 · 세월교 구간 읽기 · 판정을 _Router_Design_Ranges 로(동작 같음)
- pavement_suggestions · pavement_ranges · ford_pavement_ranges · ford_surface_drops · ford_drop_at · paved_at 옮김 · Router_Design 이 다시 내보냄(부르는 곳 그대로) - 시험 test_349_0n · test_349_0p — 정본 읽기(load_culvert_sets · load_structures) 갈아 끼움 자리를 나눈 모듈로(까닭: 읽는 함수가 옮겨 감) - Router_Design 699 → 605줄 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,18 +1,13 @@
|
||||
"""B06 라우터의 횡단 설계 파일 읽기와 프리뷰 계산.
|
||||
|
||||
짝(349-0n): `pavement_ranges` · `ford_surface_drops` · `paved_at` · `ford_drop_at` ↔
|
||||
`B06_Section/logic/B06_Section_Cross_Enforce.ts` · 거울 시험 `test_349_0n_cross_enforce_mirror.py`.
|
||||
포장 · 세월교 구간 읽기 · 판정(짝 349-0n) = `B06_Section_Router_Design_Ranges.py` · 다시 내보냄.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from B05_Profile.B05_Profile_Engine_Sections import find_cross_file
|
||||
from B05_Profile.B05_Profile_Structures_Repository import load_structures
|
||||
from B05_Profile.B05_Profile_Structures_Schema import structure_type_map
|
||||
from B06_Section.B06_Section_Engine_Culvert import load_culvert_sets
|
||||
from B06_Section.B06_Section_Engine_Design import (
|
||||
ROUTE_CARRY_KEYS,
|
||||
compute_cross_design,
|
||||
@@ -21,6 +16,16 @@ from B06_Section.B06_Section_Engine_Design import (
|
||||
stored_tilt,
|
||||
)
|
||||
from B06_Section.B06_Section_Engine_Tilt import tilt_args, tilt_ranges
|
||||
|
||||
# 포장 · 세월교 구간(나눈 파일) — 이 모듈 이름으로 다시 내보냄(부르는 곳 · 시험 갈아 끼움 그대로).
|
||||
from B06_Section.B06_Section_Router_Design_Ranges import ( # noqa: F401
|
||||
ford_drop_at,
|
||||
ford_pavement_ranges,
|
||||
ford_surface_drops,
|
||||
paved_at,
|
||||
pavement_ranges,
|
||||
pavement_suggestions,
|
||||
)
|
||||
from common_util.common_util_cross_berm import (
|
||||
BERM_DEFAULT_DEPTH_M,
|
||||
BERM_DEFAULT_INTERVAL_M,
|
||||
@@ -29,15 +34,8 @@ from common_util.common_util_cross_berm import (
|
||||
BermSpec,
|
||||
)
|
||||
from common_util.common_util_route_profile import design_elevation_from_longitudinal
|
||||
from common_util.common_util_structure_station import EDGE_TOLERANCE_M
|
||||
from config.config_system import STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 세월교 노면 하강이 걸리는 측점 판정 허용 오차(m) — 세트 부착(`attach_culvert_sets`)과
|
||||
# 같은 기준이라 "구체가 그려진 측점"과 "노면이 내려간 측점"이 어긋나지 않는다.
|
||||
_FORD_DROP_TOLERANCE_M = 0.02
|
||||
|
||||
PREVIEW_DESIGN_FIELDS = (
|
||||
"ground_type",
|
||||
"roadbed_width_m",
|
||||
@@ -52,98 +50,6 @@ PREVIEW_DESIGN_FIELDS = (
|
||||
)
|
||||
|
||||
|
||||
def pavement_suggestions(longitudinal: dict[str, Any]) -> dict[float, bool]:
|
||||
mapping: dict[float, bool] = {}
|
||||
stations = longitudinal.get("stations")
|
||||
for station in stations if isinstance(stations, list) else []:
|
||||
suggested = station.get("pavement_suggested")
|
||||
if isinstance(suggested, bool):
|
||||
mapping[round(float(station.get("chainage_m", 0.0)), 3)] = suggested
|
||||
return mapping
|
||||
|
||||
|
||||
def pavement_ranges(project_root: Path) -> list[tuple[float, float]]:
|
||||
"""포장으로 볼 누가거리 구간 — 구조물 정본 G군 + 물넘이포장(포장 필수).
|
||||
|
||||
포장 지정은 **사용자가 구간으로 준다**(2026-08-28 사용자 확정). 종단경사 자동 판정은
|
||||
더 이상 포장을 켜지 않는다 — 경고 표기(`pavement_suggested`)만 남는다.
|
||||
물넘이포장은 콘크리트 등으로 노면을 만드는 시설이라 범위 안이 항상 포장이다.
|
||||
읽기 실패는 비치명 — 빈 목록이면 사용자 지정이 없는 것과 같다.
|
||||
"""
|
||||
ranges: list[tuple[float, float]] = []
|
||||
try:
|
||||
types = structure_type_map()
|
||||
for structure in load_structures(str(project_root))[1]:
|
||||
definition = types.get(structure.type_id)
|
||||
if definition is None or definition.group != "G":
|
||||
continue
|
||||
anchor = structure.chainage_m
|
||||
start = structure.start_m if structure.start_m is not None else anchor
|
||||
end = structure.end_m if structure.end_m is not None else anchor
|
||||
if start is None or end is None:
|
||||
continue
|
||||
ranges.append((min(float(start), float(end)), max(float(start), float(end))))
|
||||
except Exception: # noqa: BLE001 — 정본을 못 읽어도 설계 계산은 이어 간다
|
||||
logger.exception("B06 포장 구간을 읽지 못했습니다 (사용자 지정 없음으로 본다)")
|
||||
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]]:
|
||||
"""세월교가 앉은 측점의 노면 하강량 — [(누가거리, 월류 높이 m)].
|
||||
|
||||
구체 위 노면은 월류 높이만큼 낮게 앉는다(2026-08-30 사용자 확정). 계획고를 그만큼
|
||||
내려 잡아야 측벽·바닥판이 맞고 절·성토 면적도 따라온다. 포장 구간과 같은 방식으로
|
||||
관 지점 정본에서 읽는다 — 읽기 실패는 비치명(하강 없음으로 본다).
|
||||
"""
|
||||
if project_root is None:
|
||||
return []
|
||||
drops: list[tuple[float, float]] = []
|
||||
try:
|
||||
for chainage, spec in load_culvert_sets(project_root).items():
|
||||
if spec.get("type") != "ford":
|
||||
continue
|
||||
depth = float(spec.get("overflow_depth_m") or 0.0)
|
||||
if depth > 0:
|
||||
drops.append((chainage, depth))
|
||||
except Exception: # noqa: BLE001 — 정본을 못 읽어도 설계 계산은 이어 간다
|
||||
logger.exception("B06 세월교 월류 높이를 읽지 못했습니다 (노면 하강 없음으로 본다)")
|
||||
return drops
|
||||
|
||||
|
||||
def ford_drop_at(chainage_m: float, drops: list[tuple[float, float]]) -> float:
|
||||
"""그 측점의 노면 하강량(m). 구체가 앉은 측점만 — 붙는 기준은 세트 부착과 같다."""
|
||||
for at, depth in drops:
|
||||
if abs(chainage_m - at) <= _FORD_DROP_TOLERANCE_M:
|
||||
return depth
|
||||
return 0.0
|
||||
|
||||
|
||||
# 범위 끝 허용오차(m) — 같은 자리 거리 한 곳(323-7)
|
||||
_PAVED_EDGE_TOLERANCE_M = EDGE_TOLERANCE_M
|
||||
|
||||
|
||||
def paved_at(chainage_m: float, ranges: list[tuple[float, float]]) -> bool:
|
||||
"""이 측점을 포장으로 볼 것인가 — 노면공 · 물넘이 범위 안만 포장, 밖은 비포장(저장값 무시).
|
||||
|
||||
브라우저 짝 = `B06_Section_Cross_Refresh.ts` `pavedBySpecs`(같은 규칙 · 138-1).
|
||||
"""
|
||||
tol = _PAVED_EDGE_TOLERANCE_M
|
||||
return any(start - tol <= chainage_m <= end + tol for start, end in ranges)
|
||||
|
||||
|
||||
# 사용자 조작값 — 다시 계산해도 그대로 승계한다. **이 목록이 서버의 유일한 한 벌이다.**
|
||||
#
|
||||
# 브라우저 쪽 짝은 `B06_Section_Cross_Refresh.ts` 의 `PRESERVED_KEYS` 다(+ `status`·
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
"""B06 포장 · 세월교 구간 읽기와 판정 — `B06_Section_Router_Design.py` 에서 나눔(472-4 · 동작 같음).
|
||||
|
||||
짝(349-0n): `pavement_ranges` · `ford_surface_drops` · `paved_at` · `ford_drop_at` ↔
|
||||
`B06_Section/logic/B06_Section_Cross_Enforce.ts` · 거울 시험 `test_349_0n_cross_enforce_mirror.py`.
|
||||
부르는 쪽은 `B06_Section_Router_Design` 이름으로 씀(다시 내보냄 · 시험이 그 이름을 갈아 끼움).
|
||||
"""
|
||||
|
||||
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
|
||||
from B06_Section.B06_Section_Engine_Culvert import load_culvert_sets
|
||||
from common_util.common_util_structure_station import EDGE_TOLERANCE_M
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 세월교 노면 하강이 걸리는 측점 판정 허용 오차(m) — 세트 부착(`attach_culvert_sets`)과
|
||||
# 같은 기준이라 "구체가 그려진 측점"과 "노면이 내려간 측점"이 어긋나지 않는다.
|
||||
_FORD_DROP_TOLERANCE_M = 0.02
|
||||
|
||||
|
||||
def pavement_suggestions(longitudinal: dict[str, Any]) -> dict[float, bool]:
|
||||
mapping: dict[float, bool] = {}
|
||||
stations = longitudinal.get("stations")
|
||||
for station in stations if isinstance(stations, list) else []:
|
||||
suggested = station.get("pavement_suggested")
|
||||
if isinstance(suggested, bool):
|
||||
mapping[round(float(station.get("chainage_m", 0.0)), 3)] = suggested
|
||||
return mapping
|
||||
|
||||
|
||||
def pavement_ranges(project_root: Path) -> list[tuple[float, float]]:
|
||||
"""포장으로 볼 누가거리 구간 — 구조물 정본 G군 + 물넘이포장(포장 필수).
|
||||
|
||||
포장 지정은 **사용자가 구간으로 준다**(2026-08-28 사용자 확정). 종단경사 자동 판정은
|
||||
더 이상 포장을 켜지 않는다 — 경고 표기(`pavement_suggested`)만 남는다.
|
||||
물넘이포장은 콘크리트 등으로 노면을 만드는 시설이라 범위 안이 항상 포장이다.
|
||||
읽기 실패는 비치명 — 빈 목록이면 사용자 지정이 없는 것과 같다.
|
||||
"""
|
||||
ranges: list[tuple[float, float]] = []
|
||||
try:
|
||||
types = structure_type_map()
|
||||
for structure in load_structures(str(project_root))[1]:
|
||||
definition = types.get(structure.type_id)
|
||||
if definition is None or definition.group != "G":
|
||||
continue
|
||||
anchor = structure.chainage_m
|
||||
start = structure.start_m if structure.start_m is not None else anchor
|
||||
end = structure.end_m if structure.end_m is not None else anchor
|
||||
if start is None or end is None:
|
||||
continue
|
||||
ranges.append((min(float(start), float(end)), max(float(start), float(end))))
|
||||
except Exception: # noqa: BLE001 — 정본을 못 읽어도 설계 계산은 이어 간다
|
||||
logger.exception("B06 포장 구간을 읽지 못했습니다 (사용자 지정 없음으로 본다)")
|
||||
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]]:
|
||||
"""세월교가 앉은 측점의 노면 하강량 — [(누가거리, 월류 높이 m)].
|
||||
|
||||
구체 위 노면은 월류 높이만큼 낮게 앉는다(2026-08-30 사용자 확정). 계획고를 그만큼
|
||||
내려 잡아야 측벽·바닥판이 맞고 절·성토 면적도 따라온다. 포장 구간과 같은 방식으로
|
||||
관 지점 정본에서 읽는다 — 읽기 실패는 비치명(하강 없음으로 본다).
|
||||
"""
|
||||
if project_root is None:
|
||||
return []
|
||||
drops: list[tuple[float, float]] = []
|
||||
try:
|
||||
for chainage, spec in load_culvert_sets(project_root).items():
|
||||
if spec.get("type") != "ford":
|
||||
continue
|
||||
depth = float(spec.get("overflow_depth_m") or 0.0)
|
||||
if depth > 0:
|
||||
drops.append((chainage, depth))
|
||||
except Exception: # noqa: BLE001 — 정본을 못 읽어도 설계 계산은 이어 간다
|
||||
logger.exception("B06 세월교 월류 높이를 읽지 못했습니다 (노면 하강 없음으로 본다)")
|
||||
return drops
|
||||
|
||||
|
||||
def ford_drop_at(chainage_m: float, drops: list[tuple[float, float]]) -> float:
|
||||
"""그 측점의 노면 하강량(m). 구체가 앉은 측점만 — 붙는 기준은 세트 부착과 같다."""
|
||||
for at, depth in drops:
|
||||
if abs(chainage_m - at) <= _FORD_DROP_TOLERANCE_M:
|
||||
return depth
|
||||
return 0.0
|
||||
|
||||
|
||||
# 범위 끝 허용오차(m) — 같은 자리 거리 한 곳(323-7)
|
||||
_PAVED_EDGE_TOLERANCE_M = EDGE_TOLERANCE_M
|
||||
|
||||
|
||||
def paved_at(chainage_m: float, ranges: list[tuple[float, float]]) -> bool:
|
||||
"""이 측점을 포장으로 볼 것인가 — 노면공 · 물넘이 범위 안만 포장, 밖은 비포장(저장값 무시).
|
||||
|
||||
브라우저 짝 = `B06_Section_Cross_Refresh.ts` `pavedBySpecs`(같은 규칙 · 138-1).
|
||||
"""
|
||||
tol = _PAVED_EDGE_TOLERANCE_M
|
||||
return any(start - tol <= chainage_m <= end + tol for start, end in ranges)
|
||||
@@ -26,6 +26,7 @@ if str(ROOT) not in sys.path:
|
||||
import B05_Profile.B05_Profile_Structures_Repository as structures_repo # noqa: E402
|
||||
import B06_Section.B06_Section_Engine_Tilt as tilt_mod # noqa: E402
|
||||
import B06_Section.B06_Section_Router_Design as design_mod # noqa: E402
|
||||
import B06_Section.B06_Section_Router_Design_Ranges as ranges_mod # noqa: E402
|
||||
from B05_Profile.B05_Profile_Structures_Schema import StructureInstance # noqa: E402
|
||||
from B06_Section.B06_Section_Engine_Culvert import culvert_sets_from # noqa: E402
|
||||
from B06_Section.B06_Section_Engine_Design import compute_cross_design # noqa: E402
|
||||
@@ -157,7 +158,8 @@ def server(r) -> list[dict]:
|
||||
"""서버 [확정] 차례 그대로 — 저장분(보정 전) → 포장 구간 → 세월교 하강."""
|
||||
patch = pytest.MonkeyPatch()
|
||||
sets = culvert_sets_from(parse_pipe_points(PIPES))
|
||||
for module in (design_mod, tilt_mod):
|
||||
# 구간 읽기는 472-4 에서 `_Router_Design_Ranges` 로 나눔 — 정본 읽기를 그 모듈에서 갈아 끼움.
|
||||
for module in (ranges_mod, tilt_mod):
|
||||
patch.setattr(module, "load_culvert_sets", lambda _root: sets)
|
||||
patch.setattr(module, "load_structures", lambda _root: (0, STRUCTURES))
|
||||
patch.setattr(structures_repo, "load_structures", lambda _root: (0, STRUCTURES))
|
||||
|
||||
@@ -27,6 +27,7 @@ from test_349_0n_cross_enforce_mirror import OFFSETS, _stations, _z # noqa: E40
|
||||
import B05_Profile.B05_Profile_Structures_Repository as structures_repo # noqa: E402
|
||||
import B06_Section.B06_Section_Engine_Tilt as tilt_mod # noqa: E402
|
||||
import B06_Section.B06_Section_Router_Design as design_mod # noqa: E402
|
||||
import B06_Section.B06_Section_Router_Design_Ranges as ranges_mod # noqa: E402
|
||||
from B05_Profile.B05_Profile_Structures_Schema import StructureInstance # noqa: E402
|
||||
from B06_Section.B06_Section_Engine_Culvert import culvert_sets_from # noqa: E402
|
||||
from B06_Section.B06_Section_Engine_Design import compute_cross_design # noqa: E402
|
||||
@@ -109,7 +110,8 @@ def server(r) -> list[dict]:
|
||||
"""서버 [확정] — 저장분(입력 없는 측점은 config 암 경계) → 포장 구간 → 세월교 하강."""
|
||||
patch = pytest.MonkeyPatch()
|
||||
sets = culvert_sets_from(parse_pipe_points(PIPES))
|
||||
for module in (design_mod, tilt_mod):
|
||||
# 구간 읽기는 472-4 에서 `_Router_Design_Ranges` 로 나눔 — 정본 읽기를 그 모듈에서 갈아 끼움.
|
||||
for module in (ranges_mod, tilt_mod):
|
||||
patch.setattr(module, "load_culvert_sets", lambda _root: sets)
|
||||
patch.setattr(module, "load_structures", lambda _root: (0, STRUCTURES))
|
||||
patch.setattr(structures_repo, "load_structures", lambda _root: (0, STRUCTURES))
|
||||
|
||||
Reference in New Issue
Block a user