feat(D 예정노선): 영역 루틴 — 받기 끝 지형면 · 받은 범위 먼저 → 겹 다시(차례 번호) · 도엽 겹 서버 자름 · 위치 칸 제목 서클 하나 · 10 km 밖 알림 (PLAN 604 · 요구 475 · 476-2)

- 받기 끝 = 지형면 · 받은 범위 다 읽은 뒤 겹 판정(동시 읽기 = 옛 범위 판정 뿌리) · 되돌리기 · 다시하기도 영역 확인
- 겹 load(have) — 받은 범위 지문 · 위성 틀 바뀜 = 차례 +1 · 자료 · 받는 중 비움 · 켠 겹 모두 다시 · 옛 차례 답은 자료 · GL 에 안 씀
- 등고 · 세류 = 예정노선 겹 길(layers/sheet) · 도엽 겹 모두 서버가 받은 범위로 자른 사본(화면 상자 · 보관함 지문 걷음)
- 서클 = 위치 칸 제목 오른쪽 하나 · 들어간 일 수(지형 받기 · 지형면 · 겹 · 임도망 올리기) · 부품 상자 원에 제목 줄 꼴(inline) 덧 · 지도 귀퉁이 서클 걷음
- 기준점 10 km 밖 점 = 위치 칸 알림 한 줄
- 시험 553_4 서클 줄 = 새 자리로 고침

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-30 21:43:20 +09:00
co-authored by Claude Opus 5.5
parent 20ed22709c
commit 64b35da265
8 changed files with 528 additions and 267 deletions
+43 -3
View File
@@ -290,12 +290,51 @@ async def _layer_root(project_id: UUID) -> tuple[Path, int, terrain.Box]:
return root, epsg, box
SHEET_CLIP_KIND = "plan_sheet"
SHEET_CLIP_VERSION = 1
def _clip_to_box(root: Path, source: Path, box: terrain.Box, epsg: int) -> Path:
"""받은 범위(경위도 외접 상자)로 자른 사본(475) — 열쇠 = 사본 지문 + 범위 · 실패 = 통째."""
from B04_PreProcess.B04_PreProcess_Engine_SheetClip import clip_collection
from common_util.common_util_derived_store import (
derived_dir,
derived_key,
file_stamp,
prune_derived,
)
try:
bounds = layers._lonlat_bounds(box, epsg)
rect = (bounds["min_lon"], bounds["min_lat"], bounds["max_lon"], bounds["max_lat"])
key = derived_key(
SHEET_CLIP_VERSION, source.name, file_stamp(source), [round(v, 1) for v in box]
)
target = derived_dir(root, SHEET_CLIP_KIND) / f"{key}.json"
if target.is_file():
return target
collection = json.loads(source.read_text(encoding="utf-8"))
text = json.dumps(
clip_collection(collection, rect), ensure_ascii=False, separators=(",", ":")
)
target.parent.mkdir(parents=True, exist_ok=True)
temporary = target.with_suffix(f".{uuid4().hex}.tmp")
temporary.write_text(text, encoding="utf-8")
temporary.replace(target)
prune_derived(root, SHEET_CLIP_KIND, keep=2 * len(layers.SHEET_LAYERS))
return target
except Exception as error: # 자르기 실패가 화면을 막으면 안 됨
logger.warning("도엽 겹 받은 범위 자름 실패 (%s): %s", source.name, error)
return source
@router.get("/{project_id}/plan/layers/sheet", response_model=None)
async def plan_layer_sheet(project_id: UUID, key: str, request: Request) -> Response:
"""도엽 겹 GeoJSON(하천중심선 · 도로중심선 …) — 없으면 지난 병합 도엽에서 풀어 냄."""
"""도엽 겹 GeoJSON(등고선 · 하천중심선 · 도로중심선 …) — 없으면 지난 병합 도엽에서 풀어 냄 ·
받은 범위로 자름(475 · 범위가 늘면 새 사본)."""
from B04_PreProcess.B04_PreProcess_Router_GIS import _display_copy
root, _, _ = await _layer_root(project_id)
root, epsg, box = await _layer_root(project_id)
try:
path = await asyncio.to_thread(layers.ensure_sheet_layer, root, key)
except KeyError as error:
@@ -303,7 +342,8 @@ async def plan_layer_sheet(project_id: UUID, key: str, request: Request) -> Resp
except FileNotFoundError as error:
raise HTTPException(status_code=404, detail=str(error)) from error
display = await asyncio.to_thread(_display_copy, path)
return cached_file_response(request, display, "application/geo+json")
clipped = await asyncio.to_thread(_clip_to_box, root, display, box, epsg)
return cached_file_response(request, clipped, "application/geo+json")
@router.get("/{project_id}/plan/layers/cadastral", response_model=None)