"""192-1 — 종단 계획선 숨은 꺾임(검증_랩탑메인 226fc805 · No.38+9). 까닭: 관을 769.28 → 769.38(0.1m) 옮김 → 「측점 있음」 판정 폭이 0.5m 라 측점이 옛 자리 769.28 에 남음 → B06(측점 769.28)과 B05(관 769.38) 두 자리에 계획고 편집이 남아 0.1m 사이 −465% 계단 · 769.38 은 측점선 없는 꼭짓점. 고침: 판정 폭 0.005m(측점 스냅 폭) · 측점을 다시 뜨면 사라진 측점의 꼭짓점 · 편집을 지움 (반올림 차이 0.006m 안은 옮김). 자료 = 고정 사본(fixtures/b05_192_1_moved_pipe_longitudinal.json · 226fc805 종단 460~840m · 측점 769.28 인 192 이전 상태 · 213-2) — 사용자 storage 에 기대지 않음. """ from __future__ import annotations import copy import json import shutil import sys from pathlib import Path import pytest PROJECT_ROOT = Path(__file__).resolve().parents[2] if str(PROJECT_ROOT) not in sys.path: sys.path.insert(0, str(PROJECT_ROOT)) import B05_Profile.B05_Profile_Engine_Sections as sections # noqa: E402 from B05_Profile.B05_Profile_Engine_Grade_Profile import rebuild_alignment_profile # noqa: E402 from B05_Profile.B05_Profile_Router_Confirm import _merge_irregular_into_longitudinal # noqa: E402 from B06_Section.B06_Section_Router_Stations import _missing_marks # noqa: E402 SOURCE = PROJECT_ROOT / "resources/tester/fixtures/b05_192_1_moved_pipe_longitudinal.json" REL = "B06_Section/longitudinal/longitudinal.json" def _copy(tmp_path: Path) -> Path: target = tmp_path / REL target.parent.mkdir(parents=True) shutil.copyfile(SOURCE, target) return target def _load(path: Path) -> dict: return json.loads(path.read_text(encoding="utf-8")) def _irregular(data: dict, old: float, new: float) -> list[dict]: """다시 뜬 비정규 측점 목록 흉내 — `old` 측점만 `new` 로 옮김.""" result = [] for station in data["stations"]: if station.get("kind") != "irregular": continue item = copy.deepcopy(station) if abs(item["chainage_m"] - old) < 1e-6: item["chainage_m"] = new result.append(item) return result def test_원본_자료가_숨은_꼭짓점을_가짐() -> None: """까닭 근거 — 769.38 꼭짓점은 측점 목록에 없고 두 편집 사이 −465%.""" data = _load(SOURCE) alignment, _ = rebuild_alignment_profile(data, data["profile_alignment"]["edits"]) stations = [s["chainage_m"] for s in data["stations"]] pvis = [p["chainage_m"] for p in alignment["pvi"]] assert 769.38 in pvis and 769.38 not in stations and 769.28 in stations assert {"769.280", "769.380"} <= set(alignment["edits"]["station_offsets"]) step = next(s for s in alignment["segments"] if s["from_m"] == 769.28) assert step["to_m"] == 769.38 and step["grade_percent"] < -400 def test_관_옮긴_측점_다시_뜨면_숨은_꼭짓점_사라짐(tmp_path: Path) -> None: path = _copy(tmp_path) data = _load(path) summary = _merge_irregular_into_longitudinal(tmp_path, REL, _irregular(data, 769.28, 769.38)) after = _load(path) alignment = after["profile_alignment"] stations = {round(s["chainage_m"], 3) for s in after["stations"]} near = [p for p in alignment["pvi"] if 760 < p["chainage_m"] < 780] assert [p["chainage_m"] for p in near] == [769.38] # 꼭짓점 하나 · 측점선 있음 assert 769.38 in stations and 769.28 not in stations assert alignment["edits"]["station_offsets"] == {"769.380": pytest.approx(0.051428)} assert all(abs(b["chainage_m"] - 769.28) > 1e-6 for b in alignment["base_pvi"]) grades = [s["grade_percent"] for s in alignment["segments"] if 760 <= s["from_m"] < 780] assert max(abs(g) for g in grades) < 20 assert summary is not None and summary["max_grade_pct"] < 20 assert after["design_profiles"][0]["summary"]["max_grade_pct"] == summary["max_grade_pct"] def test_반올림_차이만_다르면_편집을_옮김(tmp_path: Path) -> None: path = _copy(tmp_path) data = _load(path) data["profile_alignment"]["edits"]["station_offsets"]["484.210"] = 0.3 path.write_text(json.dumps(data), encoding="utf-8") _merge_irregular_into_longitudinal(tmp_path, REL, _irregular(data, 484.21, 484.213)) offsets = _load(path)["profile_alignment"]["edits"]["station_offsets"] assert "484.210" not in offsets and offsets["484.213"] == pytest.approx(0.3) def test_측점이_그대로면_계획선을_건드리지_않음(tmp_path: Path) -> None: path = _copy(tmp_path) data = _load(path) before = data["profile_alignment"] assert _merge_irregular_into_longitudinal(tmp_path, REL, _irregular(data, -1, -1)) is None assert _load(path)["profile_alignment"] == before def test_조금_옮긴_관도_측점_없음으로_셈(tmp_path: Path, monkeypatch) -> None: """0.1m 옮긴 관 — 옛 판정(0.5m)은 「측점 있음」으로 놓쳤음.""" target = tmp_path / REL target.parent.mkdir(parents=True) target.write_text( json.dumps({"stations": [{"chainage_m": 760.0}, {"chainage_m": 769.28}]}), encoding="utf-8", ) monkeypatch.setattr(sections, "_load_route_polyline", lambda *_: []) monkeypatch.setattr(sections, "_load_pipe_points", lambda *_: []) monkeypatch.setattr( sections, "resolve_extra_stations", lambda *_: [(769.38, "파형강관 D800"), (760.0, "옹벽")] ) assert _missing_marks(tmp_path, "route.csv") == [ {"chainage_m": 769.38, "label": "파형강관 D800"} ]