- 필터 = 업체 분류 · 점진 TIN · 둘의 다듬기 판만 · 분석 허용 목록 = Ground 등록부(available_filters) 한 벌 - 표현 = DTM · implicit 만 · 평활 = DTM 만 · config 기본 목록 · PRECOMPUTE · 설정 사전 맞춤(설정 지문 바뀜 — 768 에서 다시 지음) - 등고선 = 격자 · 굳힌 격자만(삼각망 굽기 · 외곽 맞춤 · implicit RBF 폴백 걷음) · 표본기 · 경로 찾기 tin · nurbs · meshfree 갈래 걷음 - 수치지형도 = 라플라스만(TIN 둘 · TPS · ANUDEM형 · 다중해상도 걷음) · B04 단추 하나 - 안 부르는 길 — B04 JSON 점군 GET · ground-stats GET + TS · B07 옛 계획평면도(라이다) 짓기 · lidar_source · LIDAR_ID · plan_lidar 종류 이름 · SOURCE_FILTER_LABELS · MODEL_METHODS(py) · MESH_* 설정 - 옛 manifest 의 지운 갈래 줄은 DB 로 되살리지 않음 · 옛 csf 행은 미리보기 · 등고선 그대로 열림 · 지운 이름 요청 = 400 - 낡은 글 — Ground · Pipeline 머리 · WF1 「없으면 csf」 · B07 음영 기본 「csf」 → 빈 값 · 등고선 폴백 「csf」 → 404 · B04 3D 보기 처음값 · B05 Schema · B06 Prebuild · 씨닝 설명 - 시험 지움 2 — test_b04_contour_raster · test_b04_contour_equivalence(지운 삼각망 굽기 · 없는 프로젝트를 봐 건너뛰던 것) - 시험 덜어냄 — test_b04_ground_filters CSF · 격자 최저 네 경우(지운 필터) - 시험 고침 — 허용 목록 · 형 검사에 걸린 csf · tin · grid_min_z · meshfree · tin_sheet 견본을 ptd · implicit · laplace 로(54_22 · 238_1 · 443 · 342_2 · 346_3 · 351_1 · 442_2 · 271 + helper · 344_1 · 133_1 · 343_2 · 637 · 754_1 · 763_1 깨진 tin → 깨진 implicit · 763_3 · b05 둘 · b06 · sheet_surface TIN(도엽선) 경우 지움) · 이름만 바꿈(273_2 · 343_1 · 343_3 · 624_1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
103 lines
4.5 KiB
Python
103 lines
4.5 KiB
Python
"""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 = {"ptd": mask}
|
|
build_all_terrain_models(data, masks, tmp_path, _config("ptd", ["dtm", "implicit"], 1.0))
|
|
narrow = _valid_area(tmp_path / "dtm_ptd.npz")
|
|
contour = tmp_path / "contour_ptd_implicit_1.0m.json"
|
|
assert not _band_contour(contour) # 1 m 닫기 = 띠 빈칸 · 106 m 줄 잘림
|
|
|
|
# 화면 [서피스 생성] = 고른 표현 하나만 — 외곽 설정(지문)이 바뀌었으니 dtm 도 다시
|
|
manifest = build_all_terrain_models(data, masks, tmp_path, _config("ptd", ["implicit"], 3.0))
|
|
methods = manifest["source_filters"]["ptd"]["methods"]
|
|
assert methods["dtm"]["status"] == "completed"
|
|
wide = _valid_area(tmp_path / "dtm_ptd.npz")
|
|
grid_area = _valid_area(implicit_grid_npz(tmp_path, "ptd"))
|
|
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_ptd.npz")
|
|
|
|
|
|
def test_dtm_이_바뀌면_등고선_캐시_낡음(tmp_path: Path) -> None:
|
|
model = tmp_path / "implicit_ptd.npz"
|
|
dtm = tmp_path / "dtm_ptd.npz"
|
|
grid = tmp_path / "implicit_ptd_grid.npz"
|
|
contour = tmp_path / "contour_ptd_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_ptd_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()
|