Files
Aislo/B05_Profile/B05_Profile_Router_PlanSource.py
T
eomsangdonandClaude Opus 5.5 e70201a626 feat(635-2): 받은 세트 문맥 GET plan-context · 원본 꼭짓점 GET route/plan/source · 다시 추적 전 조밀화 = 체인 초기선 거리 0(요구 546 · 547)
- 세트 연결 = 638-1 그대로(B 하나 = 세트 하나 · 열쇠만 세트)
- 새 D04_PlanConfirm_Context — 같은 예정노선 세트 모두 · 내 세트 · 연도 · 임도 좌표계로 pyproj 변환 · 업체 이름 없음 · 보기 전용
- 새 B05_Profile_Router_PlanSource — 원본 꼭짓점 = 범위 + 지표면 트림 · 조밀화만 뺌(Router_Replan 안 늘림 · 덧칸 대신 새 길)
- RouteFollow 추적 전 체인과 같은 3.6 m 조밀화(브레인 허락 · 옛 촘촘 입력은 그대로 = 626-2 같음)
- 실측 검증_sub3: 원본 64 점 → follow = 초기선 181 점 거리 0 · 파일 안 바뀜

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 11:46:53 +09:00

85 lines
3.5 KiB
Python

"""받은 예정노선 원본 꼭짓점(635-2 · 요구 546) — B05 「예정노선 수정」이 고칠 선.
계산 자리 ③ 서버 단독 · 읽기만(파일 안 만듦). 원본 = 올린(받은) 노선 파일 꼭짓점을 사업지 좌표로 ·
사용 범위(`projects.route_start_m · route_end_m`) · 지표면 트림까지 — 체인 예정노선
(`load_design_route`)과 같은 차례에서 **조밀화만 뺌**. 고친 꼭짓점은 `POST /route/follow` 가
같은 간격(`spacing_m`)으로 조밀화한 뒤 추적 → 안 고친 원본 = 체인 초기선과 거리 0(`test_635_2`).
GET /api/projects/{id}/route/plan/source
→ {status, expected_source: [[x, y], …], spacing_m} (원본 없음 = 빈 목록)
404 프로젝트 없음
"""
from __future__ import annotations
import asyncio
from pathlib import Path
from typing import Any
from uuid import UUID
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from B05_Profile.B05_Profile_Router_Replan import _PROJECT_PATH_MISSING, _project_paths
from common_util.common_util_route_geometry import load_design_route, trim_route_to_surface
from common_util.common_util_surface_confirmation import get_surface_confirmation_params
from common_util.common_util_surface_sampler import build_surface_sampler
from config.config_db import get_db_pool
from config.config_system import (
ROUTE_DIRECT_LINK_CELL_FACTOR,
ROUTE_GRID_RES_M,
ROUTE_PLANNED_DENSIFY_SAFETY,
)
router = APIRouter(prefix="/api/projects", tags=["B05 Plan Source"])
#: 체인 예정노선 조밀화 간격(`load_design_route` 와 같은 값)
SPACING_M = ROUTE_DIRECT_LINK_CELL_FACTOR * ROUTE_GRID_RES_M * ROUTE_PLANNED_DENSIFY_SAFETY
def source_vertices(
root: Path, surface: dict[str, Any] | None, route_range: tuple | None
) -> list[list[float]]:
"""원본 꼭짓점 — 사업지 좌표 · 범위 · 지표면 트림(지표면을 못 열면 트림 없이) · 조밀화 없음."""
planned = load_design_route(root, None, route_range)
if planned is None:
return []
points = [(v.x, v.y) for v in planned.vertices]
if surface:
try:
sampler = build_surface_sampler(
root / "B04_PreProcess" / "models",
str(surface["source_filter"]),
str(surface["method"]),
bool(surface["smooth"]),
)
except (FileNotFoundError, KeyError, OSError):
sampler = None
if sampler is not None:
points = trim_route_to_surface(points, sampler)
return [[float(x), float(y)] for x, y in points] if len(points) >= 2 else []
@router.get("/{project_id}/route/plan/source", response_model=None)
async def read_plan_source(project_id: UUID) -> dict[str, Any] | JSONResponse:
paths = await _project_paths(project_id)
if paths is None:
return JSONResponse(status_code=404, content=_PROJECT_PATH_MISSING)
async with get_db_pool().acquire() as connection:
surface = await get_surface_confirmation_params(connection, str(project_id))
async with connection.cursor() as cursor:
await cursor.execute(
"SELECT route_start_m, route_end_m FROM projects WHERE id = %s",
(str(project_id),),
)
row = await cursor.fetchone()
vertices = await asyncio.to_thread(
source_vertices, paths[0], surface, (row[0], row[1]) if row else None
)
return {
"status": "success",
"project_id": str(project_id),
"expected_source": vertices,
"spacing_m": SPACING_M,
}