- 노선 따라감으로 0.14 m 어긋난 세월교가 새 관(관경 기본값)으로 바뀌던 흠 · 화면 SAME_PIPE_M 과 같은 값 - 검증_sub5 실측(지금 노선 메모리 격자) — 관 11 · 이어받음 5(세월교 352 m 포함) · 최대 간격 99 m Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VmC34fsxxbweEG3RUjs3Mn
135 lines
6.2 KiB
Python
135 lines
6.2 KiB
Python
"""369 배수 구조물 자리 — 기본 = 세류선 교차만 · [자동 제안] 두 방식(all · add).
|
|
|
|
- 기본(auto 없음) = 세류선 교차 관만 · 100 m 보충 없음 · 관 자리 후보는 셈.
|
|
- all = 세류선 + 100 m 보충으로 다시(지금 목록 무시) · 같은 자리(0.05 m) 관은 속성 · id 유지 ·
|
|
나머지 = 새 관(등록부 기본값).
|
|
- add = 지금 목록 그대로 + 빈 구간 보충 · 더한 관 = 새 관(등록부 기본값).
|
|
- 틀린 auto = 400 · 저장본 열쇠에 auto.
|
|
"""
|
|
|
|
import asyncio
|
|
from pathlib import Path
|
|
from uuid import uuid4
|
|
|
|
import numpy as np
|
|
|
|
from B04_PreProcess import B04_PreProcess_Router_Basins as basins
|
|
from B04_PreProcess.B04_PreProcess_Engine_Watershed_Grid import GridSpec
|
|
from common_util.common_util_drainage_context import DrainageContext
|
|
from common_util.common_util_drainage_detail import RoadRouting, build_detail
|
|
from common_util.common_util_drainage_pipes import PipePoint
|
|
from common_util.common_util_route_geometry import RouteVertex, StructureCandidate
|
|
|
|
VERTICES = [RouteVertex(0.0, 0.0, 100.0, 0.0), RouteVertex(300.0, 0.0, 90.0, 300.0)]
|
|
CONTEXT = DrainageContext(stored_path="x", project_root=Path("."), vertices=VERTICES)
|
|
|
|
|
|
def _routing() -> RoadRouting:
|
|
spec = GridSpec(x_min=-5.0, y_max=5.0, cell_m=1.0, n_rows=10, n_cols=310)
|
|
road_index = np.arange(5 * 310 + 5, 5 * 310 + 305, dtype=np.int32)
|
|
road_slot = np.full(spec.size, -1, dtype=np.int32)
|
|
road_slot[road_index] = np.arange(road_index.size)
|
|
return RoadRouting(
|
|
spec=spec,
|
|
road_slot=road_slot,
|
|
path_length=np.zeros(spec.size, np.float32),
|
|
elevation=np.full(spec.size, 95.0, np.float32),
|
|
road_cell_index=road_index,
|
|
road_chainage=np.arange(road_index.size, dtype=np.float64),
|
|
strength=np.ones(road_index.size, np.int64),
|
|
base_pipes=[StructureCandidate(chainage_m=150.0, x=150.0, y=0.0, reason="stream")],
|
|
)
|
|
|
|
|
|
def _detail(tmp_path, chainages=None, auto=None):
|
|
return build_detail(tmp_path / "없음", VERTICES, chainages, _routing(), [], auto)
|
|
|
|
|
|
def test_기본은_세류선_교차만(tmp_path):
|
|
detail = _detail(tmp_path)
|
|
assert [(p.chainage_m, p.reason) for p in detail.pipes] == [(150.0, "stream")]
|
|
assert len(detail.pipe_candidates) == 2 # 0~150 · 150~300 이 100 m 넘음 — 제안만
|
|
|
|
|
|
def test_all_은_세류선_더하기_100m_보충_지금_목록_무시(tmp_path):
|
|
detail = _detail(tmp_path, [10.0, 150.02], "all")
|
|
reasons = [p.reason for p in detail.pipes]
|
|
places = [p.chainage_m for p in detail.pipes]
|
|
assert reasons.count("stream") == 1 and reasons.count("spacing") == 2
|
|
assert 150.02 in places and 10.0 not in places # 세류선 150 m 0.5 m 안의 지금 관 = 그 자리
|
|
gaps = np.diff([0.0, *places, 300.0])
|
|
assert gaps.max() <= 100.0 and not detail.pipe_candidates
|
|
|
|
|
|
def test_add_는_지금_목록_더하기_빈_구간_보충(tmp_path):
|
|
detail = _detail(tmp_path, [20.0], "add")
|
|
places = [p.chainage_m for p in detail.pipes]
|
|
assert 20.0 in places and len(places) == 3 # 20 ~ 300 = 280 m → 보충 둘
|
|
assert np.diff([0.0, *places, 300.0]).max() <= 100.0
|
|
# auto 없음 = 목록 그대로(보충은 후보만)
|
|
plain = _detail(tmp_path, [20.0])
|
|
assert [p.chainage_m for p in plain.pipes] == [20.0] and len(plain.pipe_candidates) == 2
|
|
|
|
|
|
def _build(tmp_path, monkeypatch, points, auto):
|
|
monkeypatch.setattr(basins, "drainage_dir", lambda stored: tmp_path / "없음")
|
|
return basins._build("x", CONTEXT, points, _routing(), [], auto=auto)
|
|
|
|
|
|
def test_all_같은_자리_관은_속성_id_유지_새_관은_기본값(tmp_path, monkeypatch):
|
|
kept = PipePoint(
|
|
chainage_m=150.3, # 세류선 150 m 에서 0.3 m — 노선 따라감으로 어긋난 같은 관
|
|
source="user",
|
|
facility="ford_bridge",
|
|
options={"ford_width_m": 4.0},
|
|
id="keep",
|
|
)
|
|
gone = PipePoint(chainage_m=10.0, source="user", options={"pipe_diameter_mm": 600}, id="gone")
|
|
_, carried = _build(tmp_path, monkeypatch, [gone, kept], "all")
|
|
ford = [p for p in carried if p.id == "keep"]
|
|
assert len(ford) == 1 and ford[0].facility == "ford_bridge" and ford[0].source == "user"
|
|
assert ford[0].chainage_m == 150.3 # 그 자리 그대로
|
|
assert all(p.id != "gone" for p in carried)
|
|
fresh = [p for p in carried if p.id != "keep"]
|
|
assert len(fresh) == 2 and all(p.source == "spacing" for p in fresh)
|
|
assert all((p.options or {}).get("pipe_diameter_mm") for p in fresh) # 등록부 관경
|
|
|
|
|
|
def test_add_더한_관은_기본값_지금_관은_그대로(tmp_path, monkeypatch):
|
|
mine = PipePoint(chainage_m=20.0, source="user", id="mine")
|
|
_, carried = _build(tmp_path, monkeypatch, [mine], "add")
|
|
assert [p.id for p in carried].count("mine") == 1
|
|
assert next(p for p in carried if p.id == "mine").options is None # 옛 빈칸 = 미확정 유지
|
|
added = [p for p in carried if p.id != "mine"]
|
|
assert len(added) == 2 and all(p.source == "spacing" for p in added)
|
|
assert all((p.options or {}).get("pipe_diameter_mm") for p in added)
|
|
|
|
|
|
def test_틀린_auto_는_400_열쇠에_auto(tmp_path, monkeypatch):
|
|
async def load(project_id):
|
|
return CONTEXT, ""
|
|
|
|
monkeypatch.setattr(basins, "load_drainage_context", load)
|
|
monkeypatch.setattr(basins, "drainage_dir", lambda stored: tmp_path)
|
|
result = asyncio.run(basins._resolve(uuid4(), {"auto": "spacing"}, use_stored=False))
|
|
assert result.status_code == 400
|
|
points = [PipePoint(chainage_m=20.0)]
|
|
keys = {basins._detail_key(CONTEXT, points, auto) for auto in basins.AUTO_MODES}
|
|
assert len(keys) == 3
|
|
|
|
|
|
def test_낡은_분석에_auto_는_409(monkeypatch):
|
|
async def load(project_id):
|
|
return CONTEXT, ""
|
|
|
|
monkeypatch.setattr(basins, "load_drainage_context", load)
|
|
monkeypatch.setattr(basins, "analysis_route_signature", lambda stored: "옛 노선")
|
|
points = [{"chainage_m": 20.0}]
|
|
for auto in ("all", "add"):
|
|
result = asyncio.run(
|
|
basins._resolve(uuid4(), {"points": points, "auto": auto}, use_stored=False)
|
|
)
|
|
assert result.status_code == 409 # 말없이 그대로 돌려주지 않음 — 낡음 까닭
|
|
kept = asyncio.run(basins._resolve(uuid4(), {"points": points}, use_stored=False))
|
|
assert kept[-1] is True # auto 없음 = 관 자리만 낡음으로(그대로)
|