refactor(공통): 작업 좌표계 폴백을 창구 하나로 통합 + 트림 실패 진단

같은 폴백 사다리가 네 곳에 서로 다른 모양으로 흩어져 있었고, 그중 배수유역 라우터
`_route_center_lonlat` 은 노선 CSV 의 `crs_epsg` **라벨**을 그대로 변환에 썼다(라벨과
실좌표계가 다른 사례 실측 — 2026-09-01 용화 라벨 5179 / 실제 5176).

`common_util_crs.resolve_project_crs()` 신설 — 사다리는 ① 노선 crs_input ② 파일 라벨
(원본 좌표를 읽는 자리만) ③ 지형 PRJ(작업 좌표계 정본) ④ DB epsg ⑤ EPSG:5186.
작업 좌표계는 **지형 PRJ 우선으로 확정**(2026-09-03 사용자 결정 — 서피스 격자가 모델좌표의
주인이라 현행 유지). `project_epsg_from_prj()` 도 이 창구로 위임.

더해 지표면 트림이 노선을 통째로 지울 때 노선·지표면 bbox 를 함께 로그에 남긴다 —
"겹치지 않습니다" 만으로는 좌표계 문제인지 측량 범위 문제인지 갈리지 않았다.

검증 — `tmp/tests/test_project_crs_resolution.py` 5건 신설(사다리 4·진단 1),
전체 375 passed·17 skipped, ruff format 무변경.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-03 13:25:20 +09:00
co-authored by Claude Opus 5
parent f91d589ed4
commit e83171b2b3
6 changed files with 98 additions and 16 deletions
+48
View File
@@ -209,3 +209,51 @@ def crs_input_from_prj(prj_text: str) -> str | None:
else:
logger.warning("PRJ 좌표계의 EPSG 라벨을 찾지 못함 — 파일 WKT로 변환: %s", crs.name)
return text
def project_prj_crs(project_root: Path) -> str | None:
"""프로젝트 **작업 좌표계** — 지형 PRJ 원문. PRJ가 없으면 None.
작업 좌표계는 지형 PRJ 로 확정(2026-09-03 사용자 결정, 종전 동작 유지). 서피스 격자가
모델좌표의 주인이고 노선은 `load_design_route()` 가 이 좌표계로 옮겨 오기 때문이다.
노선 shapefile 은 다른 좌표계로 들어온다(실측 2026-08-31 — 노선 UTM-K / 지형 동부원점).
기본값을 섞지 않는다 — 사다리(`resolve_project_crs`)가 다음 근거로 내려갈 수 있어야 한다.
"""
prj_path = find_project_prj(project_root)
if prj_path is None:
return None
from B04_PreProcess.B04_PreProcess_Engine_VWorld import get_epsg_from_prj
return get_epsg_from_prj(prj_path.read_text(encoding="utf-8", errors="ignore"))
def resolve_project_crs(
project_root: Path,
*,
route_crs_input: str | None = None,
file_label_epsg: int | None = None,
db_epsg: int | None = None,
) -> str:
"""작업 좌표계를 정하는 **단일 창구** — pyproj 입력 문자열(`EPSG:n` 또는 WKT 원문).
사다리(위가 이김):
① `load_design_route()` 가 돌려준 노선 `crs_input` — 이미 작업 좌표계로 맞춰 둔 값
② `file_label_epsg` — **원본 파일 좌표를 그대로 해석할 때만** 넘긴다(노선 CSV 의
`crs_epsg` 열). 그 자리에서는 파일이 스스로 밝힌 좌표계가 유일한 근거다.
③ 지형 PRJ(`project_prj_crs`) — 작업 좌표계 정본
④ DB `surface_models.crs_epsg`
⑤ 중부원점 `EPSG:5186` — 최후 폴백
**변환이 끝난 좌표를 다룰 때는 ②를 넘기지 않는다.** 그 라벨은 실좌표계와 다른 사례가
실측됐다(2026-09-01 용화 — 라벨 5179 / 실제 5176). 이 창구를 두기 전에는 같은 사다리가
네 곳에 서로 다른 모양으로 흩어져 있었다.
"""
if route_crs_input:
return route_crs_input
if file_label_epsg:
return f"EPSG:{file_label_epsg}"
prj_crs = project_prj_crs(project_root)
if prj_crs:
return prj_crs
return f"EPSG:{db_epsg or 5186}"