- [저장] 상단측 차례(POST /route/confirm)와 측점 맞춤이 _merge_irregular_into_longitudinal 로 구조물 측점 줄을 통째 갈음 → uphill_side_source=user(값은 자동으로 돌아감) · pavement_* 사라짐(검증_sub8 측점 351) - 같은 자리 옛 줄에서 STATION_CARRY_KEYS 를 들고 감 · 사용자 상단측이 새 자동값을 이김 · 옛 줄 없는 새 자리는 포장 표기를 지금 계획선으로 셈 - 시험 test_472_station_user_fields(옛 코드 빨강 3) · X6 TS ROW_CARRY_KEYS = 파이썬 CARRY_INPUT_KEYS 거울 · 위키 규칙 한 줄 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
131 lines
5.3 KiB
Python
131 lines
5.3 KiB
Python
"""PLAN 472 덧 (요구 272) — 구조물 측점을 다시 떠도 사용자 상단측 · 포장 제안 표기가 남음.
|
|
|
|
증상 — [저장] 상단측 차례(`POST /route/confirm` → `_append_irregular_cross_sections`) · 측점 맞춤
|
|
(`ensure_structure_stations`)이 `_merge_irregular_into_longitudinal` 로 구조물 측점 줄을 새 줄로
|
|
통째 갈음 → 같은 자리 옛 줄의 `uphill_side_source: user`(상단측 손 지정 · 값은 자동으로 돌아감) ·
|
|
`pavement_*`(포장 제안 표기)가 사라짐(검증_sub8 측점 351).
|
|
고침 — 같은 자리(누가거리) 옛 줄에서 `STATION_CARRY_KEYS` 를 들고 감 · 사용자 상단측은 새 자동값을
|
|
이김 · 옛 줄 없는 새 자리는 포장 표기를 지금 계획선으로 셈.
|
|
X6 — 옮긴 측점이 들고 가는 횡단 입력 TS `ROW_CARRY_KEYS` = 파이썬 `CARRY_INPUT_KEYS`(거울).
|
|
|
|
./venv/Scripts/python.exe -m pytest resources/tester/test_472_station_user_fields.py -q
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
ROOT = HERE.parents[1]
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from B05_Profile.B05_Profile_Router_Confirm import ( # noqa: E402
|
|
_merge_irregular_into_longitudinal,
|
|
)
|
|
from B06_Section.B06_Section_Router_Design import CARRY_INPUT_KEYS # noqa: E402
|
|
|
|
RELATIVE = "B06_Section/longitudinal/longitudinal.json"
|
|
CRITERIA = {
|
|
"max_grade_pct": 14.0,
|
|
"max_reverse_grade_pct": 5.0,
|
|
"min_vertical_radius_m": 100.0,
|
|
"min_curve_length_m": 20.0,
|
|
"min_tangent_length_m": 20.0,
|
|
"vertical_curve_skip_delta_pct": 5.0,
|
|
"design_speed_kph": 20,
|
|
"terrain_type": "special",
|
|
"paved": False,
|
|
"main_direction": "auto",
|
|
"balance_segment_length_m": None,
|
|
"start_elevation_offset_m": 0.0,
|
|
"end_elevation_offset_m": 0.0,
|
|
"enforce_pipe_clearance": False,
|
|
"resolved_main_direction": "ascending",
|
|
}
|
|
|
|
|
|
def _row(at: float, kind: str = "regular", **extra) -> dict:
|
|
return {"chainage_m": at, "kind": kind, "station_id": f"s{at}", "uphill_side": "left", **extra}
|
|
|
|
|
|
def _pave(grade: float) -> dict:
|
|
return {
|
|
"pavement_suggested": grade > 14.0,
|
|
"pavement_grade_pct": grade,
|
|
"pavement_grade_limit_pct": 14.0,
|
|
}
|
|
|
|
|
|
def _write(tmp_path: Path) -> Path:
|
|
# 계획선 0 → 100 m 고르게 20 % 오르막(포장 상한 14 % 넘음)
|
|
samples = [{"chainage_m": float(s), "elevation_m": 0.2 * s} for s in range(0, 101, 5)]
|
|
stations = [
|
|
_row(0.0, "bp", uphill_side_source="user", **_pave(20.0)),
|
|
_row(20.0, uphill_side_source="user", **_pave(20.0)),
|
|
_row(
|
|
35.0,
|
|
"irregular",
|
|
structure="관 D1000",
|
|
uphill_side="right",
|
|
uphill_side_source="user",
|
|
**_pave(20.0),
|
|
), # fmt: skip
|
|
_row(40.0, uphill_side="right", uphill_side_source="user", **_pave(20.0)),
|
|
_row(100.0, "ep", uphill_side_source="user", **_pave(20.0)),
|
|
]
|
|
path = tmp_path / RELATIVE
|
|
path.parent.mkdir(parents=True)
|
|
longitudinal = {
|
|
"stations": stations,
|
|
"samples": samples,
|
|
"design_profiles": [{"criteria": CRITERIA, "samples": samples}],
|
|
}
|
|
path.write_text(json.dumps(longitudinal), encoding="utf-8")
|
|
return path
|
|
|
|
|
|
def _fresh(at: float, **extra) -> dict:
|
|
"""새로 뜬 구조물 측점 — 상단측은 지형 자동값 · 포장 표기 · 출처 칸 없음."""
|
|
return {"chainage_m": at, "kind": "irregular", "station_id": f"n{at}", "uphill_side": "left",
|
|
"structure": "관 D1000", **extra} # fmt: skip
|
|
|
|
|
|
def _merged(tmp_path: Path, fresh: list[dict]) -> dict[float, dict]:
|
|
path = _write(tmp_path)
|
|
_merge_irregular_into_longitudinal(tmp_path, RELATIVE, fresh)
|
|
rows = json.loads(path.read_text(encoding="utf-8"))["stations"]
|
|
return {row["chainage_m"]: row for row in rows}
|
|
|
|
|
|
def test_같은_자리_다시_떠도_사용자_상단측_포장_표기_남음(tmp_path) -> None:
|
|
rows = _merged(tmp_path, [_fresh(35.0)])
|
|
row = rows[35.0]
|
|
assert row["station_id"] == "n35.0" # 기하 · 글은 새 줄
|
|
assert row["uphill_side"] == "right" and row["uphill_side_source"] == "user"
|
|
assert row["pavement_suggested"] is True and row["pavement_grade_pct"] == 20.0
|
|
|
|
|
|
def test_격자_자리에_구조물이_얹혀도_사용자_상단측_남음(tmp_path) -> None:
|
|
rows = _merged(tmp_path, [_fresh(35.0), _fresh(40.0, grid=True)])
|
|
assert rows[40.0]["uphill_side"] == "right" and rows[40.0]["uphill_side_source"] == "user"
|
|
assert rows[40.0]["pavement_grade_limit_pct"] == 14.0
|
|
|
|
|
|
def test_새_자리는_포장_표기를_지금_계획선으로(tmp_path) -> None:
|
|
rows = _merged(tmp_path, [_fresh(55.0)])
|
|
assert 35.0 not in rows # 옛 구조물 측점은 걷힘
|
|
row = rows[55.0]
|
|
assert row["uphill_side"] == "left" and "uphill_side_source" not in row # 새 자리 = 지형 자동
|
|
assert row["pavement_suggested"] is True
|
|
assert row["pavement_grade_pct"] == 20.0 and row["pavement_grade_limit_pct"] == 14.0
|
|
|
|
|
|
def test_X6_옮긴_측점_횡단_입력_TS_파이썬_한_벌() -> None:
|
|
source = (ROOT / "B06_Section/logic/B06_Section_Station_Swap.ts").read_text(encoding="utf-8")
|
|
body = re.search(r"export const ROW_CARRY_KEYS = \[(.*?)\] as const;", source, re.S)
|
|
assert body is not None
|
|
assert tuple(re.findall(r'"([a-z_0-9]+)"', body.group(1))) == CARRY_INPUT_KEYS
|