"""임도 프로젝트가 받은 예정노선 문맥(635-2 · 요구 546 · 547) — 같은 예정노선의 세트 모두 · 내 세트. 계산 자리 ③ 서버 단독 · 보기 전용(파일 · DB 안 씀) — 예정노선 설계 내용 파일을 그때 읽어 (`D03_PlanAnalysis_Store.plan_state`) 조각 꺾은선을 임도 프로젝트 좌표계로 바꿈(pyproj · 예정노선 = GRS80 TM · 임도 = 사업지 좌표계 `resolve_project_crs`). 업체 이름 · 메일은 안 실음. GET /api/projects/{id}/plan-context → {status, linked: False} | {status, linked: True, plan_name, epsg(임도), mine, year, sets: [{key, year, no, mine, parts, branch, length_m, lines: [[[x, y], …] 조각마다]}]} """ from __future__ import annotations from pathlib import Path from typing import Any from uuid import UUID from fastapi import APIRouter from pyproj import CRS, Transformer from common_util.common_util_crs import resolve_project_crs from common_util.common_util_storage import resolve_stored_project_path from D03_PlanAnalysis import D03_PlanAnalysis_Store as store from D04_PlanConfirm.D04_PlanConfirm_Link import _fetch_one router = APIRouter(prefix="/api/projects", tags=["D04 Plan Context"]) def context_sets( state: dict[str, Any], mine: dict[str, Any] | None, target: str ) -> list[dict[str, Any]]: """세트마다 조각 꺾은선(임도 좌표) — 세트 · 조각 차례 그대로.""" source = f"EPSG:{int(state['plan']['epsg'])}" move = Transformer.from_crs(source, target, always_xy=True) pieces = {p["id"]: p for p in state["pieces"]} out = [] for unit in state["units"]: lines = [ [list(move.transform(x, y)) for x, y in pieces[pid]["planned"]] for pid in unit["pieces"] ] out.append( { "key": unit["id"], "year": unit["year"], "no": unit["set"], "mine": mine is not None and unit["id"] == mine["id"], "parts": unit["parts"], "branch": unit["branch"], "length_m": unit["length_m"], "lines": lines, } ) return out @router.get("/{project_id}/plan-context", response_model=None) async def plan_context(project_id: UUID) -> dict[str, Any]: """받은 세트 · 같은 예정노선의 다른 세트(다른 해 · 같은 해 다른 업체) · 연결 없으면 linked False.""" row = await _fetch_one( "SELECT a.project_id, a.piece_id, s.name AS plan_name FROM plan_route_assignees a" " JOIN projects s ON s.id = a.project_id AND s.deleted_at IS NULL" " WHERE a.linked_project_id = %s ORDER BY a.id LIMIT 1", (str(project_id),), ) if not row: return {"status": "success", "linked": False} road = await store.project_row(project_id) target = resolve_project_crs(Path(resolve_stored_project_path(road["storage_path"]))) state = await store.plan_state(str(row["project_id"])) mine = store.find_unit(state["units"], row["piece_id"]) return { "status": "success", "linked": True, "plan_name": row["plan_name"], "epsg": CRS.from_user_input(target).to_epsg(), # WKT 원문이면 못 찾을 때 None "mine": mine and mine["id"], "year": mine and mine["year"], "sets": context_sets(state, mine, target) if state["plan"]["epsg"] else [], }