Files
Aislo/resources/tester/test_581_2_plan_soils.py
T
eomsangdonandClaude Opus 5.5 8793b4f6b2 feat(D03): 노선분석 바탕 MapLibre 전체 적용 — 겹 스물 · 등고 글 · 지명 GL · 처음 = 켬 · WebGL 못 쓰면 옛 캔버스 (PLAN 586-2 ①②③⑤ · 요구 448)
- GL 겹 스물: 위성 조각 · 음영 · 급경사 · 산사태위험(image) · 산림입지 · 보호구역 · 띠 · 물 면 · 건물(fill + 테두리)
  · 지적 · 등고선 · 도로 · 임도 짐작 · 올린 임도망도 · 세류 · 단층 · 지질구조선 · 0차곡 · 산사태취약 필지(line · 점선 px 그대로)
  · 산사태취약 점 · 지명 점(circle) — 색 · 굵기 · 투명도 · 차례 = 옛 캔버스 그대로
- 음영 = 위성 위 「곱하기 0.75」와 같은 셈의 검은 반투명 그림(밝기 g → 투명도 0.75·(1−g)) · 위성 끔 = 그대로
- 등고 수치 · 지명 = symbol(겹치면 뺌 · 등고 글은 선 따라) · 글꼴 = 화면 Noto Sans KR(OFL · 자체 보관)을 그 자리에서 그림
  (꼴에 글꼴 주소 없음 = 글꼴 조각 파일 없음 · 받기 전에 그 글자 글꼴 미리 받음)
- 카메라 주인 = D03 화면 view · 돌림 하나 — 휠 · 가운데 끌기 · 돌림 · 맞춤 · 단축키 모두 draw() → glCamera
- 처음 = GL(aislo:pref:d03.canvas = "1" 이면 옛 캔버스) · 겹 단추줄 끝 단추로 바꿈 · WebGL 못 열거나 잃음 = 저절로 옛 캔버스 + 알림
- GL 이면 캔버스 바탕 겹은 쉼(등고 주곡선 간격 셈만) · GL 이면 위성 한 장 그림 안 받음
- 산사태위험 그림도 경위도 귀퉁이(lonlat_corners 한 곳) · 묶음 꼴 표시 없는 합친 자료도 GL 이 받게 다시 쌈
- 캔버스 그리기 목록 = 새 D03_PlanAnalysis_UI_LayerPaint.ts(700줄 규칙) · 검증 창구 layers().gl = GL 겹별 그린 지물 수
- 잼(headless Edge 실 GPU · 검증_sub2_D · 겹 18 켬 = 자료 있는 겹 전부): 줌아웃 화면 간격 중앙 58.4 → 4.2 ms · 끊김 27 → 0/30
  · 끌기 62.5 → 4.2 ms · 끊김 37 → 0/40 · 마우스 4.2 ms 끊김 0 그대로 · 그은 선 대 GL 0.019 px(돌림 30° 0.019 · 확대 0.077)
- 시험: 캔버스 그리기를 옮긴 자리로 지킴 시험 넷 고침(581-2 · 582 · 584-1 · 584-3 — 그리기 차례 · 색 값이 LayerPaint 로)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 20:28:13 +09:00

100 lines
4.6 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 노선분석 GIS 겹 「다음」 묶음(PLAN 581-2) — 산림입지도 잘라 넓이 · 토양형 코드 차례
이름 없으면 글자 그대로 · 실패 = 오류(파일 안 씀) · 덮으면 다시 안 받음 · 화면 겹 정의 · 색 값.
./venv/Scripts/python.exe -m pytest resources/tester/test_581_2_plan_soils.py -q
"""
import json
import re
from pathlib import Path
import pytest
from common_util.common_util_crs import tm_to_lonlat
from D03_PlanAnalysis import D03_PlanAnalysis_Layers as layers
EPSG = 5186
BOX = (230000.0, 450000.0, 232000.0, 451000.0) # 2 × 1 km
ROOT = Path(__file__).resolve().parents[2]
def _square(x0: float, y0: float, side: float) -> dict:
ring = [(x0, y0), (x0 + side, y0), (x0 + side, y0 + side), (x0, y0 + side), (x0, y0)]
return {"type": "Polygon", "coordinates": [[list(tm_to_lonlat(x, y, EPSG)) for x, y in ring]]}
def _soil(gid: str, geometry: dict, cd: str, code: str, name: str | None) -> dict:
props = {"sltp_cd": cd, "toyanghyun": code, **({"name": name} if name else {})}
return {"id": gid, "geometry": geometry, "properties": props}
def test_clip_soils_area_order_names() -> None:
found = [
_soil("a", _square(230100.0, 450100.0, 100.0), "03", "B₃", "갈색적윤산림토양"),
_soil("b", _square(231800.0, 450800.0, 400.0), "01", "B₁", "갈색건조산림토양"), # 귀퉁이
_soil("b", _square(231800.0, 450800.0, 400.0), "01", "B₁", "갈색건조산림토양"), # 겹침
_soil("c", _square(230500.0, 450500.0, 100.0), "03", "B₃", "갈색적윤산림토양"),
_soil("d", _square(230800.0, 450100.0, 50.0), "99", "Xx", None), # 모르는 값
_soil("e", _square(240000.0, 460000.0, 100.0), "02", "B₂", "갈색약건산림토양"), # 밖
]
features, summary = layers.clip_soils(found, BOX, EPSG)
assert [row["code"] for row in summary] == ["B₁", "B₃", "Xx"]
assert summary[0]["area_m2"] == pytest.approx(200 * 200, rel=0.01)
assert summary[1] == {
"code": "B₃",
"name": "갈색적윤산림토양",
"count": 2,
"area_m2": pytest.approx(2 * 100 * 100, rel=0.01),
}
assert summary[2]["name"] == "Xx" # 이름 없으면 글자 그대로
assert len(features) == 4
assert {f["properties"]["code"] for f in features} == {"B₁", "B₃", "Xx"}
class FakeWfs:
def __init__(self, fail: bool = False) -> None:
self.fail, self.calls = fail, []
def __call__(self, typename, filename, bounds, output_dir) -> None:
self.calls.append(typename)
if self.fail:
return
feats = [_soil("a", _square(230500.0, 450200.0, 300.0), "01", "B₁", "갈색건조산림토양")]
body = {"type": "FeatureCollection", "features": feats}
(Path(output_dir) / filename).write_text(json.dumps(body), "utf-8")
def test_soils_fetch_once(tmp_path, monkeypatch) -> None:
fake = FakeWfs()
monkeypatch.setattr(layers, "request_vworld_wfs", fake)
got = layers.ensure_soils(tmp_path, BOX, EPSG)
assert got["fetched"] and fake.calls == [layers.SOIL_TYPENAME]
saved = json.loads((tmp_path / layers.SOIL_FILE).read_text("utf-8"))
assert saved["summary"][0]["count"] == 1 and len(saved["features"]) == 1
inner = (230500.0, 450100.0, 231000.0, 450900.0)
assert not layers.ensure_soils(tmp_path, inner, EPSG)["fetched"] # 덮으면 다시 안 받음
assert len(fake.calls) == 1
assert layers.ensure_soils(tmp_path, (229000.0, *BOX[1:]), EPSG)["fetched"] # 넓어지면 다시
def test_soils_fail_raises(tmp_path, monkeypatch) -> None:
monkeypatch.setattr(layers, "request_vworld_wfs", FakeWfs(fail=True))
with pytest.raises(RuntimeError):
layers.ensure_soils(tmp_path, BOX, EPSG)
assert not (tmp_path / layers.SOIL_FILE).exists()
def test_screen_def_and_colors() -> None:
defs = (ROOT / "D03_PlanAnalysis" / "D03_PlanAnalysis_UI_LayerDefs.ts").read_text("utf-8")
entry = re.search(r'key: "soils",\s*icon: "(\w+)",[^}]*on: (\w+)', defs)
assert entry and entry.group(2) == "false" # 처음 값 끔
icons = (ROOT / "ui_template" / "ui_template_icons.ts").read_text("utf-8")
assert re.search(rf"^\s+{entry.group(1)}:", icons, re.M)
theme = (ROOT / "ui_template" / "ui_template_theme.css").read_text("utf-8")
assert all(f"--draw-map-soil-{n}:" in theme for n in range(1, 7))
page = (ROOT / "D03_PlanAnalysis" / "D03_PlanAnalysis_UI_LayerPaint.ts").read_text(
"utf-8"
) # 586-2
assert "if (!on.soils || !data.soils) return;" in page
assert page.count("base: true,") == page.count("draw: (context, map) =>") # 겹 모두 바탕(582)