"""격자 칸 = 좌표 간격(요구 538 · PLAN 633). - 적힌 칸(0.5) ≠ 좌표 간격(1.0) → 급경사 · 음영 · 0차곡이 좌표 간격으로 셈 - 둘이 맞는 격자(D 수치지형도) → 적힌 값 그대로 · 결과 전과 같음 ./venv/Scripts/python.exe -m pytest resources/tester/test_633_grid_cell.py -q """ import logging import numpy as np from common_util.common_util_grid_cell import grid_cell_m from D03_PlanAnalysis import D03_PlanAnalysis_Layers as layers from D03_PlanAnalysis import D03_PlanAnalysis_ZeroOrder as zero_order EPSG = 5186 X0, Y0 = 200000.0, 450000.0 def _npz(path, cell: float, written: float | None, n: int = 60, tilt: float = 0.5): x, y = X0 + np.arange(n) * cell, Y0 + np.arange(n) * cell z = np.add.outer(np.zeros(n), x - X0) * tilt # 동쪽으로 tilt(= 50 %) extra = {} if written is None else {"resolution": np.array([written], np.float32)} path.parent.mkdir(parents=True, exist_ok=True) np.savez(path, x=x, y=y, z=z, valid_mask=np.ones(z.shape, bool), **extra) return path def test_grid_cell_prefers_spacing(tmp_path, caplog) -> None: with np.load(_npz(tmp_path / "a.npz", 2.0, 2.0)) as dem: assert grid_cell_m(dem, dem["x"]) == 2.0 with np.load(_npz(tmp_path / "b.npz", 1.5, None)) as dem: assert grid_cell_m(dem, dem["x"]) == 1.5 with caplog.at_level(logging.WARNING), np.load(_npz(tmp_path / "c.npz", 1.078, 0.5)) as dem: assert abs(grid_cell_m(dem, dem["x"]) - 1.078) < 1e-9 assert "좌표 간격" in caplog.text def test_slope_uses_real_cell(tmp_path) -> None: npz = _npz(tmp_path / "models" / "dtm_csf_smooth.npz", 1.0, 0.5) value = layers.slope_layer(tmp_path, tmp_path / "models", EPSG, npz=npz) counts = np.asarray(value["counts"]) assert int(counts.argmax()) == 50 # 0.5 를 믿으면 100 assert value["box_m"][0] == X0 - 0.5 def test_sheet_slope_unchanged(tmp_path) -> None: """적힌 칸 = 좌표 간격 → 옛 셈(적힌 칸)과 같은 그림.""" npz = _npz(tmp_path / "models" / "dtm_sheet_x.npz", 2.0, 2.0) value = layers.slope_layer(tmp_path, tmp_path / "models", EPSG, npz=npz) with np.load(npz) as dem: old = layers.slope_values_png(dem["z"], dem["valid_mask"], 2.0) assert value["image"] == old["image"] and value["counts"] == old["counts"] def test_hillshade_uses_real_cell(tmp_path) -> None: npz = _npz(tmp_path / "models" / "dtm_csf_smooth.npz", 1.0, 0.5) value = layers.slope_layer(tmp_path, tmp_path / "models", EPSG, kind="hillshade", npz=npz) with np.load(npz) as dem: real = layers.hillshade_layer_png(dem["z"], dem["valid_mask"], 1.0) assert value["image"] == real["image"] def test_zero_order_dem_uses_real_cell(tmp_path, monkeypatch) -> None: npz = _npz(tmp_path / "models" / "dtm_csf_smooth.npz", 1.0, 0.5, n=40) seen = {} def spy(z, valid, x, y, cell, *rest): seen.update(shape=z.shape, dx=float(x[1] - x[0]), cell=cell) return [] monkeypatch.setattr(zero_order, "zero_order_lines", spy) value = zero_order.dem_zero_order_layer(tmp_path, npz, EPSG) # 0.5 를 믿으면 4 칸 건너(10×10 · 실제 4 m 를 2 m 로 셈) assert seen == {"shape": (20, 20), "dx": 2.0, "cell": 2.0} and value["cell_m"] == 2.0 assert zero_order._project_box(npz)[0] == X0 - 0.5 def test_b07_frame_hillshade_uses_real_cell(tmp_path) -> None: """B07 도곽 음영 — 옛 평활 파일(0.5 적힘)도 좌표 간격으로.""" from types import SimpleNamespace from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Lidar import hillshade_png from B07_DesignDetail.B07_DesignDetail_Router_Support_Basin import _hillshade_for_box npz = _npz(tmp_path / "B04_PreProcess" / "models" / "dtm_csf_smooth.npz", 1.0, 0.5) context = SimpleNamespace(project_root=tmp_path, surface_params={}) url, _ = _hillshade_for_box(context, (X0, Y0, X0 + 100, Y0 + 100)) with np.load(npz) as dem: assert url == hillshade_png(dem["z"], dem["valid_mask"], 1.0)[0] def test_smooth_writes_real_cell(tmp_path) -> None: """B04 평활 격자 — 칸을 넓히면(꼭짓점 상한) 넓힌 칸을 적음.""" from types import SimpleNamespace from B04_PreProcess.B04_PreProcess_Engine_Smooth import smooth_dtm src = _npz(tmp_path / "dtm_csf.npz", 1.0, 1.0, n=20) wide = X0 + np.arange(10) * 2.0 # 요청 0.5 → 상한 때문에 2.0 context = SimpleNamespace( config={"dtm_grid_resolution_meters": 1.0}, preview_grid=lambda res: (wide, Y0 + np.arange(10) * 2.0, None), contains_xy=lambda xs, ys: np.ones(np.shape(xs), bool), bounds=np.array([[X0, X0 + 18.0], [Y0, Y0 + 18.0], [0.0, 10.0]]), ) smooth_dtm(context, tmp_path, "dtm_csf", src) with np.load(tmp_path / "dtm_csf_smooth.npz") as dem: assert float(dem["resolution"][0]) == 2.0