feat(561-2): D 예정노선 지형 격자 2 m · B 공용 1 m 그대로 (grid_m 인자)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 21:05:42 +09:00
co-authored by Claude Sonnet 5.5
parent 2ba29afb79
commit 0fc0f0aae2
4 changed files with 42 additions and 17 deletions
@@ -344,6 +344,7 @@ def _write_method_model(
spec: Any,
surface: np.ndarray,
method_key: str,
grid_m: float = SHEET_SURFACE_GRID_M,
) -> dict[str, Any] | None:
"""방식 하나의 표고 격자를 npz·프리뷰 glb로 저장하고 등록용 dict를 만든다."""
# DtmGridSampler 규약에 맞춰 y 오름차순으로 뒤집어 저장한다.
@@ -379,15 +380,20 @@ def _write_method_model(
valid_mask=valid_grid,
bounds=bounds,
scene_bounds=scene,
resolution=np.array([SHEET_SURFACE_GRID_M], np.float32),
resolution=np.array([grid_m], np.float32),
)
vertices, faces = _preview_mesh(x_coords, y_coords, z_grid, valid_grid)
write_glb(preview_path, vertices, faces, scene)
_write_smoothed(models_dir, stem, x_coords, y_coords, z_grid, valid_grid, bounds, scene)
return _method_model_entry(project_root, models_dir, method_key)
return _method_model_entry(project_root, models_dir, method_key, grid_m)
def _method_model_entry(project_root: Path, models_dir: Path, method_key: str) -> dict[str, Any]:
def _method_model_entry(
project_root: Path,
models_dir: Path,
method_key: str,
grid_m: float = SHEET_SURFACE_GRID_M,
) -> dict[str, Any]:
"""방식 하나의 등록용 dict — 저장본 그대로 쓸 때도 같은 모양."""
source_filter = f"{SHEET_SOURCE_FILTER}_{method_key}"
stem = f"dtm_{source_filter}"
@@ -398,7 +404,7 @@ def _method_model_entry(project_root: Path, models_dir: Path, method_key: str) -
"source_filter": source_filter,
"representation": "regular_grid",
"model_file_path": str(model_path.relative_to(project_root)).replace("\\", "/"),
"resolution_m": SHEET_SURFACE_GRID_M,
"resolution_m": grid_m,
"generation_params": {
"source_filter": source_filter,
"representation": "regular_grid",
@@ -420,7 +426,12 @@ def _method_model_entry(project_root: Path, models_dir: Path, method_key: str) -
def _sheet_key(
project_root: Path, processed_dir: Path, route_xy: np.ndarray, crs: str, method_key: str
project_root: Path,
processed_dir: Path,
route_xy: np.ndarray,
crs: str,
method_key: str,
grid_m: float = SHEET_SURFACE_GRID_M,
) -> dict[str, Any] | None:
"""도엽 3D 면 열쇠 — 등고선 파일 · 노선 · 좌표계 · 격자 · 여유 · 스무딩 · 화면 상자 · 판."""
contour = processed_dir / _CONTOUR_FILE
@@ -435,7 +446,7 @@ def _sheet_key(
"contour": f"{contour.name}:{stat.st_mtime_ns}:{stat.st_size}",
"route": hashlib.sha1(np.round(route_xy, 3).tobytes()).hexdigest(),
"crs": str(crs),
"grid_m": SHEET_SURFACE_GRID_M,
"grid_m": grid_m,
"margin_m": SHEET_SURFACE_MARGIN_M,
"smoothing": [
SURFACE_SMOOTHING_DTM_SIGMA_M,
@@ -453,12 +464,16 @@ def _key_path(models_dir: Path, method_key: str) -> Path:
def _cached_method_model(
project_root: Path, models_dir: Path, method_key: str, key: dict[str, Any] | None
project_root: Path,
models_dir: Path,
method_key: str,
key: dict[str, Any] | None,
grid_m: float = SHEET_SURFACE_GRID_M,
) -> dict[str, Any] | None:
"""열쇠가 같고 결과 파일이 있으면 등록용 dict — 아니면 None(다시 셈)."""
if key is None:
return None
entry = _method_model_entry(project_root, models_dir, method_key)
entry = _method_model_entry(project_root, models_dir, method_key, grid_m)
if not all((project_root / item).is_file() for item in _entry_files(entry)):
return None
try:
@@ -479,6 +494,7 @@ def build_sheet_surface_model(
route_xy: np.ndarray,
crs: str,
methods: list[str] | None = None,
grid_m: float = SHEET_SURFACE_GRID_M,
) -> list[dict[str, Any]]:
"""도엽등고선으로 방식별 DTM npz·프리뷰 glb를 만들고 등록용 dict 목록을 돌려준다.
@@ -486,14 +502,18 @@ def build_sheet_surface_model(
화면에서 바꿔 보며 정해야 한다(2026-08-30 사용자 지시). 실패하면 빈 목록 —
호출측은 분석을 계속한다(도엽 미확보 지역 폴백).
`route_xy`: (N, 2) 노선 정점 XY(사업지 CRS, m), `crs`: 그 좌표계(pyproj 입력 문자열).
`route_xy`: (N, 2) 노선 정점 XY(사업지 CRS, m), `crs`: 그 좌표계(pyproj 입력 문자열),
`grid_m`: 격자 한 변(m · 처음 값 = B 공용 1 m · D 예정노선은 2 m).
"""
started = time.monotonic()
selected = methods or list(SHEET_SURFACE_METHODS)
# 열쇠가 같은 방식은 저장본 그대로 — 다시 셀 방식만 등고선을 읽고 굽는다.
keys = {key: _sheet_key(project_root, processed_dir, route_xy, crs, key) for key in selected}
keys = {
key: _sheet_key(project_root, processed_dir, route_xy, crs, key, grid_m) for key in selected
}
cached = {
key: _cached_method_model(project_root, models_dir, key, keys[key]) for key in selected
key: _cached_method_model(project_root, models_dir, key, keys[key], grid_m)
for key in selected
}
pending = [key for key in selected if cached[key] is None]
if not pending:
@@ -507,7 +527,7 @@ def build_sheet_surface_model(
x_max = float(np.max(route_xy[:, 0])) + SHEET_SURFACE_MARGIN_M
y_min = float(np.min(route_xy[:, 1])) - SHEET_SURFACE_MARGIN_M
y_max = float(np.max(route_xy[:, 1])) + SHEET_SURFACE_MARGIN_M
spec = grid_spec_from_bounds(x_min, y_min, x_max, y_max, SHEET_SURFACE_GRID_M)
spec = grid_spec_from_bounds(x_min, y_min, x_max, y_max, grid_m)
# ① 2D — 등고 라인을 격자에 굽는다(라인 셀 = 표고, 그 외 NaN).
burned = _rasterize_contour_levels(spec, features)
@@ -536,7 +556,7 @@ def build_sheet_surface_model(
continue
# 쓰는 도중 끊기면 옛 열쇠가 새 파일 조각을 덮지 않게 먼저 지움.
_key_path(models_dir, method_key).unlink(missing_ok=True)
model = _write_method_model(project_root, models_dir, spec, surface, method_key)
model = _write_method_model(project_root, models_dir, spec, surface, method_key, grid_m)
if model is not None:
built[method_key] = model
if keys[method_key] is not None: