feat(B03·B04): 지형 라이다 여러 장 입력·병합 전처리
- 지형 파일 개수 제한 해제(정확히 1개 → 1장 이상), 한 카드에 여러 장 담기 (화면 표시 「용화_서편.las 외 1장」, 업로드는 한 장씩 차례로 전송) - 구조화 엔진이 여러 파일을 합친 범위로 한 벌 생성 — WF1 자동 전처리·B04 재분석 모두 프로젝트 지형 파일 전부를 대상으로 실행 - 점이 1억 개를 넘으면 씨닝 — 지면 분류점은 전부 남기고 나머지만 0.5m 칸 최저점으로 축소 (용화 실측: 4,900만점 → 249만점, 지면점 1,140,716개 그대로, 1m 지면격자 표고차 0.0000m) - 머리글만 읽어 5km 넘게 떨어진 파일은 업로드 거부 (임도는 길어도 2~3km) - 화면 조립부 700줄 준수를 위해 terrainCoverage 를 판정 모듈로 이동 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
from collections.abc import Callable
|
||||
from collections.abc import Callable, Sequence
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -37,14 +37,22 @@ GROUND_POINT_SAMPLE_LIMIT = 500_000
|
||||
GROUND_POINT_CACHE_VERSION = 2
|
||||
|
||||
|
||||
def _source_identity(las_path: Path) -> dict[str, Any]:
|
||||
"""입력 LAS의 정체성(이름·크기·수정시각)으로 캐시 세대를 식별한다 (PLAN B-2)."""
|
||||
stat = las_path.stat()
|
||||
return {
|
||||
"filename": las_path.name,
|
||||
"size_bytes": int(stat.st_size),
|
||||
"mtime": float(stat.st_mtime),
|
||||
}
|
||||
def _source_identity(las_paths: list[Path]) -> dict[str, Any]:
|
||||
"""입력 지형 파일들의 정체성(이름·크기·수정시각)으로 캐시 세대를 식별한다 (PLAN B-2).
|
||||
|
||||
여러 장을 병합하므로 **한 장이라도 바뀌거나 늘고 줄면** 다시 계산해야 한다
|
||||
(2026-09-06 다중 입력).
|
||||
"""
|
||||
files = [
|
||||
{
|
||||
"filename": path.name,
|
||||
"size_bytes": int(path.stat().st_size),
|
||||
"mtime": float(path.stat().st_mtime),
|
||||
}
|
||||
for path in sorted(las_paths, key=lambda item: item.name)
|
||||
]
|
||||
# 한 장일 때는 옛 형식과 같은 모양을 유지한다 — 이미 만든 캐시를 헛되이 버리지 않는다.
|
||||
return files[0] if len(files) == 1 else {"files": files}
|
||||
|
||||
|
||||
def _relative_to_project(project_root: Path, path: Path) -> str:
|
||||
@@ -108,7 +116,7 @@ def cache_ground_points(
|
||||
|
||||
def run_surface_analysis(
|
||||
project_root: Path,
|
||||
las_path: Path,
|
||||
las_path: Path | Sequence[Path],
|
||||
*,
|
||||
source_filters: list[str] | None,
|
||||
methods: list[str],
|
||||
@@ -117,6 +125,9 @@ def run_surface_analysis(
|
||||
) -> dict[str, Any]:
|
||||
"""구조화→필터→모델 빌드를 수행하고 산출 메타데이터를 반환한다.
|
||||
|
||||
`las_path`는 지형 파일 한 장 또는 여러 장이다 — 여러 장이면 합친 범위로 한 벌을
|
||||
만든다(2026-09-06 사용자 확정).
|
||||
|
||||
`source_filters`가 비면 입력 LAS를 보고 기본 필터를 정한다(자동 전처리 경로).
|
||||
|
||||
반환 dict:
|
||||
@@ -132,6 +143,9 @@ def run_surface_analysis(
|
||||
on_progress(percent, stage, message)
|
||||
|
||||
total_started = time.monotonic()
|
||||
las_paths = [las_path] if isinstance(las_path, Path) else [Path(item) for item in las_path]
|
||||
if not las_paths:
|
||||
raise ValueError("지형 파일이 없습니다.")
|
||||
stage_root = project_root / "B04_PreProcess"
|
||||
processed_dir = stage_root / "processed"
|
||||
models_dir = stage_root / "models"
|
||||
@@ -140,7 +154,7 @@ def run_surface_analysis(
|
||||
|
||||
# 0. 입력 세대 검증: LAS가 바뀌었으면 모든 캐시를 재계산한다 (PLAN B-2)
|
||||
identity_path = processed_dir / "source_identity.json"
|
||||
current_identity = _source_identity(las_path)
|
||||
current_identity = _source_identity(las_paths)
|
||||
stored_identity: dict[str, Any] | None = None
|
||||
if identity_path.is_file():
|
||||
try:
|
||||
@@ -154,10 +168,12 @@ def run_surface_analysis(
|
||||
if rebuild or not structured_path.is_file():
|
||||
_report(10, "structurize", "LAS 구조화 중")
|
||||
step_started = time.monotonic()
|
||||
structured_path = structurize_las(las_path, processed_dir)
|
||||
structured_path = structurize_las(las_paths, processed_dir)
|
||||
atomic_write_json(identity_path, current_identity)
|
||||
logger.info(
|
||||
"B04 LAS 구조화 완료: %s (%.1fs)", las_path.name, time.monotonic() - step_started
|
||||
"B04 LAS 구조화 완료: %s (%.1fs)",
|
||||
", ".join(path.name for path in las_paths),
|
||||
time.monotonic() - step_started,
|
||||
)
|
||||
else:
|
||||
_report(10, "structurize", "구조화 캐시 재사용")
|
||||
@@ -254,7 +270,7 @@ def run_surface_analysis(
|
||||
"z": [float(bounds[2, 0]), float(bounds[2, 1])],
|
||||
}
|
||||
download_geodata(
|
||||
project_root, processed_dir, las_bounds_dict, las_path.parent, rebuild, report=_report
|
||||
project_root, processed_dir, las_bounds_dict, las_paths[0].parent, rebuild, report=_report
|
||||
)
|
||||
|
||||
# 3-4. 도엽등고선 3D 서피스 — LAS가 있어도 참고용으로 같이 만들어 영구저장한다
|
||||
|
||||
Reference in New Issue
Block a user