/* ============================================================================= * D03_PlanAnalysis_UI_LayerDefs.ts * D03 바탕 겹 정의(PLAN 569-2 · 581-1) — 겹 단추 묶음 · 처음 값 · 보호구역 색 · 면 겹 채우기. * 부르는 곳 = `D03_PlanAnalysis_UI_Layers.ts`(받기 · 그리기 차례 · 범례). * ========================================================================== */ import type { IconName } from "@ui/ui_template_icons"; import { normalizedToScreen, type PreparedLayer, type ViewState, } from "../B04_PreProcess/B04_PreProcess_UI_MapRender"; export type PlanLayerKey = | "satellite" | "hillshade" | "contour" | "slope" | "stream" | "water" | "road" | "forestRoad" | "cadastral" | "zones" | "bands" | "building" | "place"; export interface LayerDef { key: PlanLayerKey; icon: IconName; title: string; on: boolean; } /** 단추 묶음(사이 나눔선) — 바탕 · 물 · 길 · 구역. 「지금」 겹 처음 값 = 끔(`_분석_440-C` 1-4). */ export const GROUPS: readonly (readonly LayerDef[])[] = [ [ { key: "satellite", icon: "satellite", title: "위성사진", on: true }, { key: "hillshade", icon: "sun", title: "음영기복(2 m 지형 · 산줄기 · 골)", on: false }, { key: "contour", icon: "mountain", title: "도엽 등고선(굵은 선 = 계곡선 25 m)", on: true }, { key: "slope", icon: "angle", title: "급경사 색(15° · 35° · 40° 이상)", on: false }, ], [ { key: "stream", icon: "droplet", title: "세류선(가는 파랑) · 하천(굵은 파랑)", on: true }, { key: "water", icon: "ripple", title: "물 면(도엽 실폭하천 · 저수지 등)", on: false }, { key: "road", icon: "road", title: "기존 도로(도엽 도로중심선)", on: false }, { key: "forestRoad", icon: "route", title: "기존 임도 짐작(도엽 도로 소로 · 미분류 = 주황 점선 · 정식 임도망도 아님)", on: false, }, ], [ { key: "cadastral", icon: "borderAll", title: "지적도(참고용 · 법적 효력 없음)", on: false }, { key: "zones", icon: "shield", title: "보호 · 규제 구역(브이월드 여덟 종 · 참고용)", on: false, }, { key: "bands", icon: "borderOuter", title: "계류 100 m · 도로 300 m 띠(타당성평가)", on: false, }, { key: "building", icon: "building", title: "건물(도엽 · 하류 민가 · 인가)", on: false }, { key: "place", icon: "mapPin", title: "지명(도엽 계곡 · 령 · 부락)", on: false }, ], ]; export const LAYERS: readonly LayerDef[] = GROUPS.flat(); /** 보호구역 종류 → 그림 색 값(서버 `ZONE_KINDS` 열쇠). */ export const ZONE_COLORS: Record = { forest: "--draw-map-zone-forest", baekdu: "--draw-map-zone-baekdu", water: "--draw-map-zone-water", park: "--draw-map-zone-park", heritage: "--draw-map-zone-heritage", wetland: "--draw-map-zone-wetland", wildlife: "--draw-map-zone-wildlife", greenbelt: "--draw-map-zone-greenbelt", }; /** 도로 중 기존 임도로 짐작하는 구분. */ export const FOREST_ROAD_KINDS = new Set(["소로", "미분류"]); /** 물 면 = 도엽 수계(E) 면 셋. */ export const WATER_KEYS = ["실폭하천", "수계면E003", "수계면E005"]; export const hectare = (m2: number): string => `${(m2 / 10000).toFixed(1)} ha`; /** 면 겹 채움 — 지물마다 한 길(evenodd = 구멍) · 화면 밖 지물 거름(돌린 지도 귀퉁이까지 여유). */ export function fillLayer( context: CanvasRenderingContext2D, layer: PreparedLayer, view: ViewState, fill: string, alpha: number, edge?: { color: string; width: number; dash?: number[] }, ): void { const [ox, oy] = normalizedToScreen(view, 0, 0); const [ex, ey] = normalizedToScreen(view, 1, 1); const ax = ex - ox; const ay = ey - oy; const pad = Math.max(view.width, view.height) / 2; context.fillStyle = fill; if (edge) { context.strokeStyle = edge.color; context.lineWidth = edge.width; context.setLineDash(edge.dash ?? []); } for (const feature of layer.features) { if ( feature.maxX * ax + ox < -pad || feature.minX * ax + ox > view.width + pad || feature.maxY * ay + oy < -pad || feature.minY * ay + oy > view.height + pad ) continue; context.beginPath(); for (const { coords } of feature.parts) { context.moveTo(coords[0] * ax + ox, coords[1] * ay + oy); for (let i = 2; i < coords.length; i += 2) context.lineTo(coords[i] * ax + ox, coords[i + 1] * ay + oy); context.closePath(); } context.globalAlpha = alpha; context.fill("evenodd"); if (edge) { context.globalAlpha = 0.9; context.stroke(); } } context.setLineDash([]); }