fix(765-8): 벌마다 평면노선 따로 — 받은 노선을 그 벌 지형으로 잘라 벌 예정노선으로 두고 따라감(요구 612)

- 벌 예정노선 terrain_route.csv(벌 몫) — 체인이 받은 노선을 그 지형으로 잘라 한 번 적음 · 초기 폴리라인(merged_follow) · 화면 예상노선 · 고칠 선 끝이 이것을 바탕으로(route_source_path · 없으면 공유 예상노선)
- 공유 예상노선 expected_route.csv = 설계 지형으로 자른 하나(추천 열쇠 그대로) · 고친 예정노선 expected_route_edit.csv 는 벌 몫(다른 벌에 안 번짐)
- 검증_sub5: 라이다 벌 1,352.9 m · 곡선 19(옛 값과 같음) · 수치지형도 벌 2,110.2 m · 곡선 28(예정노선 끝까지) · 칩 왕복 · 벌마다 [초기화] 값 같음 · 공유 예상노선 그대로
- 시험 test_765_design_sets — 벌 몫 가름에 벌 예정노선 · 고친 예정노선 · 벌 예정노선 자름 · 초기 폴리라인 바탕 둘 덧 · 지운 csf 필터 글자를 ptd · classification 으로(637 · 763_3 · 765)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-03 16:56:10 +09:00
co-authored by Claude Opus 5.5
parent 61c6f0e9df
commit 57a6c0ee22
9 changed files with 115 additions and 18 deletions
+33 -3
View File
@@ -231,7 +231,8 @@ async def run_auto_design_chain(
route_range = (range_row[0], range_row[1]) if range_row else None
seeded_speed = range_row[2] if range_row else None
points = _planned_route_points_in_project_crs(project_root, defaults, route_range)
# 벌 예정노선 = 받은 노선을 이 벌 지형으로 자름(765-8 · 요구 612) — 벌마다 길이 · 곡선 다름
points = await asyncio.to_thread(_terrain_route_points, project_root, defaults, route_range)
if not points:
logger.warning("자동 설계 체인 중단(계획노선 없음): project_id=%s", project_id)
mark_design_failed(project_root, "계획노선이 없어 초기 노선을 세울 수 없습니다.")
@@ -415,8 +416,8 @@ async def run_preprocess_chain(
missing = "sheet" if design == "las" else "las"
sets.note_unbuilt(root, missing, f"{missing} 지표면 모델 없음 — 한 벌만")
else:
# 예상노선(공유 · 추천이 씀)은 설계 지형으로 자른 것 하나 — 먼저 적어 두면 두 벌이 같은
# 평면 노선으로 지어짐(지형만 다름 · 다른 지형 벌 [초기화]도 같은 노선)
# 예상노선(공유 · 추천 열쇠)은 설계 지형으로 자른 것 하나 — 벌 평면노선은 벌마다 그 지형으로
# 자른 벌 예정노선(`terrain_route.csv`)을 따라감(765-8 · 요구 612)
await _seed_expected_route(project_id, root, params)
try:
async with pool.acquire() as connection:
@@ -453,6 +454,35 @@ async def run_preprocess_chain(
schedule_precompute(project_id) # 추천은 지형 · 예정노선만 씀 — 실패 길에도(763-3)
def _terrain_route_points(
root: Path, surface: dict[str, Any], route_range: tuple[Any, Any] | None
) -> list[dict[str, float]] | None:
"""벌 예정노선(`terrain_route.csv` · 벌 몫) — 있으면 그것 · 없으면 받은 노선을 이 지형으로
잘라 적음.
받은 노선 파일이 없는 옛 프로젝트 = 정본 차례(`load_design_route`) 그대로."""
from common_util.common_util_route_geometry import (
load_design_route,
read_planned_route_csv,
terrain_route_csv_path,
write_route_csv,
)
path = terrain_route_csv_path(root)
stored = read_planned_route_csv(path) if path.is_file() else None
if stored is not None and len(stored.vertices) >= 2:
return [{"x": v.x, "y": v.y} for v in stored.vertices]
planned = load_design_route(root, surface, route_range, from_source=True)
points = (
[{"x": v.x, "y": v.y} for v in planned.vertices]
if planned is not None
else _planned_route_points_in_project_crs(root, surface, route_range)
)
if points:
write_route_csv(path, points)
return points
async def _seed_expected_route(project_id: UUID, root: Path, surface: dict[str, Any]) -> None:
"""예상노선 정본을 설계 지형으로 잘라 적음(없을 때만 · 자동 체인 2.5 와 같은 값)."""
from common_util.common_util_route_geometry import expected_route_csv_path, write_route_csv