Files
Aislo/resources/tester/test_530_1_plan_terrain.py
T
eomsangdonandClaude Opus 5.5 17534a01ad feat(530-1 · 530-2): D 예정노선 지형 받기 · 2단계 분석 서버 — B05 엔진 인자 셋(models_dir · cache_dir · 복도 반폭) · D03 지형 서비스 · 분석 · API
- B05 지형 로더 models_dir · cache_dir=None · 비용면 cache_dir=None · 탐색 복도 corridor_m(기본 100 m · 기본이면 묶음 열쇠 그대로)
- D03_PlanAnalysis_Terrain — 지점 + 1 km 표본 3×3 도엽 · prune 없음 · 병합 옛 ∪ 새 · 위성 합집합 · 연도별 models_<연도> DEM · plan.prj
- D03_PlanAnalysis_Analyze — 연도 구간마다 갈래 넷 · 법정 기준 · 누가 이어 셈 · 파생 저장본(입력 지문)
- D03_PlanAnalysis_Router — POST plan/terrain · plan/analyze(뒤에서 · 진행률) · GET plan/progress · plan/analysis · PUT [저장]
- 지킴 시험 test_530_1_plan_terrain · test_530_2_plan_analyze · test_530_2_plan_router

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:38:40 +09:00

116 lines
5.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""D 예정노선 지형 받기(PLAN 530-1) — 가짜 내려받기 · 옛 도엽 유지 · 새 도엽만 · 빠짐 없음.
./venv/Scripts/python.exe -m pytest resources/tester/test_530_1_plan_terrain.py -q
"""
import json
import math
from pathlib import Path
import pytest
from pyproj import Transformer
from B04_PreProcess import B04_PreProcess_Engine_SheetStore as store_mod
from B04_PreProcess.B04_PreProcess_Engine_MapSheet import sheets_for_points
from common_util.common_util_crs import resolve_project_crs
from config.config_system_terrain import SHEET_SURFACE_MARGIN_M
from D03_PlanAnalysis import D03_PlanAnalysis_Terrain as terrain
EPSG = 5186
A, B, C = (230000.0, 450000.0), (231500.0, 451200.0), (238000.0, 455000.0)
@pytest.fixture
def fake(tmp_path, monkeypatch):
seen = {"download": [], "merge": [], "satellite": [], "dem": []}
def download_sheet(store_dir, sheet_no, version=2):
seen["download"].append(sheet_no)
store_dir = Path(store_dir)
store_dir.mkdir(parents=True, exist_ok=True)
(store_dir / f"{sheet_no}.zip").write_bytes(b"PK")
index = store_mod._load_index(store_dir)
index["sheets"][sheet_no] = {"file": f"{sheet_no}.zip"}
store_mod._save_index(store_dir, index)
def merge(store_dir, sheets, output_dir, layers=None):
seen["merge"].append(list(sheets))
(output_dir / "도엽_등고선.geojson").write_text("{}", encoding="utf-8")
return {}
def satellite(prj, bounds, output_dir, layer_name="Satellite", ext="jpeg"):
seen["satellite"].append(bounds)
meta = {"x_min": bounds["x"][0], "x_max": bounds["x"][1]}
meta.update({"y_min": bounds["y"][0], "y_max": bounds["y"][1]})
(output_dir / "vworld_satellite_meta.json").write_text(json.dumps(meta), "utf-8")
return output_dir / "vworld_satellite.png"
def dem(root, processed, models_dir, route_xy, crs, methods=None):
seen["dem"].append((models_dir.name, route_xy.tolist(), crs))
return [{"model_file_path": f"B04_PreProcess/{models_dir.name}/dtm.npz"}]
monkeypatch.setattr(store_mod, "download_sheet", download_sheet)
monkeypatch.setattr(terrain, "parse_and_merge_sheets", merge)
monkeypatch.setattr(terrain, "download_vworld_satellite_map", satellite)
monkeypatch.setattr(terrain, "build_sheet_surface_model", dem)
return tmp_path, seen
def _zips(root):
return sorted(p.stem for p in store_mod.get_project_map_sheets_dir(root).glob("*.zip"))
def test_more_points_keep_old_sheets_and_fetch_only_new(fake) -> None:
root, seen = fake
first = terrain.ensure_plan_terrain(root, [{"year": 2026, "start": A, "end": B}], EPSG)
old = _zips(root)
assert sorted(seen["download"]) == old == sorted(first["sheets"]["downloaded"])
assert first["satellite"] == "done" and list(first["years"]) == [2026]
# 작업 좌표계 .prj — B 좌표계 창구가 읽음
assert resolve_project_crs(root) == f"EPSG:{EPSG}"
seen["download"].clear()
segs = [{"year": 2026, "start": A, "end": B}, {"year": 2027, "start": B, "end": C}]
second = terrain.ensure_plan_terrain(root, segs, EPSG)
now = _zips(root)
assert set(old) <= set(now) # 옛 도엽 그대로(prune 없음)
assert sorted(seen["download"]) == sorted(set(now) - set(old)) # 새 도엽만 받음
assert seen["merge"][-1] == now # 병합 = 옛 ∪ 새
# 위성 = 옛 범위 ∪ 새 범위
first_box, union = seen["satellite"][0], seen["satellite"][-1]
assert union["x"][0] <= first_box["x"][0] and union["x"][1] >= first_box["x"][1]
assert union["y"][0] <= first_box["y"][0] and union["y"][1] >= first_box["y"][1]
assert [d[0] for d in seen["dem"][-2:]] == ["models_2026", "models_2027"]
assert second["sheets"]["failed"] == {}
# 같은 지점 다시 — 받기 0 · 위성 건너뜀 · 병합 열쇠 같음
seen["download"].clear()
merges = len(seen["merge"])
third = terrain.ensure_plan_terrain(root, segs, EPSG)
assert seen["download"] == [] and third["satellite"] == "skip"
assert len(seen["merge"]) == merges
def test_far_points_leave_no_gap(fake) -> None:
root, _ = fake
far = (A[0] + 9000.0, A[1] + 7000.0) # 11 km — 3×3 두 벌이 안 닿는 거리
got = terrain.ensure_plan_terrain(root, [{"year": 2026, "start": A, "end": far}], EPSG)
to_wgs = Transformer.from_crs(f"EPSG:{EPSG}", "EPSG:4326", always_xy=True)
on_line = []
for i in range(201):
lon, lat = to_wgs.transform(
A[0] + (far[0] - A[0]) * i / 200, A[1] + (far[1] - A[1]) * i / 200
)
on_line.append((lat, lon))
assert set(sheets_for_points(on_line)) <= set(got["sheets"]["merged"])
def test_dem_outline_covers_corridor() -> None:
box = terrain.dem_outline(A, B)
reach = terrain.PLAN_CORRIDOR_M + terrain.DEM_EXTRA_M
assert box[0][0] - SHEET_SURFACE_MARGIN_M <= min(A[0], B[0]) - reach + 1e-9
assert box[1][1] + SHEET_SURFACE_MARGIN_M >= max(A[1], B[1]) + reach - 1e-9
samples = terrain.segment_samples(A, C)
steps = [math.dist(p, q) for p, q in zip(samples, samples[1:])]
assert samples[0] == A and samples[-1] == C and max(steps) <= terrain.SAMPLE_STEP_M