fix(763-1): 추천노선 = 확정 지표면에서만 — 확정 전 409 · 비용면 = 굳힌 격자 마디 · 조용한 DTM 대체 걷음

- `confirmed_surface_params()` — stage 1 COMPLETE · params 에 source_filter 있을 때만 · 아니면 None
- compute_initial = 확정 전 409 「지표면 확정 전 — B04 [확정] 뒤 추천이 생김」 · 미리 셈 지형 = [](건너뜀 로그)
- Solver implicit 갈래 = `implicit_grid_npz` 마디 그대로(RBF 다시 안 셈 · 짓기 16.2 s → 0.02 s · 유효 칸 최대차 0.00003 m)
- 모델 파일 없음 · 깨짐 = 까닭 그대로 FileNotFoundError · ValueError(409) · 서명에 굳힌 격자(서명 전에 지음)
- 시험 224_3 · 351_1 · 509_5 의 확정 갈아 끼우기를 새 함수로 옮김(라우터가 새 함수를 부름)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-03 13:17:31 +09:00
co-authored by Claude Opus 5.5
parent 92f74671ad
commit 80c2aabfcf
6 changed files with 202 additions and 29 deletions
+22 -22
View File
@@ -19,7 +19,11 @@ from B05_Profile.B05_Profile_Engine_Geometry import (
resample_polyline_2d,
single_segment_dijkstra,
)
from common_util.common_util_surface_sampler import check_surface_keys
from common_util.common_util_surface_sampler import (
DtmGridSampler,
check_surface_keys,
implicit_grid_npz,
)
from config.config_system import (
FOREST_ROAD_MAX_GRADE,
FOREST_ROAD_MIN_CURVE_R_M,
@@ -71,7 +75,7 @@ def _sample_surface_on_grid(
y_coords: np.ndarray,
dtm_z: np.ndarray,
) -> np.ndarray:
"""확정 지표면 모델의 표고를 DTM 격자에 샘플링한다(실패 시 DTM으로 폴백)."""
"""확정 지표면 표고를 DTM 격자에 샘플링 — 파일 없음 · 깨짐은 까닭 그대로 던짐(763-1)."""
check_surface_keys(filter_key, method)
if method == "dtm":
return dtm_z
@@ -114,24 +118,14 @@ def _sample_surface_on_grid(
return _finalize(spline(y_coords, x_coords).ravel())
if method == "implicit":
from scipy.interpolate import RBFInterpolator
d = np.load(models_dir / f"implicit_{filter_key}.npz")
centers = np.asarray(d["centers_xy"], dtype=np.float64)
cz = np.asarray(d["center_z"], dtype=np.float64)
smoothing = float(d["smoothing"][0]) if "smoothing" in d else 0.0
interp = RBFInterpolator(
centers,
cz,
neighbors=min(64, len(centers)),
smoothing=smoothing,
kernel="thin_plate_spline",
)
out = np.empty(len(query), dtype=np.float64)
for s in range(0, len(query), 50_000):
e = min(s + 50_000, len(query))
out[s:e] = interp(query[s:e])
return _finalize(out)
# 굳힌 1 m 격자(641-1) 마디 그대로 — B05 종횡단 표본기와 같은 값 · RBF 다시 안 셈(763-1)
grid = DtmGridSampler.open_cached(implicit_grid_npz(models_dir, filter_key))
ix = np.abs(grid.x[None, :] - np.asarray(x_coords)[:, None]).argmin(axis=1)
iy = np.abs(grid.y[None, :] - np.asarray(y_coords)[:, None]).argmin(axis=1)
off = max(np.abs(grid.x[ix] - x_coords).max(), np.abs(grid.y[iy] - y_coords).max())
if off > 1e-3:
raise ValueError(f"굳힌 격자 축이 DTM 과 다름: implicit_{filter_key}_grid.npz")
return _finalize(grid.z[np.ix_(iy, ix)])
if method == "meshfree":
from scipy.interpolate import griddata
@@ -140,8 +134,11 @@ def _sample_surface_on_grid(
pts = np.asarray(d["points"], dtype=np.float64)
z = griddata(pts[:, :2], pts[:, 2], query, method="linear")
return _finalize(z)
except Exception:
return dtm_z
except FileNotFoundError as error:
name = Path(error.filename).name if error.filename else f"{method}_{filter_key}"
raise FileNotFoundError(f"지표면 모델 파일이 없습니다: {name}") from error
except Exception as error: # noqa: BLE001 — 깨진 파일 = 까닭 그대로 409
raise ValueError(f"지표면 모델을 읽지 못함({method}_{filter_key}): {error}") from error
return dtm_z
@@ -157,6 +154,9 @@ def _source_npz_paths(models_dir: Path, filter_key: str, method: str, smooth: bo
if method != "dtm":
candidates.append(models_dir / f"{method}_{filter_key}{suffix}.npz")
candidates.append(models_dir / f"{method}_{filter_key}.npz")
# 비용면이 읽는 굳힌 격자 — 서명 전에 지음(763-1)
if method == "implicit" and candidates[-1].exists():
candidates.append(implicit_grid_npz(models_dir, filter_key))
seen: set[Path] = set()
out: list[Path] = []
for p in candidates: