- 확정 기본 표현 implicit · 평활 False · WF1 자동 짓기에 확정 표현 더함(SURFACE_AUTO_METHODS 는 dtm 그대로) - 「분류 있음」 자동 고름 = 비율 0.2 % + 10 m 칸 덮임 50 % · 덮임 저장본(classification_coverage.json · 점군 안 풂) - 분류 LAS 의 implicit 는 기본 필터에만 지음(csf 두 번 안 지음) - B04 화면 확정 없을 때 기본 표현 implicit - 지킴 시험 test_640_lidar_default Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
82 lines
3.3 KiB
Python
82 lines
3.3 KiB
Python
"""640 라이다 기본 고름 · implicit 점 수 · 격자 칸 뽑기 · 지문 · 틈 닫기 (요구 558).
|
|
|
|
서버 함수만 — 작은 합성 점군 · 임시 폴더.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
|
|
import numpy as np
|
|
|
|
from B04_PreProcess.B04_PreProcess_Engine_Filter_Classification import (
|
|
classified_coverage,
|
|
has_classified_coverage,
|
|
)
|
|
from B04_PreProcess.B04_PreProcess_Engine_Ground import resolve_auto_source_filters
|
|
from B04_PreProcess.B04_PreProcess_Engine_Pipeline import build_all_terrain_models
|
|
from config import config_system as cfg
|
|
|
|
|
|
def _plane(size_m: float = 60.0, spacing_m: float = 0.5) -> np.ndarray:
|
|
axis = np.arange(0.0, size_m, spacing_m)
|
|
x, y = np.meshgrid(axis, axis)
|
|
z = 100.0 + 0.2 * x + 0.1 * np.sin(y / 5.0)
|
|
return np.column_stack([x.ravel(), y.ravel(), z.ravel()])
|
|
|
|
|
|
def _structured(points: np.ndarray, classification: np.ndarray | None = None) -> dict:
|
|
data = {"xyz": points, "bounds": np.array([points.min(axis=0), points.max(axis=0)]).T}
|
|
if classification is not None:
|
|
data["classification"] = classification
|
|
return data
|
|
|
|
|
|
def test_확정_기본값은_implicit_평활_끔() -> None:
|
|
assert cfg.SURFACE_CONFIRM_DEFAULT_METHOD == "implicit"
|
|
assert cfg.SURFACE_CONFIRM_DEFAULT_SMOOTH is False
|
|
assert cfg.SURFACE_AUTO_METHODS == ("dtm",) # D03 자체 라이다는 dtm 만 그대로
|
|
|
|
|
|
def test_일부만_분류된_LAS_는_자동_기본에서_csf() -> None:
|
|
points = _plane(100.0)
|
|
partial = np.ones(len(points), dtype=np.uint8)
|
|
partial[(points[:, 0] < 30) & (np.arange(len(points)) % 4 == 0)] = 2 # 비율 7 % · 덮임 30 %
|
|
assert classified_coverage(_structured(points, partial)) < 0.5
|
|
assert resolve_auto_source_filters(_structured(points, partial)) == ["csf"]
|
|
|
|
spread = np.ones(len(points), dtype=np.uint8)
|
|
spread[::37] = 2 # 비율 2.7 % · 덮임 100 %
|
|
assert classified_coverage(_structured(points, spread)) == 1.0
|
|
assert resolve_auto_source_filters(_structured(points, spread)) == ["classification", "csf"]
|
|
|
|
|
|
def test_덮임은_저장본을_다시_씀(tmp_path: Path) -> None:
|
|
points = _plane(40.0)
|
|
classes = np.full(len(points), 2, dtype=np.uint8)
|
|
(tmp_path / "structured.npz").write_bytes(b"x")
|
|
assert has_classified_coverage(_structured(points, classes), tmp_path)
|
|
assert (tmp_path / "classification_coverage.json").is_file()
|
|
|
|
class _NoXyz(dict):
|
|
def __getitem__(self, key): # type: ignore[no-untyped-def]
|
|
assert key != "xyz", "저장본이 맞으면 점군을 풀지 않아야 함"
|
|
return dict.__getitem__(self, key)
|
|
|
|
assert has_classified_coverage(_NoXyz(_structured(points, classes)), tmp_path)
|
|
|
|
|
|
def _config(**override: object) -> dict:
|
|
config = cfg.build_surface_model_config()
|
|
config.update({"source_filters": ["a", "b"], "precompute": ["dtm", "implicit"]})
|
|
config.update(override)
|
|
return config
|
|
|
|
|
|
def test_implicit_은_기본_필터에만(tmp_path: Path) -> None:
|
|
points = _plane()
|
|
data = _structured(points)
|
|
masks = {"a": np.ones(len(points), bool), "b": np.arange(len(points)) % 2 == 0}
|
|
manifest = build_all_terrain_models(data, masks, tmp_path, _config())
|
|
assert set(manifest["source_filters"]["a"]["methods"]) == {"dtm", "implicit"}
|
|
assert set(manifest["source_filters"]["b"]["methods"]) == {"dtm"}
|
|
assert not (tmp_path / "implicit_b.npz").exists()
|