106 lines
3.5 KiB
Python
106 lines
3.5 KiB
Python
"""455 (433-13 ③) 옛 정책 종단 — 법정 최소 R · L 미달 경고가 초안 · 저장 뒤 같음.
|
|
|
|
까닭: 옛 저장 정책엔 `min_vertical_radius_m` · `min_curve_length_m` 가 없어 화면 초안(저장
|
|
정책 그대로) 경고 0 · 노선 [저장](새 정책)에서야 뜸. 고침: 빈 값은 첫 계획선 설계 기준(criteria =
|
|
설계속도 법정값)
|
|
— TS `readAlignment` ↔ 서버 `rebuild_alignment_profile` 같은 규칙(`helper_455_old_policy.cjs`).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import copy
|
|
import json
|
|
import math
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
ROOT = HERE.parents[1]
|
|
if str(ROOT) not in sys.path:
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from B05_Profile.B05_Profile_Engine_Grade import GradeDesignOptions # noqa: E402
|
|
from B05_Profile.B05_Profile_Engine_Grade_Profile import ( # noqa: E402
|
|
design_ground_following_profile,
|
|
rebuild_alignment_profile,
|
|
)
|
|
|
|
# 설계속도 40 — 법정 최소 R 450 · L 40 > 기본 L 20 → 곡선마다 미달
|
|
OPTIONS = GradeDesignOptions(
|
|
max_grade_pct=14.0,
|
|
max_reverse_grade_pct=5.0,
|
|
min_vertical_radius_m=450.0,
|
|
min_curve_length_m=40.0,
|
|
min_tangent_length_m=20.0,
|
|
vertical_curve_skip_delta_pct=0.5,
|
|
design_speed_kph=40,
|
|
)
|
|
|
|
|
|
def _ground(s: float) -> float:
|
|
return 100.0 + 0.05 * s + 2.0 * math.sin(s / 30.0)
|
|
|
|
|
|
def _fresh() -> dict:
|
|
samples = [
|
|
{"chainage_m": float(s), "elevation_m": round(_ground(s), 6), "valid": True}
|
|
for s in range(0, 401)
|
|
]
|
|
stations = [{"chainage_m": float(s), "kind": "regular"} for s in range(0, 401, 20)]
|
|
data = {"length_m": 400.0, "samples": samples, "stations": stations}
|
|
alignment, profile = design_ground_following_profile(data, OPTIONS, station_interval_m=20.0)
|
|
data["profile_alignment"], data["design_profiles"] = alignment, [profile]
|
|
return data
|
|
|
|
|
|
def _old() -> dict:
|
|
data = _fresh()
|
|
for key in ("min_vertical_radius_m", "min_curve_length_m"):
|
|
data["profile_alignment"]["policy"].pop(key)
|
|
return data
|
|
|
|
|
|
def _law(warnings: list[str]) -> list[str]:
|
|
return [text for text in warnings if "법정 최소" in text]
|
|
|
|
|
|
def _ts(tmp_path: Path, data: dict) -> dict:
|
|
source = tmp_path / "in455.json"
|
|
source.write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8")
|
|
proc = subprocess.run( # noqa: S603 — 고정 실행 파일
|
|
["node", str(HERE / "helper_455_old_policy.cjs"), str(source)],
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
cwd=str(ROOT),
|
|
check=False,
|
|
)
|
|
assert proc.returncode == 0, proc.stderr
|
|
return json.loads(proc.stdout)
|
|
|
|
|
|
def test_새_정책_경고_기준(tmp_path: Path) -> None:
|
|
fresh = _fresh()
|
|
law = _law(fresh["profile_alignment"]["warnings"])
|
|
assert law
|
|
assert _ts(tmp_path, fresh)["warnings"] == law
|
|
|
|
|
|
def test_옛_정책_초안_경고_저장_뒤와_같음(tmp_path: Path) -> None:
|
|
expected = _law(_fresh()["profile_alignment"]["warnings"])
|
|
old = _old()
|
|
ts = _ts(tmp_path, copy.deepcopy(old))
|
|
assert ts["policy"]["min_vertical_radius_m"] == 450.0
|
|
assert ts["policy"]["min_curve_length_m"] == 40.0
|
|
assert ts["warnings"] == expected
|
|
alignment, _profile = rebuild_alignment_profile(old, {})
|
|
assert _law(alignment["warnings"]) == expected
|
|
assert alignment["policy"]["min_curve_length_m"] == 40.0
|
|
|
|
|
|
def test_criteria_없는_판은_그대로(tmp_path: Path) -> None:
|
|
old = _old()
|
|
old["design_profiles"][0].pop("criteria")
|
|
assert _ts(tmp_path, copy.deepcopy(old))["warnings"] == []
|