- 새 B05_Profile_Engine_RouteInitial_Fit: 성긴 최소제곱 맞춤 · 측점 벌점 1 → 1000 · 끝 확인(곡선 수 · MC 측점 어김 0 · R ≥ 최소 R) · 수정 끝 틀 고르기(예정노선 거리 덜 늘어나는 합침) - merged_follow: 합치기 뒤 측점 맞춤 대신 맞춤 · 끝 확인 못 지나면 지금 측점 맞춤으로 물러남 · keep_curves - /route/follow: 수정 전 곡선 수 = 본문 keep_curves 또는 저장 노선 곡선표 · 응답 keep_curves · curve_note - 773 여덟 꼴 평균 최대 26.5 → 6.9 m · 평균 3.80 → 1.24 m · N 유지 8/8 · 측점 어김 0 · R 미달 0 · 물러남 0 - 지킴 시험 test_775_route_fit 새로 · test_758_1 그대로 통과(입력 덧점 정리로 체인 · 추적 같은 선) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
93 lines
3.5 KiB
Python
93 lines
3.5 KiB
Python
"""775 평면계획선 예정노선 맞춤(요구 615 · 617 · 갈래 M) — 곡선 수 그대로 · 스냅 절대 · 안전판.
|
|
|
|
./venv/Scripts/python.exe -m pytest resources/tester/test_775_route_fit.py -q
|
|
|
|
- 맞춤 = 지금 측점 맞춤보다 예정노선에 가까움 · MC 모두 일반측점 · R ≥ 최소 R · 곡선 수 = 합친 뒤.
|
|
- 수정 끝 `keep_curves` = 그 곡선 수(덜 합침 · 더 합침) · 굽이보다 많으면 있는 만큼 + 안내 글.
|
|
- 끝 확인 못 지나면 지금 측점 맞춤으로 물러남.
|
|
- 저장 노선 곡선 수 = 수정본 → 초기본 곡선표.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
|
|
from shapely.geometry import LineString
|
|
from test_758_1_follow_merge import EXPECTED, RADIUS, _land
|
|
|
|
from B05_Profile import B05_Profile_Engine_RouteInitial_Follow as follow_mod
|
|
from B05_Profile.B05_Profile_Engine_RouteInitial_Fit import passes
|
|
from B05_Profile.B05_Profile_Engine_RouteInitial_Station import off_station_count
|
|
from B05_Profile.B05_Profile_Router_RouteFollow import follow_route_line, saved_curve_count
|
|
from common_util.common_util_route_geometry import (
|
|
planned_route_initial_path,
|
|
planned_route_working_path,
|
|
)
|
|
|
|
|
|
def _gap(line) -> float:
|
|
return LineString(EXPECTED).hausdorff_distance(LineString(line.vertices))
|
|
|
|
|
|
def _ok(line) -> None:
|
|
assert off_station_count(line) == 0
|
|
assert min(c.radius_m for c in line.curves) >= RADIUS - 1e-6
|
|
|
|
|
|
def _old(monkeypatch):
|
|
follow_mod._MEMO.clear()
|
|
monkeypatch.setattr(follow_mod, "fit_to_expected", lambda *a: None)
|
|
try:
|
|
return follow_mod.merged_follow(EXPECTED, RADIUS, _land())
|
|
finally:
|
|
monkeypatch.undo()
|
|
follow_mod._MEMO.clear()
|
|
|
|
|
|
def test_맞춤_가까움_측점_최소_R(monkeypatch):
|
|
old = _old(monkeypatch)
|
|
new = follow_mod.merged_follow(EXPECTED, RADIUS, _land())
|
|
_ok(new)
|
|
assert passes(new, len(new.curves), RADIUS)
|
|
assert _gap(new) < _gap(old)
|
|
|
|
|
|
def test_곡선_수_지킴():
|
|
base = len(follow_mod.merged_follow(EXPECTED, RADIUS, _land()).curves)
|
|
for keep in (base - 1, base + 1):
|
|
line = follow_mod.merged_follow(EXPECTED, RADIUS, _land(), keep_curves=keep)
|
|
assert len(line.curves) == keep
|
|
_ok(line)
|
|
|
|
|
|
def test_굽이보다_많으면_있는_만큼_안내():
|
|
found = follow_route_line(EXPECTED, RADIUS, _land(), keep_curves=500)
|
|
assert found["curve_count"] < 500
|
|
assert "500" in found["curve_note"]
|
|
plain = follow_route_line(EXPECTED, RADIUS, None, keep_curves=500)
|
|
assert plain["curve_note"] is None and plain["keep_curves"] is None
|
|
|
|
|
|
def test_끝_확인_못_지나면_지금_측점_맞춤(monkeypatch):
|
|
old = _old(monkeypatch)
|
|
_ok(old) # 물러난 선도 측점 · 최소 R 지킴
|
|
assert not passes(old, len(old.curves) + 1, RADIUS) # 곡선 수 다르면 끝 확인 못 지남
|
|
|
|
|
|
def test_저장_노선_곡선_수(tmp_path):
|
|
assert saved_curve_count(tmp_path) is None
|
|
initial = planned_route_initial_path(tmp_path)
|
|
initial.parent.mkdir(parents=True, exist_ok=True)
|
|
initial.write_text("x,y\n0,0\n", encoding="utf-8")
|
|
initial.with_name(f"{initial.stem}_curves.json").write_text(
|
|
json.dumps({"curves": [{}, {}, {}]}), encoding="utf-8"
|
|
)
|
|
assert saved_curve_count(tmp_path) == 3
|
|
working = planned_route_working_path(tmp_path)
|
|
working.parent.mkdir(parents=True, exist_ok=True)
|
|
working.write_text("x,y\n0,0\n", encoding="utf-8")
|
|
working.with_name(f"{working.stem}_curves.json").write_text(
|
|
json.dumps({"curves": [{}, {}]}), encoding="utf-8"
|
|
)
|
|
assert saved_curve_count(tmp_path) == 2
|