- [저장] 횡단 차례 ensure_structure_stations 가 곡선 측점 어긋남(_curve_marks)도 셈 · 노선 저장 안 기다림 - 다시 뜰 때 곡선 측점도 뜸(sample_structure_sections with_curves) · 종단 병합이 옛 곡선 측점도 갈음 - 측점 자리 한 벌 _station_layout 으로 뺌 · planned_curve_stations = generate_sections 와 같은 목록(표본 없음) - 시험: test_407_3_curve_station_follow(놓음 = 구조물 측점 하나 · 뺌 = 곡선 측점 되살아남) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QyNXY2TUKXCFMwAca2APG
131 lines
5.5 KiB
Python
131 lines
5.5 KiB
Python
"""PLAN 407-3 — 구조물을 MC 위에 놓았다 빼면 서버 종단 정본의 곡선 측점이 되살아남.
|
|
|
|
[저장] 차례(관 · 구조물 → 횡단) 의 `ensure_structure_stations` 길 — `_curve_marks`(어긋남 판정) ·
|
|
`sample_structure_sections(with_curves=True)`(곡선 측점도 뜸) · `_merge_irregular_into_longitudinal`
|
|
(옛 곡선 측점도 새 목록이 갈음). 노선 저장을 안 기다림 · 놓은 동안엔 그 자리 구조물 측점 하나.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
if str(ROOT) not in sys.path:
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from B05_Profile import B05_Profile_Engine_Sections as sections # noqa: E402
|
|
from B05_Profile import B05_Profile_Engine_Sections_Core as core # noqa: E402
|
|
from B05_Profile.B05_Profile_Router_Confirm import ( # noqa: E402
|
|
_merge_irregular_into_longitudinal,
|
|
)
|
|
from B06_Section.B06_Section_Router_Stations import _curve_marks # noqa: E402
|
|
|
|
LINE = [[0.0, 0.0, 100.0], [1000.3, 0.0, 100.0]]
|
|
SHORT = {"start": [101.0, 0.0], "apex": [107.3, 2.0], "end": [113.6, 0.0], "radius_m": 30.0}
|
|
LONG = {"start": [616.1, 0.0], "apex": [631.3, 3.0], "end": [646.5, 0.0], "radius_m": 40.0}
|
|
|
|
|
|
class _Flat:
|
|
def sample_xy(self, xy):
|
|
count = len(np.asarray(xy, dtype=np.float64))
|
|
return np.full(count, 100.0), np.ones(count, dtype=bool)
|
|
|
|
|
|
@pytest.fixture
|
|
def project(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> dict:
|
|
"""노선 · 곡선표 · 관 · 구조물 정본 대신 — 바꿀 수 있는 구조물 측점 하나(`extras`)."""
|
|
state: dict = {"extras": ()}
|
|
monkeypatch.setattr(sections, "_load_route_polyline", lambda *_: LINE)
|
|
monkeypatch.setattr(sections, "_load_design_curves", lambda *_: [SHORT, LONG])
|
|
monkeypatch.setattr(sections, "load_station_snap", lambda *_: False)
|
|
monkeypatch.setattr(sections, "load_station_snap_step", lambda *_: None)
|
|
monkeypatch.setattr(sections, "build_surface_sampler", lambda *_, **__: _Flat())
|
|
monkeypatch.setattr(sections, "_load_pipe_points", lambda *_: [])
|
|
monkeypatch.setattr(sections, "resolve_extra_stations", lambda *_: state["extras"])
|
|
# 노선 저장 때 정본 = 전체 생성(곡선 측점 107 · 631)
|
|
full = core.generate_sections(LINE, _Flat(), design_curves=[SHORT, LONG])["longitudinal"]
|
|
folder = tmp_path / "B06_Section" / "longitudinal"
|
|
folder.mkdir(parents=True)
|
|
(tmp_path / "B06_Section" / "cross_sections").mkdir()
|
|
(folder / "longitudinal.json").write_text(json.dumps(full), encoding="utf-8")
|
|
state["root"] = tmp_path
|
|
return state
|
|
|
|
|
|
def _save(state: dict) -> list[dict]:
|
|
"""[저장] 의 횡단 차례 — 어긋나면 다시 떠 병합(`ensure_structure_stations` 와 같은 길)."""
|
|
root = state["root"]
|
|
options = core.SectionGenerationOptions()
|
|
if _curve_marks(root, "route.json", options) == 0 and not state["extras"]:
|
|
return _stations(root)
|
|
fresh = sections.generate_irregular_sections(
|
|
root, "route.json", "f", "m", False, extra_stations=state["extras"], options=options
|
|
)
|
|
_merge_irregular_into_longitudinal(root, "B06_Section/longitudinal/longitudinal.json", fresh)
|
|
return _stations(root)
|
|
|
|
|
|
def _stations(root: Path) -> list[dict]:
|
|
path = root / "B06_Section" / "longitudinal" / "longitudinal.json"
|
|
return json.loads(path.read_text(encoding="utf-8"))["stations"]
|
|
|
|
|
|
def _at(stations: list[dict], chainage: float) -> list[str]:
|
|
return [s["kind"] for s in stations if abs(s["chainage_m"] - chainage) < 0.01]
|
|
|
|
|
|
def test_처음은_맞음(project) -> None:
|
|
assert _curve_marks(project["root"], "route.json", core.SectionGenerationOptions()) == 0
|
|
assert _at(_stations(project["root"]), 107.0) == ["curve"]
|
|
|
|
|
|
def test_MC_위_구조물_놓음_뺌(project) -> None:
|
|
root = project["root"]
|
|
project["extras"] = ((107.0, "관"),)
|
|
assert _curve_marks(root, "route.json", core.SectionGenerationOptions()) == 1
|
|
placed = _save(project)
|
|
assert _at(placed, 107.0) == ["irregular"] # 놓은 동안 = 구조물 측점 하나
|
|
assert _at(placed, 631.0) == ["curve"] # 다른 곡선 측점은 그대로
|
|
assert list((root / "B06_Section" / "cross_sections").glob("cross_*.json"))
|
|
|
|
project["extras"] = ()
|
|
assert _curve_marks(root, "route.json", core.SectionGenerationOptions()) == 1
|
|
removed = _save(project)
|
|
assert _at(removed, 107.0) == ["curve"] # 노선 저장 없이 되살아남
|
|
assert _at(removed, 631.0) == ["curve"]
|
|
assert _curve_marks(root, "route.json", core.SectionGenerationOptions()) == 0
|
|
assert len(removed) == len(_stations_full())
|
|
|
|
|
|
def _stations_full() -> list[dict]:
|
|
return core.generate_sections(LINE, _Flat(), design_curves=[SHORT, LONG])["longitudinal"][
|
|
"stations"
|
|
]
|
|
|
|
|
|
def test_planned_curve_stations_는_생성과_같음() -> None:
|
|
extras = ((107.0, "관"), (300.0, "옹벽"))
|
|
for snap, step in [(False, None), (True, None), (True, 0.1)]:
|
|
options = core.SectionGenerationOptions(extra_stations=extras)
|
|
made = core.generate_sections(
|
|
LINE,
|
|
_Flat(),
|
|
options,
|
|
design_curves=[SHORT, LONG],
|
|
station_snap=snap,
|
|
station_snap_step=step,
|
|
)["longitudinal"]["stations"]
|
|
planned = core.planned_curve_stations(
|
|
LINE,
|
|
options,
|
|
design_curves=[SHORT, LONG],
|
|
station_snap=snap,
|
|
station_snap_step=step,
|
|
)
|
|
assert planned == [s["chainage_m"] for s in made if s["kind"] == "curve"]
|