fix(755-3): 받은 노선 원본 꼭짓점을 체인 예정노선 파일 양끝에서 자름 — 지표면 넓어진 뒤 280 m 긴 흠
- /route/plan/source = 지표면 트림 뒤 expected_route.csv 시점 · 종점 투영 자리에서 자름(파일 없음 = 그대로) · 0.1 mm 반올림 겹친 점 하나만 - 검증_sub2 source 1,377.4 → 1,097.4 m(= csv) · follow 1,356.5 → 1,080.2 m · 검증_sub1 은 전후 같음(1,097.4) - 시험 test_635_2 양끝 자름 · 파일 없음 그대로 한 건 더함 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,9 @@
|
||||
|
||||
계산 자리 ③ 서버 단독 · 읽기만(파일 안 만듦). 원본 = 올린(받은) 노선 파일 꼭짓점을 사업지 좌표로 ·
|
||||
사용 범위(`projects.route_start_m · route_end_m`) · 지표면 트림까지 — 체인 예정노선
|
||||
(`load_design_route`)과 같은 차례에서 **조밀화만 뺌**. 고친 꼭짓점은 `POST /route/follow` 가
|
||||
(`load_design_route`)과 같은 차례에서 **조밀화만 뺌**. 끝 = 체인이 자른 `expected_route.csv`
|
||||
양끝(755-3 · 지표면이 그 뒤 넓어져도 화면 예상노선 · 저장 csv 와 같은 길이).
|
||||
고친 꼭짓점은 `POST /route/follow` 가
|
||||
같은 간격(`spacing_m`)으로 조밀화한 뒤 추적 → 안 고친 원본 = 체인 초기선과 거리 0(`test_635_2`).
|
||||
|
||||
GET /api/projects/{id}/route/plan/source
|
||||
@@ -13,15 +15,23 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import math
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import JSONResponse
|
||||
from shapely.geometry import LineString, Point
|
||||
|
||||
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_route_geometry import (
|
||||
clip_route_by_chainage,
|
||||
expected_route_csv_path,
|
||||
load_design_route,
|
||||
read_planned_route_csv,
|
||||
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
|
||||
@@ -57,9 +67,23 @@ def source_vertices(
|
||||
sampler = None
|
||||
if sampler is not None:
|
||||
points = trim_route_to_surface(points, sampler)
|
||||
points = clip_to_expected(root, points)
|
||||
return [[float(x), float(y)] for x, y in points] if len(points) >= 2 else []
|
||||
|
||||
|
||||
def clip_to_expected(root: Path, points: list[tuple[float, float]]) -> list[tuple[float, float]]:
|
||||
"""체인이 자른 예정노선 파일 양끝에서 자름(755-3) — 파일 없음 · 2점 미만 = 그대로."""
|
||||
path = expected_route_csv_path(root)
|
||||
stored = read_planned_route_csv(path) if path.is_file() else None
|
||||
if stored is None or len(stored.vertices) < 2 or len(points) < 2:
|
||||
return points
|
||||
line = LineString(points)
|
||||
ends = [line.project(Point(v.x, v.y)) for v in (stored.vertices[0], stored.vertices[-1])]
|
||||
clipped = clip_route_by_chainage(points, min(ends), max(ends))
|
||||
# 파일 좌표 반올림(0.1 mm) = 끝이 꼭짓점 바로 앞에 떨어짐 — 1 mm 안 겹친 점은 하나만
|
||||
return [p for i, p in enumerate(clipped) if not i or math.dist(p, clipped[i - 1]) > 1e-3]
|
||||
|
||||
|
||||
@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)
|
||||
|
||||
@@ -133,6 +133,31 @@ def test_plan_source_skips_densify_keeps_trim(tmp_path: Path, monkeypatch) -> No
|
||||
assert asyncio.run(source.read_plan_source(uuid4())).status_code == 404
|
||||
|
||||
|
||||
def test_plan_source_clips_to_expected_csv_ends(tmp_path: Path, monkeypatch) -> None:
|
||||
"""755-3 — 지표면이 넓어져 트림이 덜 잘라도 체인 예정노선 파일 양끝에서 자름(같은 길이)."""
|
||||
monkeypatch.setattr(
|
||||
source,
|
||||
"load_design_route",
|
||||
lambda *a: SimpleNamespace(vertices=[SimpleNamespace(x=x, y=y) for x, y in SPARSE]),
|
||||
)
|
||||
monkeypatch.setattr(source, "build_surface_sampler", lambda *a: object())
|
||||
monkeypatch.setattr(source, "trim_route_to_surface", lambda points, sampler: points)
|
||||
surface = {"source_filter": "classification", "method": "dtm", "smooth": True}
|
||||
dense = densify_route(SPARSE[3:30], source.SPACING_M)
|
||||
write_route_csv(expected_route_csv_path(tmp_path), [{"x": x, "y": y} for x, y in dense])
|
||||
got = source.source_vertices(tmp_path, surface, None)
|
||||
|
||||
def length(points):
|
||||
return sum(math.dist(a, b) for a, b in zip(points, points[1:]))
|
||||
|
||||
assert abs(length(got) - length(dense)) < 1e-3 # 파일 좌표 0.1 mm 반올림
|
||||
assert math.dist(got[0], SPARSE[3]) < 1e-3 and math.dist(got[-1], SPARSE[29]) < 1e-3
|
||||
assert len(got) == 27 # 끝 = 꼭짓점 바로 앞 · 겹친 점 없음
|
||||
# 파일 없음 = 그대로
|
||||
expected_route_csv_path(tmp_path).unlink()
|
||||
assert len(source.source_vertices(tmp_path, surface, None)) == len(SPARSE)
|
||||
|
||||
|
||||
MAIN = {
|
||||
"id": "main",
|
||||
"vertices": [[200000.0, 450000.0], [200100.0, 450000.0], [200200.0, 450000.0]],
|
||||
|
||||
Reference in New Issue
Block a user