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:
@@ -147,15 +147,11 @@ def project_epsg_from_prj(project_root: Path) -> str:
|
||||
"""프로젝트 PRJ에서 좌표계를 읽는다. 없으면 중부원점(EPSG:5186).
|
||||
|
||||
PRJ가 둘 이상 올라오므로(노선 세트 + 지형) 지형 PRJ를 고른다 — `find_project_prj`.
|
||||
판정은 작업 좌표계 창구(`resolve_project_crs`) 하나로 모았다(2026-09-03).
|
||||
"""
|
||||
from common_util.common_util_crs import find_project_prj
|
||||
from common_util.common_util_crs import resolve_project_crs
|
||||
|
||||
from .B04_PreProcess_Engine_VWorld import get_epsg_from_prj
|
||||
|
||||
prj_path = find_project_prj(project_root)
|
||||
if prj_path is not None:
|
||||
return get_epsg_from_prj(prj_path.read_text(encoding="utf-8", errors="ignore"))
|
||||
return "EPSG:5186"
|
||||
return resolve_project_crs(project_root)
|
||||
|
||||
|
||||
def map_meta_covers(meta_path: Path, extent: dict[str, list[float]]) -> bool:
|
||||
|
||||
@@ -77,19 +77,16 @@ async def _resolve_epsg(project_id: UUID, stored_path: str) -> str:
|
||||
`crs_epsg` 열은 표시용 라벨이라 그 값을 쓰면 좌표가 딴 곳으로 간다
|
||||
(2026-09-01 실측: 라벨 5179, 실제 5176 — 유입 폴리곤이 1,500km 밖에 찍혔다).
|
||||
"""
|
||||
from B04_PreProcess.B04_PreProcess_Engine_Extent import project_epsg_from_prj
|
||||
from common_util.common_util_crs import resolve_project_crs
|
||||
|
||||
project_root = Path(resolve_stored_project_path(stored_path))
|
||||
planned = load_design_route(project_root)
|
||||
if planned is not None and planned.crs_input:
|
||||
return planned.crs_input
|
||||
prj_crs = project_epsg_from_prj(project_root)
|
||||
if prj_crs:
|
||||
return prj_crs
|
||||
pool = get_db_pool()
|
||||
async with pool.acquire() as connection:
|
||||
epsg = await get_surface_crs_epsg(connection, project_id, 0)
|
||||
return f"EPSG:{epsg or 5186}"
|
||||
return resolve_project_crs(project_root, db_epsg=epsg)
|
||||
|
||||
|
||||
def _collect_inflow(
|
||||
|
||||
@@ -42,6 +42,7 @@ from common_util.common_util_route_geometry import (
|
||||
load_design_route,
|
||||
read_planned_route,
|
||||
)
|
||||
from common_util.common_util_crs import resolve_project_crs
|
||||
from common_util.common_util_storage import resolve_stored_project_path
|
||||
from common_util.common_util_surface_confirmation import get_surface_confirmation_params
|
||||
from common_util.common_util_wamis_rainfall import (
|
||||
@@ -190,7 +191,7 @@ async def _prepare(project_id: UUID) -> dict[str, Any] | JSONResponse:
|
||||
)
|
||||
|
||||
# 설계 계통과 같은 프로젝트 좌표계로 맞춘다. 도엽 재투영도 이 좌표계로 간다.
|
||||
source_crs = planned.crs_input or f"EPSG:{epsg or 5186}"
|
||||
source_crs = resolve_project_crs(project_root, route_crs_input=planned.crs_input, db_epsg=epsg)
|
||||
to_lonlat_transformer = Transformer.from_crs(source_crs, "EPSG:4326", always_xy=True)
|
||||
to_metric_transformer = Transformer.from_crs("EPSG:4326", source_crs, always_xy=True)
|
||||
|
||||
@@ -257,7 +258,13 @@ def _route_center_lonlat(stored_path: str, fallback_epsg: int | None) -> tuple[f
|
||||
if planned is None or not planned.vertices:
|
||||
return None
|
||||
middle = planned.vertices[len(planned.vertices) // 2]
|
||||
source_crs = f"EPSG:{planned.epsg or fallback_epsg or 5186}"
|
||||
# 여기 좌표는 **원본 파일 그대로**(트림 전)라 파일이 밝힌 좌표계로 읽는다 — 창구 사다리.
|
||||
source_crs = resolve_project_crs(
|
||||
Path(resolve_stored_project_path(stored_path)),
|
||||
route_crs_input=planned.crs_input,
|
||||
file_label_epsg=planned.epsg,
|
||||
db_epsg=fallback_epsg,
|
||||
)
|
||||
lon, lat = Transformer.from_crs(source_crs, "EPSG:4326", always_xy=True).transform(
|
||||
middle.x, middle.y
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user