"""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 / "A00_Common" / "map" / "map_layer_list.ts").read_text("utf-8") # 624-2 # 634-2 표 한 줄 = row(열쇠, 아이콘, 호버제목, 묶음[, true]) · true 없음 = 처음 끔 entry = re.search(r'row\("soils", "(\w+)", "[^"]+", (?:"\w+"|null)(, true)?\)', defs) assert entry and not entry.group(2) # 처음 값 끔 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)