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:
2026-10-29 16:59:11 +09:00
co-authored by Claude Opus 5.5
parent 7d4a94a887
commit 84f243cee1
9 changed files with 262 additions and 25 deletions
@@ -1,11 +1,13 @@
/* =============================================================================
* D03_PlanAnalysis_UI_LayerDefs.ts
* D03 바탕 겹 정의(PLAN 569-2 · 581-1 · 581-2 · 584-1) — 겹 단추 묶음 · 처음 값 · 보호구역 · 토양형 색 ·
* 선 겹(단층 · 지질구조선 · 0차곡) 꼴 · 면 겹 채우기 · 선 긋기 · 그림 겹 놓기.
* 산사태위험지도 범례 · 선 겹(단층 · 지질구조선 · 0차곡) 꼴 · 켬끔 취향 읽기 · 면 겹 채우기 · 선 긋기 · 그림 겹 놓기.
* 부르는 곳 = `D03_PlanAnalysis_UI_Layers.ts`(받기 · 그리기 차례 · 범례).
* ========================================================================== */
import type { IconName } from "@ui/ui_template_icons";
import type { LegendItem } from "@ui/ui_template_flow_legend";
import { readByKey } from "../A00_Common/b_page_state";
import {
drawPreparedLayer,
normalizedToScreen,
@@ -27,6 +29,7 @@ export type PlanLayerKey =
| "cadastral"
| "zones"
| "soils"
| "landslide"
| "faults"
| "lineaments"
| "zeroOrder"
@@ -74,6 +77,12 @@ export const GROUPS: readonly (readonly LayerDef[])[] = [
title: "산림입지도(토양형 · 산림청 산림입지토양도 · 참고용)",
on: false,
},
{
key: "landslide",
icon: "alert",
title: "산사태위험지도(1~5 등급 · 행안부 생활안전지도 · 참고용)",
on: false,
},
{ key: "faults", icon: "bolt", title: "단층(브이월드 지질 · 참고용)", on: false },
{
key: "lineaments",
@@ -109,6 +118,11 @@ export const ZONE_COLORS: Record<string, string> = {
wildlife: "--draw-map-zone-wildlife",
greenbelt: "--draw-map-zone-greenbelt",
};
/** 산사태위험지도 범례(1 등급 = 가장 위험). */
export const LANDSLIDE_LEGEND: LegendItem[] = [1, 2, 3, 4, 5].map((grade) => ({
label: `${grade}등급`,
color: `var(--draw-map-landslide-${grade})`,
}));
/** 선 겹 — 이름 · 그림 꼴(서버 `LINE_KINDS` 열쇠 = `kind` · 없으면 0차곡). 새 선 겹 = 여기 한 줄. */
export type LineLayerKey = "faults" | "lineaments" | "zeroOrder";
export const LINE_LAYERS: Record<
@@ -213,3 +227,19 @@ export function drawRaster(
const [right, bottom] = map.toScreen([x1, y0]);
context.drawImage(raster.image, left, top, right - left, bottom - top);
}
/** 켬끔 취향(`key`) — 없거나 깨지면 처음 값. */
export function readPref(key: string): Record<PlanLayerKey, boolean> {
const on = Object.fromEntries(LAYERS.map((def) => [def.key, def.on])) as Record<
PlanLayerKey,
boolean
>;
try {
const saved = JSON.parse(readByKey(key) ?? "{}") as Partial<Record<PlanLayerKey, unknown>>;
for (const def of LAYERS)
if (typeof saved[def.key] === "boolean") on[def.key] = saved[def.key] as boolean;
} catch {
// 깨진 값 = 처음 값
}
return on;
}