- 기준지정 시점 · 종점 목록 → 「지점 목록」 하나 · [+ 지점] · 지점마다 차례 색 여섯 돌림(줄 왼 띠 · 지도 이름표 바탕)
- 저장 꼴 plan_base_json {epsg, points} · 옛 starts + ends · start · end 는 읽을 때 이어 붙임(옮김 없음) · 상한 40
- 노선분석 지점 = 핀 + 「지점 N」 · 기준지정과 같은 색 · 겹 목록 맨 끝(늘 맨 위) · 지점 누름 = 새 본선(No.0 = 그 지점) · 선 놓인 지점 = 갈래
- 공용 카카오 지도 = 휠 설정 방향 한 단(마우스 자리 고정) · 가운데 끌기 옮김 — 기준지정 · 지형 범위 창 · B08 이정표 같이
- 시험: 529-3 · 530-2 새 꼴 · 585-1 새 지킴 시험 · 낡은 B08 가짜 카카오(바탕 종류 · 사건 받기 없음) 맞춤
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
157 lines
5.8 KiB
Python
157 lines
5.8 KiB
Python
"""D02 기준 지정(PLAN 529-3 · 560-2 · 579-1 · 585-1) — 경위도 ↔ TM 변환 · 지점 목록 하나 정본 API ·
|
|
옛 시점 · 종점 꼴 이어 붙임 · 옛 한 점 꼴 감쌈 · 옛 연도 행 접기 · 저장된 좌표계 그대로.
|
|
|
|
./venv/Scripts/python.exe -m pytest resources/tester/test_529_3_plan_points.py -q
|
|
"""
|
|
|
|
import json
|
|
|
|
import pytest
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
|
|
from common_util.common_util_auth import require_company
|
|
from common_util.common_util_crs import lonlat_to_tm, tm_epsg_for_lon, tm_to_lonlat
|
|
from D02_PlanBase import D02_PlanBase_Router as router_mod
|
|
|
|
PID = "12953f5a-5ba1-42ad-a427-8ac857a58bc2"
|
|
URL = f"/api/projects/{PID}/plan/points"
|
|
|
|
|
|
# --- 좌표 변환 ---
|
|
|
|
|
|
@pytest.mark.parametrize("epsg, lon0", [(5185, 125.0), (5186, 127.0), (5187, 129.0)])
|
|
def test_origin_is_false_origin(epsg, lon0) -> None:
|
|
x, y = lonlat_to_tm(lon0, 38.0, epsg) # 원점 = (200 000, 600 000)
|
|
assert abs(x - 200000.0) < 0.001 and abs(y - 600000.0) < 0.001
|
|
|
|
|
|
@pytest.mark.parametrize("lon, lat", [(127.3851, 36.3504), (125.7, 34.1), (129.36, 36.02)])
|
|
def test_round_trip_within_1mm(lon, lat) -> None:
|
|
epsg = tm_epsg_for_lon(lon)
|
|
x, y = lonlat_to_tm(lon, lat, epsg)
|
|
back = lonlat_to_tm(*tm_to_lonlat(x, y, epsg), epsg)
|
|
assert abs(back[0] - x) < 0.001 and abs(back[1] - y) < 0.001
|
|
|
|
|
|
def test_belt_by_longitude() -> None:
|
|
assert [tm_epsg_for_lon(v) for v in (124.5, 125.999, 126.0, 127.999, 128.0, 131.8)] == [
|
|
5185,
|
|
5185,
|
|
5186,
|
|
5186,
|
|
5187,
|
|
5187,
|
|
]
|
|
|
|
|
|
# --- 지점 정본 API ---
|
|
|
|
|
|
A, B, C = (
|
|
{"lon": 127.10, "lat": 37.50},
|
|
{"lon": 127.12, "lat": 37.51},
|
|
{"lon": 127.15, "lat": 37.52},
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def api(monkeypatch):
|
|
state = {
|
|
"project": {"project_kind": "PLAN", "project_year": 2026, "project_year_end": 2027},
|
|
"rows": {},
|
|
"session": {"role": "USER", "email": "kim@forest.go.kr", "company_id": 1},
|
|
}
|
|
|
|
async def load(project_id):
|
|
return state["project"], {y: dict(r) for y, r in state["rows"].items()}
|
|
|
|
async def store_base(project_id, base):
|
|
state["project"] = {**state["project"], "plan_base_json": json.dumps(base)}
|
|
|
|
monkeypatch.setattr(router_mod, "_load", load)
|
|
monkeypatch.setattr(router_mod, "_store_base", store_base)
|
|
app = FastAPI()
|
|
app.include_router(router_mod.router)
|
|
app.dependency_overrides[require_company] = lambda: state["session"]
|
|
client = TestClient(app)
|
|
client.state = state
|
|
return client
|
|
|
|
|
|
def test_put_lists(api) -> None:
|
|
got = api.put(URL, json={"points": [A, B, C]})
|
|
assert got.status_code == 200
|
|
data = got.json()
|
|
assert data["epsg"] == 5186 and len(data["points"]) == 3
|
|
assert (data["points"][0]["x"], data["points"][0]["y"]) == pytest.approx(
|
|
lonlat_to_tm(127.10, 37.50, 5186)
|
|
)
|
|
end = data["points"][2]
|
|
assert abs(end["lon"] - C["lon"]) < 1e-8 and abs(end["lat"] - C["lat"]) < 1e-8
|
|
stored = json.loads(api.state["project"]["plan_base_json"])
|
|
assert set(stored) == {"epsg", "points"} and set(stored["points"][1]) == {"x", "y"}
|
|
assert api.state["rows"] == {} # 연도 행은 D02 가 안 만듦
|
|
|
|
|
|
def test_put_partial_and_empty(api) -> None:
|
|
data = api.put(URL, json={"points": [A]}).json()
|
|
assert len(data["points"]) == 1 and data["epsg"] == 5186
|
|
data = api.put(URL, json={}).json()
|
|
assert data == {"status": "success", "epsg": None, "points": []}
|
|
assert api.put(URL, json={"points": [A] * 40}).status_code == 200
|
|
assert api.put(URL, json={"points": [A] * 41}).status_code == 422 # 상한 40(옛 20 + 20)
|
|
|
|
|
|
def test_put_keeps_stored_epsg(api) -> None:
|
|
api.put(URL, json={"points": [A, C]})
|
|
far = {"lon": 128.9, "lat": 37.5} # 평균이 128° 를 넘어도 저장된 5186 그대로
|
|
data = api.put(URL, json={"points": [A, far, far]}).json()
|
|
assert data["epsg"] == 5186
|
|
|
|
|
|
def test_get_joins_old_start_end_lists(api) -> None:
|
|
a, b, c = (lonlat_to_tm(p["lon"], p["lat"], 5186) for p in (A, B, C))
|
|
old = {"epsg": 5186, "starts": [{"x": a[0], "y": a[1]}, {"x": b[0], "y": b[1]}],
|
|
"ends": [{"x": c[0], "y": c[1]}]} # fmt: skip
|
|
api.state["project"]["plan_base_json"] = json.dumps(old)
|
|
data = api.get(URL).json()
|
|
assert [p["x"] for p in data["points"]] == pytest.approx([a[0], b[0], c[0]]) # 시점 뒤 종점
|
|
|
|
|
|
def test_get_wraps_old_single_points(api) -> None:
|
|
a, c = (lonlat_to_tm(p["lon"], p["lat"], 5186) for p in (A, C))
|
|
old = {"epsg": 5186, "start": {"x": a[0], "y": a[1]}, "end": {"x": c[0], "y": c[1]}}
|
|
api.state["project"]["plan_base_json"] = json.dumps(old)
|
|
data = api.get(URL).json()
|
|
assert [p["x"] for p in data["points"]] == pytest.approx([a[0], c[0]])
|
|
old["end"] = None
|
|
api.state["project"]["plan_base_json"] = json.dumps(old)
|
|
assert [p["x"] for p in api.get(URL).json()["points"]] == pytest.approx([a[0]])
|
|
|
|
|
|
def test_put_forest_only(api) -> None:
|
|
api.state["session"] = {"role": "USER", "email": "lee@company.com", "company_id": 1}
|
|
assert api.put(URL, json={"points": [A, B]}).status_code == 403
|
|
api.state["session"] = {"role": "SYSTEM_ADMIN", "email": "admin@x.com", "company_id": None}
|
|
assert api.put(URL, json={"points": [A, B]}).status_code == 200
|
|
|
|
|
|
def test_get_folds_old_year_rows(api) -> None:
|
|
epsg = 5186
|
|
a, b, c = (lonlat_to_tm(p["lon"], p["lat"], epsg) for p in (A, B, C))
|
|
for year, s, e in ((2026, a, b), (2027, b, c)):
|
|
api.state["rows"][year] = {
|
|
"year": year,
|
|
**{"start_x": s[0], "start_y": s[1], "end_x": e[0], "end_y": e[1]},
|
|
"epsg": epsg,
|
|
}
|
|
data = api.get(URL).json()
|
|
assert data["epsg"] == epsg
|
|
assert [p["x"] for p in data["points"]] == pytest.approx([a[0], c[0]])
|
|
|
|
|
|
def test_get_empty_new_project(api) -> None:
|
|
assert api.get(URL).json() == {"status": "success", "epsg": None, "points": []}
|