Merge remote-tracking branches 'origin/sub_laptop_2' and 'origin/sub_laptop_7' into main_laptop_1

This commit is contained in:
2026-11-02 23:14:46 +09:00
10 changed files with 273 additions and 36 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ def _text(path: Path) -> str:
def test_721_1_단추_이름과_자리():
detail = _text(M02 / "M02_MasterTemplete_Sangeun_Detail.ts")
assert "children: [views.root, addButton, baseHint]" in detail
assert "children: [views.root, addButton]" in detail # 750-2 「시험 계산 = 바탕」 안내 걷음
assert "M02_SangeunPickBase" not in detail # 727-2 단추 걷음
locale = _text(ROOT / "ui_template" / "ui_template_locale_m2.ts")
assert "M02_SangeunPickBase" not in locale
@@ -30,3 +30,14 @@ def test_calc_view_answer_hook_skips_logic_lib():
ts = _ts("M01_MasterData/M01_MasterData_UI_Logic_Calc.ts")
assert "ctx.onText && !ctx.answer" in ts # 미리보기 안 부름
assert "if (ctx.answer)" in ts and "runCalc" in ts # M01 옛 길 그대로
def test_752_1_source_panel_only_on_sangeun_screen():
"""752-1 원문 패널 — 단가산출 화면만 켬 · 일위대가 줄 모달 안 상세는 끔 · 기초마다 탭 · 좁은 폭 접힘."""
side = _ts("M02_MasterTemplete/M02_MasterTemplete_Sangeun_Side.ts")
assert "/m02/logic-lib/source" in side and "createSidePanels" in side
assert "OPEN_FROM" in side and "createSegment" in side
assert "source: true" in _ts("M02_MasterTemplete/M02_MasterTemplete_Sangeun_View.ts")
assert "source" not in _ts("M02_MasterTemplete/M02_MasterTemplete_Labor_RowSk.ts")
detail = _ts("M02_MasterTemplete/M02_MasterTemplete_Sangeun_Detail.ts")
assert "opt.source ? createSangeunSide()" in detail and "side?.set(" in detail
@@ -0,0 +1,102 @@
"""754-1 등고선 · 격자 외곽 = 지금 지표면 외곽 (요구 589-11).
implicit 하나만 지어도 같은 필터 dtm 이 낡았으면 같이 다시 지음 · 등고선 캐시는 dtm · 굳힌 격자가
바뀌면 다시 셈 · ptd 처럼 dtm 없던 필터도 굳힌 격자가 생김. 서버 함수만 — 합성 점군 · 임시 폴더.
"""
import json
import os
from pathlib import Path
import numpy as np
from B04_PreProcess.B04_PreProcess_Engine_Contour import contour_cache_is_stale
from B04_PreProcess.B04_PreProcess_Engine_Pipeline import build_all_terrain_models
from common_util.common_util_surface_sampler import implicit_grid_npz
from config import config_system as cfg
def _banded_plane() -> tuple[dict, np.ndarray]:
axis = np.arange(0.0, 60.0, 0.5)
x, y = np.meshgrid(axis, axis)
points = np.column_stack([x.ravel(), y.ravel(), (100.0 + 0.2 * x).ravel()])
points = points[~((points[:, 0] > 28) & (points[:, 0] < 33))] # 5 m 빈 띠(z 106 = x 30)
data = {"xyz": points, "bounds": np.array([points.min(axis=0), points.max(axis=0)]).T}
return data, np.ones(len(points), bool)
def _config(filter_key: str, precompute: list[str], close_m: float) -> dict:
config = cfg.build_surface_model_config()
config.update(
{
"source_filters": [filter_key],
"precompute": precompute,
"footprint_gap_close_meters": close_m,
"implicit_min_points": 500,
"implicit_max_points": 2_000,
}
)
return config
def _valid_area(path: Path) -> float:
with np.load(path) as grid:
resolution = float(np.asarray(grid["resolution"]).ravel()[0])
return float(np.count_nonzero(grid["valid_mask"])) * resolution**2
def _band_contour(path: Path) -> bool:
lines = json.loads(path.read_text(encoding="utf-8"))["contours"]
return any(abs(line["level"] - 106.0) < 1e-6 for line in lines)
def test_implicit_만_지어도_낡은_dtm_같이_다시(tmp_path: Path) -> None:
data, mask = _banded_plane()
masks = {"csf": mask}
build_all_terrain_models(data, masks, tmp_path, _config("csf", ["dtm", "implicit"], 1.0))
narrow = _valid_area(tmp_path / "dtm_csf.npz")
contour = tmp_path / "contour_csf_implicit_1.0m.json"
assert not _band_contour(contour) # 1 m 닫기 = 띠 빈칸 · 106 m 줄 잘림
# 화면 [서피스 생성] = 고른 표현 하나만 — 외곽 설정(지문)이 바뀌었으니 dtm 도 다시
manifest = build_all_terrain_models(data, masks, tmp_path, _config("csf", ["implicit"], 3.0))
methods = manifest["source_filters"]["csf"]["methods"]
assert methods["dtm"]["status"] == "completed"
wide = _valid_area(tmp_path / "dtm_csf.npz")
grid_area = _valid_area(implicit_grid_npz(tmp_path, "csf"))
glb_area = methods["implicit"]["footprint_area_m2"]
assert wide > 1.05 * narrow
assert grid_area == wide # 격자 외곽 = dtm 외곽
assert abs(glb_area - wide) / wide < 0.05 # 3D 외곽 = 격자 외곽
assert _band_contour(contour) # 등고선이 넓힌 띠까지
assert not contour_cache_is_stale(contour, tmp_path / "implicit_csf.npz")
def test_dtm_이_바뀌면_등고선_캐시_낡음(tmp_path: Path) -> None:
model = tmp_path / "implicit_csf.npz"
dtm = tmp_path / "dtm_csf.npz"
grid = tmp_path / "implicit_csf_grid.npz"
contour = tmp_path / "contour_csf_implicit_1.0m.json"
assert contour_cache_is_stale(contour, model) # 없음
for index, path in enumerate((model, dtm, grid, contour)):
path.write_bytes(b"x")
os.utime(path, ns=(10**18 + index, 10**18 + index))
assert not contour_cache_is_stale(contour, model)
for newer in (dtm, grid):
os.utime(newer, ns=(2 * 10**18, 2 * 10**18))
assert contour_cache_is_stale(contour, model)
os.utime(newer, ns=(10**18, 10**18))
tin_smooth = tmp_path / "tin_csf_smooth.npz"
tin_smooth.write_bytes(b"x")
os.utime(tin_smooth, ns=(10**18, 10**18))
os.utime(dtm, ns=(2 * 10**18, 2 * 10**18))
assert contour_cache_is_stale(contour, tin_smooth) # 스무딩 면도 같은 필터 dtm 을 봄
def test_ptd_implicit_만_지어도_굳힌_격자(tmp_path: Path) -> None:
data, mask = _banded_plane()
build_all_terrain_models(data, {"ptd": mask}, tmp_path, _config("ptd", ["implicit"], 3.0))
assert (tmp_path / "dtm_ptd.npz").is_file()
grid = implicit_grid_npz(tmp_path, "ptd") # B05 · B06 표본이 읽는 파일
with np.load(grid) as loaded:
assert np.isfinite(loaded["z"][loaded["valid_mask"]]).all()