feat(D03 겹): 노선분석 지도 산사태위험지도 겹 — 행안부 생활안전지도 WMS · 키 없으면 그 겹만 꺼짐 (PLAN 584-1 · 요구 444)
- 서버: IF_0046_WMS 겹 A2SM_SANSATAI 를 받은 범위 경위도 상자로 한 장(2 m/px · 한 변 2,048) → 작업 좌표계 상자로 옮겨 그림 · 덮으면 저장본 · 키 없음 = 503 「키 없음」 · 받기 실패 · 그림 아님 = 502 - 키 = .env SAFEMAP_API_KEY 새 칸(값 비움) - 화면: 단추 하나(alert) · 묶음 셋째 · 처음 끔 · 바탕 캔버스 · 범례 1~5 등급(--draw-map-landslide-1~5) - 켬끔 취향 읽기를 LayerDefs 로 옮김(700줄 한도) - test_553_4: 겹 목록에 landslide 더함(새 겹 · 기대 목록 갱신) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -67,6 +67,7 @@ def test_d03_parts():
|
||||
("cadastral", "false"),
|
||||
("zones", "false"),
|
||||
("soils", "false"),
|
||||
("landslide", "false"),
|
||||
("faults", "false"),
|
||||
("lineaments", "false"),
|
||||
("zeroOrder", "false"),
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
"""D 노선분석 GIS 겹 더함 2(PLAN 584-1) — 단층 · 지질구조선 잘라 가닥 · 길이 · 실패 = 오류 ·
|
||||
덮으면 다시 안 받음 · 0차곡 흐름누적(웅덩이 · 평지 넘김) · 임계 · 선 뽑기 · 저장본 · 화면 겹 정의.
|
||||
덮으면 다시 안 받음 · 산사태위험지도(키 없음 · 그림 아님 = 오류 · 옮겨 그림 · 덮으면 저장본) ·
|
||||
0차곡 흐름누적(웅덩이 · 평지 넘김) · 임계 · 선 뽑기 · 저장본 · 화면 겹 정의.
|
||||
|
||||
./venv/Scripts/python.exe -m pytest resources/tester/test_584_1_plan_geo_lines.py -q
|
||||
"""
|
||||
|
||||
import base64
|
||||
import io
|
||||
import json
|
||||
import math
|
||||
import re
|
||||
@@ -13,6 +16,7 @@ import numpy as np
|
||||
import pytest
|
||||
|
||||
from common_util.common_util_crs import tm_to_lonlat
|
||||
from D03_PlanAnalysis import D03_PlanAnalysis_Landslide as landslide
|
||||
from D03_PlanAnalysis import D03_PlanAnalysis_Layers as layers
|
||||
from D03_PlanAnalysis import D03_PlanAnalysis_ZeroOrder as zo
|
||||
|
||||
@@ -81,6 +85,60 @@ def test_line_kinds_vworld_names() -> None:
|
||||
assert layers.LINE_KINDS["lineaments"][1] == "lt_l_gimslinea"
|
||||
|
||||
|
||||
# ── 산사태위험지도 ──
|
||||
|
||||
|
||||
def _png(width: int, height: int, rgba: tuple[int, int, int, int]) -> bytes:
|
||||
from PIL import Image
|
||||
|
||||
buffer = io.BytesIO()
|
||||
Image.new("RGBA", (width, height), rgba).save(buffer, format="PNG")
|
||||
return buffer.getvalue()
|
||||
|
||||
|
||||
def test_landslide_no_key(tmp_path, monkeypatch) -> None:
|
||||
monkeypatch.setenv(landslide.LANDSLIDE_KEY_ENV, " ")
|
||||
with pytest.raises(landslide.MissingKeyError, match="키 없음"):
|
||||
landslide.ensure_landslide(tmp_path, BOX, EPSG)
|
||||
assert not list(tmp_path.iterdir())
|
||||
|
||||
|
||||
def test_landslide_not_image(tmp_path, monkeypatch) -> None:
|
||||
class Reply(io.BytesIO):
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc) -> None:
|
||||
pass
|
||||
|
||||
monkeypatch.setenv(landslide.LANDSLIDE_KEY_ENV, "k")
|
||||
monkeypatch.setattr(landslide.urllib.request, "urlopen", lambda *a, **k: Reply(b"<xml/>"))
|
||||
with pytest.raises(RuntimeError, match="그림이 아닌"):
|
||||
landslide.ensure_landslide(tmp_path, BOX, EPSG)
|
||||
assert not list(tmp_path.iterdir())
|
||||
|
||||
|
||||
def test_landslide_warp_and_saved(tmp_path, monkeypatch) -> None:
|
||||
from PIL import Image
|
||||
|
||||
calls = []
|
||||
|
||||
def fake(bounds, width, height):
|
||||
calls.append((width, height))
|
||||
return _png(width, height, (220, 38, 38, 200))
|
||||
|
||||
monkeypatch.setattr(landslide, "request_wms", fake)
|
||||
value = landslide.ensure_landslide(tmp_path, BOX, EPSG)
|
||||
assert calls == [(1000, 500)] # 2 m/px
|
||||
assert value["box_m"] == list(BOX)
|
||||
raw = base64.b64decode(value["image"].split(",", 1)[1])
|
||||
image = np.asarray(Image.open(io.BytesIO(raw)))
|
||||
assert image.shape == (500, 1000, 4)
|
||||
assert tuple(image[250, 500]) == (220, 38, 38, 200) # 가운데 = 받은 색
|
||||
inner = (230100.0, 450100.0, 231000.0, 450900.0)
|
||||
assert landslide.ensure_landslide(tmp_path, inner, EPSG) == value and len(calls) == 1
|
||||
|
||||
|
||||
# ── 0차곡 ──
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user