"""342-4 지반고 값 갈림 맞춤 — 노선 꼭짓점 z · 종단 미리보기가 공용 1 m 표본기와 같은 값. ./venv/Scripts/python.exe -m pytest resources/tester/test_342_4_ground_unify.py -q """ from pathlib import Path import numpy as np import pytest from B05_Profile.B05_Profile_Engine_AsPlanned import solve_as_planned from B05_Profile.B05_Profile_Engine_RouteInitial_Profile import route_sections from common_util.common_util_surface_sampler import DtmGridSampler, build_surface_sampler def _grid(tmp_path: Path) -> Path: """1 m 남짓 고르지 않은 축 · 기울어진 면 · 가운데 빈칸 한 덩이.""" x = np.cumsum(np.r_[0.0, np.full(120, 1.0) + 0.03 * np.sin(np.arange(120))]) y = np.cumsum(np.r_[0.0, np.full(80, 1.0) + 0.02 * np.cos(np.arange(80))]) xx, yy = np.meshgrid(x, y) z = 100.0 + 0.1 * xx + 0.05 * yy + np.sin(xx / 7.0) valid = np.ones(z.shape, dtype=bool) valid[38:42, 58:62] = False models = tmp_path / "B04_PreProcess" / "models" models.mkdir(parents=True) np.savez(models / "dtm_classification.npz", x=x, y=y, z=z, valid_mask=valid) return models def test_꼭짓점_z_는_공용_표본기_값(tmp_path: Path) -> None: models = _grid(tmp_path) vertices = [(5.3, 10.7), (40.2, 30.9), (60.0, 40.0), (100.4, 60.2)] # 셋째 = 빈칸 result = solve_as_planned(tmp_path, "classification", False, vertices, {}) z = [p[2] for p in result["polyline"]] expect, valid = build_surface_sampler(models, "classification", "dtm", False).sample_xy( np.asarray(vertices) ) assert valid.tolist() == [True, True, False, True] assert np.allclose([z[0], z[1], z[3]], expect[[0, 1, 3]], atol=1e-9) # 빈칸은 0.0 이 아니라 이웃 정점 사이 누가거리 보간 chain = np.concatenate([[0.0], np.cumsum(np.hypot(*np.diff(vertices, axis=0).T))]) assert z[2] == pytest.approx(np.interp(chain[2], chain[[1, 3]], expect[[1, 3]])) assert min(z) > 50.0 def test_꼭짓점_모두_밖이면_멈춤(tmp_path: Path) -> None: _grid(tmp_path) with pytest.raises(ValueError): solve_as_planned(tmp_path, "classification", False, [(-50.0, -50.0), (-20.0, -40.0)], {}) def test_종단_미리보기_지반은_1m_쌍선형(tmp_path: Path) -> None: models = _grid(tmp_path) sampler = DtmGridSampler.from_npz(models / "dtm_classification.npz") vertices = [(10.0, 40.0), (110.0, 40.0)] # 가운데 빈칸을 지남 at, ground, sections, _modes, _curves = route_sections(vertices, sampler) for chain, row in zip(at, sections): cx = 10.0 + chain offsets = np.array([s["offset_m"] for s in row]) z, valid = sampler.sample_xy(np.column_stack([np.full(offsets.size, cx), 40.0 + offsets])) assert [s["valid"] for s in row] == valid.tolist() got = np.array([np.nan if s["elevation_m"] is None else s["elevation_m"] for s in row]) assert np.allclose(got[valid], z[valid], atol=1e-9) assert all(s["elevation_m"] is None for s, ok in zip(row, valid) if not ok) # 중심 빈칸 측점의 지반고는 이웃 측점 보간 — 0.0 · 평균 표고로 안 메움 assert np.isfinite(ground).all() and ground.min() > 50.0