⚠ **뿌리** — `tmp/` 는 창 사이에 안 건너감(실측 확인: 상대 창이 놓은 `tmp/_sync_probe.txt` 가 시간을 두고 두 번 봐도 안 보임). 그래서 **정본(등록부 스키마)만 건너가고 그것을 읽는 시험은 안 건너가** 오늘 두 번, 같은 시험이 **연 창은 통과·받은 창은 실패**가 됐음. - `tmp/tests/*` 를 `resources/tester/` 로 **복사**(127 파일). 내용은 **한 줄도 안 고침** - `tmp/tests` 는 **남겨 둠** — 되돌릴 자리(사용자 지시) - 실행: `./venv/Scripts/python.exe -m pytest resources/tester/ -q` 옮기기 전과 **같은 수**: 617 통과 / 22 건너뜀 / 실패 0 ⇒ 이제 시험·예외·까닭이 **정본과 함께** 움직임. 오늘 세운 「예외는 정본 스키마에」와 짝임. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
117 lines
4.1 KiB
Python
117 lines
4.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""PRJ 좌표계 판별 시제 — 문자열 매칭 대신 투영 파라미터로 EPSG를 고른다.
|
|
|
|
현행 `B04_PreProcess_Engine_VWorld.get_epsg_from_prj()`는 WKT에 항상 들어 있는
|
|
`false_easting` 때문에 "EAST" 분기가 먼저 걸려 **모든 PRJ에 EPSG:5187을 준다**.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
import pytest
|
|
from pyproj import CRS
|
|
|
|
# 한국에서 실제로 들어오는 좌표계만 후보로 둔다.
|
|
CANDIDATES = (
|
|
5185, 5186, 5187, 5188, # Korea 2000 (GRS80) 서·중부·동부·동해
|
|
5179, 5178, # UTM-K (GRS80 / Bessel)
|
|
5173, 5174, 5176, 5177, # Korean 1985 (Bessel) 구 좌표계
|
|
32652, 4326,
|
|
)
|
|
_AUTHORITY = re.compile(r'AUTHORITY\["EPSG","(\d+)"\]')
|
|
|
|
|
|
def _projected(crs: CRS) -> CRS:
|
|
"""COMPD_CS(수직 CRS 결합)면 수평 성분만 꺼낸다."""
|
|
return crs.sub_crs_list[0] if crs.is_compound else crs
|
|
|
|
|
|
def _fingerprint(crs: CRS) -> tuple | None:
|
|
"""투영 지문 — (중앙자오선, 원점위도, 축척, FE, FN, 타원체 장반경)."""
|
|
crs = _projected(crs)
|
|
op = crs.coordinate_operation
|
|
if op is None:
|
|
return None
|
|
p = {q.name.lower(): q.value for q in op.params}
|
|
|
|
def get(*keys):
|
|
for k in keys:
|
|
for name, value in p.items():
|
|
if k in name:
|
|
return value
|
|
return None
|
|
|
|
return (
|
|
round(get("longitude of natural origin", "central meridian") or 0.0, 6),
|
|
round(get("latitude of natural origin") or 0.0, 6),
|
|
round(get("scale factor") or 1.0, 6),
|
|
round(get("false easting") or 0.0, 3),
|
|
round(get("false northing") or 0.0, 3),
|
|
round(crs.ellipsoid.semi_major_metre, 3),
|
|
)
|
|
|
|
|
|
def epsg_from_prj(text: str) -> int | None:
|
|
"""PRJ WKT에서 EPSG를 판별한다. 못 고르면 None (기본값으로 때우지 않는다)."""
|
|
crs = CRS.from_wkt(text)
|
|
|
|
epsg = _projected(crs).to_epsg()
|
|
if epsg:
|
|
return epsg
|
|
|
|
codes = [int(c) for c in _AUTHORITY.findall(text)]
|
|
for code in reversed(codes): # WKT 끝의 PROJCS AUTHORITY가 그 좌표계 자신이다
|
|
if code in CANDIDATES:
|
|
return code
|
|
|
|
mine = _fingerprint(crs)
|
|
if mine is None:
|
|
return None
|
|
for code in CANDIDATES:
|
|
try:
|
|
if _fingerprint(CRS.from_epsg(code)) == mine:
|
|
return code
|
|
except Exception:
|
|
continue
|
|
return None
|
|
|
|
|
|
def test_uploaded_prj_files() -> None:
|
|
upload = r"C:/Users/umsan/.claude/uploads/ab448e1d-3fa5-4f88-8e02-b26428c8dfc7"
|
|
try:
|
|
las = open(upload + "/a450e281-__.prj", encoding="utf-8").read()
|
|
route = open(upload + "/105c7734-______.__.2.2.prj", encoding="utf-8").read()
|
|
except FileNotFoundError as e:
|
|
pytest.skip(f"laptop 업로드 픽스처 없음 (환경 의존): {e.filename}")
|
|
assert epsg_from_prj(las) == 5176, epsg_from_prj(las) # AUTHORITY 태그로 판별
|
|
assert epsg_from_prj(route) == 5179, epsg_from_prj(route) # 파라미터 지문으로 판별
|
|
|
|
|
|
def test_standard_codes_round_trip() -> None:
|
|
for code in (5185, 5186, 5187, 5188, 5179, 5174, 5176, 32652):
|
|
wkt = CRS.from_epsg(code).to_wkt("WKT1_GDAL")
|
|
assert epsg_from_prj(wkt) == code, (code, epsg_from_prj(wkt))
|
|
|
|
|
|
def test_compound_vertical_prj() -> None:
|
|
"""KNGeoid24 결합 PRJ(기존 프로젝트 자료)도 수평 성분으로 판별한다."""
|
|
import pathlib
|
|
|
|
path = (
|
|
pathlib.Path(__file__).resolve().parents[2]
|
|
/ "storage/1/3/2f940d8a-2065-4cf6-8bf8-dc3f0af84e57/B03_FileInput/input/prj/result.prj"
|
|
)
|
|
# 그 프로젝트가 지워진 창에서는 늘 실패한다 — 같은 파일의 다른 검사와 같은 태도로
|
|
# 건너뛴다(2026-09-08). 자료가 있는 창에서는 그대로 판별을 검사한다.
|
|
if not path.is_file():
|
|
pytest.skip(f"프로젝트 자료 없음 (환경 의존): {path.name}")
|
|
assert epsg_from_prj(path.read_text(encoding="utf-8")) == 5187
|
|
|
|
|
|
if __name__ == "__main__":
|
|
test_uploaded_prj_files()
|
|
test_standard_codes_round_trip()
|
|
test_compound_vertical_prj()
|
|
print("OK — 3개 검사 통과")
|