feat(508-2 B05 · B06 남은 셋): 3D 길 모형 D3 = 서버 — 낡은 저장본 = 옛 모형 세움(빈 3D 아님) · [3D 업데이트]만 서버 창구(/calc corridor.mesh · 같은 TS · 라이브 계획선 샘플)가 지어 바꿔 끼움 · 같은 입력 다시 안 지음 · 실패 = 옛 모형 · 늦음 남김 · 창 열 때 자동 안 지음(요구 324) · both 그대로 · 추천 창 [재계산] = 저장본 없음(409) 안내 줄 옆 · 칸 바꿈 뒤만 · 노선 편집 도엽 = 놓은 뒤(잠잠) 범위 밖이면 지금 노선 bbox 로 잘린 사본 다시 받아 바꿔 끼움(서버 bbox · 열쇠 = 사본 지문 + 범위 · 여는 때도 통째 사본 안 받음 · 배수유역 범위 그대로) · 시험 test_508_2 셋 · 낡은 기대 셋(383_3 · 309_2 · 345_4) 고침 — 요구 323

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-22 17:13:41 +09:00
co-authored by Claude Opus 5.5
parent 4cf9e5d0a0
commit f1a6de017e
19 changed files with 701 additions and 58 deletions
@@ -6,7 +6,8 @@
- 범위(사업지 m · 바깥으로 `SNAP_M` 눈금에 맞춤 → 노선을 조금 옮겨도 같은 열쇠)
- `route` = 저장 노선 bbox + 띠 `ROUTE_BAND_M` + 여유 `ROUTE_SPARE_M` — 편집이 이 밖으로
나가면 화면이 통째 사본을 받는다(`clip_lonlat` 으로 판정).
나가면 화면이 놓은 뒤 편집 중 노선 bbox(`route_bounds` · 508-2)로 다시 받는다
(`clip_lonlat` 으로 판정).
- `drainage` = 세부 유역(노선 교차 관으로 모이는 유역) ∪ 줌아웃 한계(노선 bbox ×2)
+ `DRAIN_SPARE_M`.
- 파생 저장본 = `_derived/sheet_<범위>/<열쇠>.json`(GeoJSON 그대로 · 열쇠 = 사본 지문 + 범위)
@@ -144,9 +145,25 @@ def _basin_points(project_root: Path, meta: dict[str, Any]) -> list[tuple[float,
return points
def clip_rect_m(project_root: Path, scope: str, meta: dict[str, Any]) -> Rect | None:
"""범위 사각형(사업지 m · 눈금 맞춤). 노선이 없으면(또는 배수유역도에 유역이 없으면) None."""
route = _bounds(_route_points(project_root))
def parse_route_bbox(text: str | None) -> Rect | None:
"""화면이 보낸 편집 중 노선 bbox(`x0,y0,x1,y1` m) — 숫자 넷 · 차례 맞음만 · 아니면 None."""
try:
values = [float(part) for part in (text or "").split(",")]
except ValueError:
return None
if len(values) != 4 or not all(math.isfinite(v) for v in values):
return None
if values[0] > values[2] or values[1] > values[3]:
return None
return values[0], values[1], values[2], values[3]
def clip_rect_m(
project_root: Path, scope: str, meta: dict[str, Any], route_bounds: Rect | None = None
) -> Rect | None:
"""범위 사각형(사업지 m · 눈금 맞춤). 노선이 없으면(또는 배수유역도에 유역이 없으면) None.
`route_bounds` = 저장 노선 대신 쓸 노선 bbox(편집 중 · 508-2)."""
route = route_bounds or _bounds(_route_points(project_root))
if route is None:
return None
if scope == "route":
@@ -214,11 +231,18 @@ def _write_compact(path: Path, value: Any) -> None:
raise
def clipped_sheet(project_root: Path, source: Path, scope: str) -> Path:
"""`scope` 범위로 자른 표시본 경로. 범위를 못 정하거나 실패하면 `source` 그대로."""
def clipped_sheet(
project_root: Path, source: Path, scope: str, route_bounds: Rect | None = None
) -> Path:
"""`scope` 범위로 자른 표시본 경로. 범위를 못 정하거나 실패하면 `source` 그대로.
열쇠 = 사본 지문 + 범위 — 같은 범위면 노선을 어디서 재든 같은 저장본(다시 안 셈)."""
try:
meta = _read_meta(project_root / "B04_PreProcess" / "processed")
rect_m = clip_rect_m(project_root, scope, meta) if meta and scope in SCOPES else None
rect_m = (
clip_rect_m(project_root, scope, meta, route_bounds)
if meta and scope in SCOPES
else None
)
if rect_m is None or meta is None:
return source
west, south = _to_lonlat(meta, rect_m[0], rect_m[1])