fix(B04): 프로젝트 좌표계를 지형 자료와 짝인 PRJ로 고른다

실측 사고(2026-08-31): 실제 업로드에서 자동 설계 체인이 B05 경로 계산 400으로
멈췄다. input/prj/ 에 옛 result.prj(KGD2002 East Belt 2010, FN 600,000)가 남아
있었고, find_project_prj 가 이름 정렬 첫 번째를 골라 새 용화.prj(Korean 1985
Modified East Belt, FN 500,000) 대신 그것을 집었다. 그 결과 노선이 Y로
100,000m 어긋나(로그의 bp y=468228.23, 옳은 값 367920.75) 지표면 밖으로 나갔다.

자료를 다시 올려도 파일명이 다르면 옛 PRJ가 그대로 남는다 - 이름 정렬은 어느
것이 지금 쓰는 것인지 알지 못한다. 그래서 지금 쓰는 지형 자료(LAS/LAZ/TIF/TFW)와
basename이 같은 PRJ를 먼저 찾고, 못 찾으면 가장 최근 것을 쓰고 경고를 남긴다.
폴백 경로에서도 노선 세트 폴더(input/shp/)의 PRJ는 제외한다 - 그건 노선 좌표계다.

검증: 실제 프로젝트 폴더로 확인 - 선택이 용화.prj로 바뀌고 노선 시점이
x=208403.20 y=367920.75 로 TFW 원점(208288.84/368092.28) 안쪽에 앉는다.
tmp/tests/test_route_shapefile_input.py 10개 통과(회귀 시험 추가), 전체 302 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-31 20:10:16 +09:00
co-authored by Claude Opus 5
parent 1eb5ad4a60
commit afc404cbc5
+33 -6
View File
@@ -124,18 +124,45 @@ def identify_epsg(crs: CRS, wkt_text: str | None = None) -> int | None:
return None
_TERRAIN_DATA_GLOBS = ("las/*", "laz/*", "tif/*", "tfw/*")
def find_project_prj(project_root: Path) -> Path | None:
"""프로젝트 **작업 좌표계**를 정하는 PRJ를 고른다.
자료가 둘 이상의 좌표계로 들어온다(실측 2026-08-31 — 노선 shapefile은 UTM-K,
지형은 동부원점 Bessel). 서피스 격자가 모델좌표의 주인이므로 지형 PRJ가 우선이고,
노선 PRJ는 shapefile 세트 폴더(`input/shp/`)에 있어 여기서 섞이지 않는다.
파일명 정렬 순서에 기대던 옛 `glob()[0]`을 대신한다.
지형은 동부원점 Bessel). 서피스 격자가 모델좌표의 주인이므로 **지형 PRJ**를 쓴다.
노선 PRJ는 shapefile 세트 폴더(`input/shp/`)에 있어 여기서 섞이지 않는다.
`input/prj/`에도 PRJ가 여럿 쌓인다 — 자료를 다시 올려도 파일명이 다르면 옛 PRJ가
남기 때문이다. 이름 정렬로 고르면 잔재를 집는다(실측 2026-08-31: 옛 `result.prj`
(5187)가 새 `용화.prj`(5176)보다 앞서 뽑혀 노선이 Y로 100,000m 어긋났고 B05
경로 계산이 400으로 실패했다). 그래서 **지금 쓰는 지형 자료(LAS·TIF·TFW)와
basename이 같은 PRJ**를 먼저 찾고, 못 찾으면 가장 최근 것을 쓴다.
"""
terrain = sorted(project_root.glob("B03_FileInput/input/prj/*.prj"))
if terrain:
return terrain[0]
prj_dir = project_root / "B03_FileInput" / "input" / "prj"
candidates = sorted(prj_dir.glob("*.prj")) if prj_dir.is_dir() else []
if candidates:
if len(candidates) == 1:
return candidates[0]
input_root = project_root / "B03_FileInput" / "input"
terrain_stems = {
path.stem
for pattern in _TERRAIN_DATA_GLOBS
for path in input_root.glob(pattern)
if path.is_file()
}
paired = [path for path in candidates if path.stem in terrain_stems]
pool = paired or candidates
chosen = max(pool, key=lambda path: path.stat().st_mtime)
if not paired:
logger.warning(
"지형 자료와 짝이 되는 PRJ를 찾지 못해 가장 최근 PRJ를 씁니다: %s", chosen.name
)
return chosen
remainder = sorted(project_root.glob("B03_FileInput/**/*.prj"))
# 노선 세트 폴더의 PRJ는 노선 좌표계라 프로젝트 좌표계가 될 수 없다.
remainder = [path for path in remainder if path.parent.name != "shp"] or remainder
return remainder[0] if remainder else None