fix(B05): 예정노선 정점을 직결 문턱 아래로 좁혀 계획선을 그대로 채택한다
용화 예정노선으로 자동 체인을 돌리자 B05가 400으로 끊겼다. 세그먼트 1 (BP -> CP1) 경로 탐색 실패: 종단경사 한계(26%)로 통과 경로가 없습니다. 원인은 제어점 간격이다. Solver.py:394 는 제어점 쌍이 direct_link_max_m (ROUTE_DIRECT_LINK_CELL_FACTOR 2.0 x ROUTE_GRID_RES_M 2.0 = 4.0m) 이하면 격자 탐색 없이 직결해 원청 계획노선을 그대로 보존하고, 경사·곡선 위반은 경고로만 남긴다. 샘플 CSV 350m / 135점 = 간격 2.6m -> 대부분 직결 -> 통과(곡선 위반 9건은 경고) 용화 예정 2136m / 121점 = 간격 17.6m -> 전부 탐색 -> 첫 구간 44.6%에서 실패 기존 프로젝트가 됐던 것은 제약을 만족해서가 아니라 탐색을 타지 않아서였다. 계획노선은 바꿀 수 없는 선이므로(사용자 확정), 같은 직선 위에 점을 더 찍어 간격만 좁힌다. densify_route()는 원래 정점을 모두 남기고 사이만 채운다 — 평면 형상도 연장도 끝점도 그대로다. 문턱과 정확히 같게 두면 부동소수 오차 한 번에 탐색으로 넘어가므로 ROUTE_PLANNED_DENSIFY_SAFETY(0.9)를 곱한다. 실측(용화, classification/dtm/smooth): 트림 후 63정점 -> 조밀화 323정점, 연장 1,070m 불변, 최대 간격 3.50m(문턱 4.0m), 시·종점 동일. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,10 +39,16 @@ def _planned_route_points_in_project_crs(
|
||||
"""
|
||||
from B04_PreProcess.B04_PreProcess_Engine_Extent import project_epsg_from_prj
|
||||
from common_util.common_util_route_geometry import (
|
||||
densify_route,
|
||||
find_planned_route_file,
|
||||
read_planned_route_csv,
|
||||
trim_route_to_surface,
|
||||
)
|
||||
from config.config_system import (
|
||||
ROUTE_DIRECT_LINK_CELL_FACTOR,
|
||||
ROUTE_GRID_RES_M,
|
||||
ROUTE_PLANNED_DENSIFY_SAFETY,
|
||||
)
|
||||
|
||||
route_file = find_planned_route_file(project_root / "B03_FileInput" / "input")
|
||||
planned = read_planned_route_csv(route_file) if route_file else None
|
||||
@@ -71,6 +77,13 @@ def _planned_route_points_in_project_crs(
|
||||
logger.warning("자동 설계 체인 노선 트림 건너뜀 — 지표면을 열지 못했습니다: %s", exc)
|
||||
else:
|
||||
points = trim_route_to_surface(points, sampler)
|
||||
# 예정노선은 정점이 성기다(용화 평균 17.6m). 그대로 넘기면 구간마다 격자
|
||||
# 탐색을 타고 급경사 현에서 끊긴다. 문턱 아래로 간격만 좁히면 계획노선이
|
||||
# 손대지 않은 채 채택된다 — 같은 직선 위에 점을 더 찍는 것이라 형상 불변.
|
||||
points = densify_route(
|
||||
points,
|
||||
ROUTE_DIRECT_LINK_CELL_FACTOR * ROUTE_GRID_RES_M * ROUTE_PLANNED_DENSIFY_SAFETY,
|
||||
)
|
||||
if len(points) < 2:
|
||||
logger.warning(
|
||||
"자동 설계 체인 중단(노선이 지표면 밖): 계획노선과 라이다 측량 범위가"
|
||||
|
||||
@@ -182,6 +182,36 @@ def trim_route_to_surface(
|
||||
return kept
|
||||
|
||||
|
||||
def densify_route(
|
||||
points: list[tuple[float, float]], max_spacing_m: float
|
||||
) -> list[tuple[float, float]]:
|
||||
"""원래 정점은 모두 남기고, 간격이 `max_spacing_m`을 넘는 구간에만 점을 끼워 넣는다.
|
||||
|
||||
**평면 형상은 바뀌지 않는다** — 같은 직선 위에 점을 더 찍을 뿐이다.
|
||||
B05 경로 탐색은 제어점 간격이 `ROUTE_DIRECT_LINK_CELL_FACTOR × ROUTE_GRID_RES_M`
|
||||
이하일 때만 격자 탐색을 건너뛰고 원좌표를 그대로 잇는다
|
||||
(`B05_Profile_Engine_Solver.py:394`). 예정노선처럼 정점이 성긴 선(용화 평균 17.6m)은
|
||||
그 문턱을 넘어 탐색을 타고, 급경사 현에서 "통과 경로 없음"으로 끊긴다.
|
||||
간격만 좁혀 주면 계획노선이 손대지 않은 채 그대로 채택된다 (2026-09-01 사용자 확정).
|
||||
"""
|
||||
if len(points) < 2 or max_spacing_m <= 0:
|
||||
return list(points)
|
||||
dense: list[tuple[float, float]] = [points[0]]
|
||||
for start, end in zip(points, points[1:]):
|
||||
distance = math.dist(start, end)
|
||||
steps = int(math.ceil(distance / max_spacing_m))
|
||||
for step in range(1, steps):
|
||||
ratio = step / steps
|
||||
dense.append(
|
||||
(
|
||||
start[0] + (end[0] - start[0]) * ratio,
|
||||
start[1] + (end[1] - start[1]) * ratio,
|
||||
)
|
||||
)
|
||||
dense.append(end)
|
||||
return dense
|
||||
|
||||
|
||||
def _trim_ends(
|
||||
points: list[tuple[float, float]], head_m: float, tail_m: float
|
||||
) -> list[tuple[float, float]]:
|
||||
|
||||
@@ -285,6 +285,9 @@ ROUTE_REQUIRED_POINT_TOLERANCE_M = float(os.getenv("ROUTE_REQUIRED_POINT_TOLERAN
|
||||
# 제어점 쌍이 이 배수×비용면 셀보다 가까우면 격자 탐색 없이 직결한다 — 원청 계획노선
|
||||
# 보존 (2026-08-30 사용자 확정. 조밀 기준선은 격자 중간점이 못 끼어들어 평면 불변).
|
||||
ROUTE_DIRECT_LINK_CELL_FACTOR = float(os.getenv("ROUTE_DIRECT_LINK_CELL_FACTOR", "2.0"))
|
||||
# 예정노선을 자동 체인에 넘기기 전, 직결 문턱의 이 비율까지 정점 간격을 좁힌다.
|
||||
# 문턱과 정확히 같게 두면 부동소수 오차 한 번에 탐색으로 넘어가 노선이 바뀐다.
|
||||
ROUTE_PLANNED_DENSIFY_SAFETY = float(os.getenv("ROUTE_PLANNED_DENSIFY_SAFETY", "0.9"))
|
||||
# 임도 종류 — 현행 규칙(별표2)의 3종. `branch`(지선)는 규칙에서 폐지됐으나 기존
|
||||
# 저장분이 남아 있어 값으로는 계속 받는다(화면 선택지에서는 뺀다, 2026-08-19).
|
||||
ROUTE_GRADE_CLASSES = ("trunk", "fire", "work", "branch")
|
||||
|
||||
Reference in New Issue
Block a user