⚠ **뿌리** — `tmp/` 는 창 사이에 안 건너감(실측 확인: 상대 창이 놓은 `tmp/_sync_probe.txt` 가 시간을 두고 두 번 봐도 안 보임). 그래서 **정본(등록부 스키마)만 건너가고 그것을 읽는 시험은 안 건너가** 오늘 두 번, 같은 시험이 **연 창은 통과·받은 창은 실패**가 됐음. - `tmp/tests/*` 를 `resources/tester/` 로 **복사**(127 파일). 내용은 **한 줄도 안 고침** - `tmp/tests` 는 **남겨 둠** — 되돌릴 자리(사용자 지시) - 실행: `./venv/Scripts/python.exe -m pytest resources/tester/ -q` 옮기기 전과 **같은 수**: 617 통과 / 22 건너뜀 / 실패 0 ⇒ 이제 시험·예외·까닭이 **정본과 함께** 움직임. 오늘 세운 「예외는 정본 스키마에」와 짝임. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
93 lines
4.1 KiB
Python
93 lines
4.1 KiB
Python
"""관 지점이 어느 선으로 읽든 그 선 위에 앉는지.
|
|
|
|
B04가 관 자리를 정한 선(계획노선 CSV)과 B05 계획선은 같은 자리를 지나면서 연장이 다르다.
|
|
누가거리만 저장하면 읽는 쪽 선에 따라 관이 3~4m 미끄러진다(2026-08-30 사용자 지적).
|
|
"""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import numpy as np
|
|
from shapely.geometry import LineString, Point
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
|
|
from common_util.common_util_drainage_pipes import ( # noqa: E402
|
|
PIPE_SOURCE_STREAM,
|
|
PipePoint,
|
|
load_pipe_points_file,
|
|
route_signature,
|
|
save_pipe_points,
|
|
)
|
|
from common_util.common_util_route_geometry import RouteVertex # noqa: E402
|
|
|
|
|
|
def _vertices(points) -> list[RouteVertex]:
|
|
vertices, cumulative, previous = [], 0.0, None
|
|
for x, y in points:
|
|
if previous is not None:
|
|
cumulative += float(np.hypot(x - previous[0], y - previous[1]))
|
|
vertices.append(RouteVertex(x=float(x), y=float(y), z=0.0, chainage_m=cumulative))
|
|
previous = (x, y)
|
|
return vertices
|
|
|
|
|
|
def test_pipe_point_projects_onto_another_route(tmp_path, monkeypatch):
|
|
# 같은 자리를 지나지만 정점을 더 촘촘히 깔아 연장이 조금 긴 선 — 실제 B05 계획선과 같은 관계
|
|
coarse = _vertices([(0.0, 0.0), (100.0, 0.0), (200.0, 40.0)])
|
|
# 경로 탐색이 만든 선 — 같은 자리를 지나지만 잔물결이 있어 연장이 1%쯤 길다
|
|
reference = LineString([(0.0, 0.0), (100.0, 0.0), (200.0, 40.0)])
|
|
fine_xy = [(0.0, 0.0)]
|
|
for step in range(1, 40):
|
|
along = reference.interpolate(reference.length * step / 40.0)
|
|
fine_xy.append((along.x, along.y + (0.7 if step % 2 else -0.7)))
|
|
fine_xy.append((200.0, 40.0))
|
|
fine = _vertices(fine_xy)
|
|
coarse_line = LineString([(v.x, v.y) for v in coarse])
|
|
fine_line = LineString([(v.x, v.y) for v in fine])
|
|
assert abs(fine_line.length - coarse_line.length) > 0.3 # 연장이 실제로 다르다
|
|
|
|
import common_util.common_util_drainage_pipes as pipes_module
|
|
|
|
monkeypatch.setattr(pipes_module, "pipe_points_path", lambda _p: tmp_path / "pipe_points.json")
|
|
|
|
pipe = PipePoint(chainage_m=130.0, source=PIPE_SOURCE_STREAM, start_m=125.0, end_m=135.0)
|
|
save_pipe_points("storage/x", route_signature(coarse), [pipe], coarse)
|
|
|
|
stored = (tmp_path / "pipe_points.json").read_text(encoding="utf-8")
|
|
assert '"x"' in stored and '"y"' in stored # 좌표가 실제로 남는다
|
|
|
|
# 다른 선으로 읽는다 — 지문이 다르지만 버리지 않고 투영해 온다
|
|
loaded = load_pipe_points_file(tmp_path / "pipe_points.json", route_signature(fine), fine)
|
|
assert loaded is not None and len(loaded) == 1
|
|
moved = loaded[0]
|
|
|
|
# ① 원래 자리를 유지한다 (좌표가 정본, 저장 정밀도 mm)
|
|
original = coarse_line.interpolate(130.0)
|
|
assert Point(moved.x, moved.y).distance(original) < 1e-3
|
|
# ② 새 선 위에서 그 자리에 **가장 가까운 점**에 앉는다 (더 가까운 자리는 없다)
|
|
on_fine = fine_line.interpolate(moved.chainage_m)
|
|
assert abs(on_fine.distance(original) - original.distance(fine_line)) < 1e-3
|
|
# ③ 누가거리는 선이 달라진 만큼 옮겨간다
|
|
assert abs(moved.chainage_m - 130.0) > 0.1
|
|
# ④ 구간 길이(시설 치수)는 그대로, 기준점과 함께 밀린다
|
|
assert abs((moved.end_m - moved.start_m) - 10.0) < 1e-6
|
|
assert abs((moved.chainage_m - moved.start_m) - 5.0) < 1e-6
|
|
|
|
|
|
def test_old_file_without_coordinates_is_discarded(tmp_path, monkeypatch):
|
|
"""좌표 없는 구 저장분은 이월하지 않는다 — 자리를 되짚을 근거가 없다."""
|
|
import json
|
|
|
|
import common_util.common_util_drainage_pipes as pipes_module
|
|
|
|
path = tmp_path / "pipe_points.json"
|
|
path.write_text(
|
|
json.dumps({"route_signature": "old", "points": [{"chainage_m": 10.0}]}),
|
|
encoding="utf-8",
|
|
)
|
|
monkeypatch.setattr(pipes_module, "pipe_points_path", lambda _p: path)
|
|
fine = _vertices([(0.0, 0.0), (100.0, 0.0)])
|
|
assert load_pipe_points_file(path, route_signature(fine), fine) is None
|