Files
Aislo/D03_PlanAnalysis/D03_PlanAnalysis_UI_LayerDefs.ts
T
eomsangdonandClaude Opus 5.5 f29fb01dd5 feat(D03 겹): 노선분석 지도 산림입지도 겹 — 브이월드 WFS 토양형 · 묶음 셋째 · 처음 끔 (PLAN 581-2 · 요구 440-8)
- 서버: lt_c_fsdifrsts 받은 범위로 잘라 토양형 코드 · 이름 · 넓이 · 토양형별 합 · 덮으면 다시 안 받음 · 실패 = 502
- 화면: 단추 하나(잎 아이콘 · 부품 상자에 더함) · 토양형 색 --draw-map-soil-1~6 · 범례 = 토양형 이름 · ha · 바탕 캔버스
- 산사태위험지도(생활안전지도 = 공공누리 4유형 · 데이터 준비 중) · 단층 · 지질구조선(이용 조건 문서 없음)은 넣지 않음(브레인 결정 A)
- test_553_4: 겹 목록에 soils 더함(새 겹 · 기대 목록 갱신)

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

145 lines
5.1 KiB
TypeScript

/* =============================================================================
* D03_PlanAnalysis_UI_LayerDefs.ts
* D03 바탕 겹 정의(PLAN 569-2 · 581-1 · 581-2) — 겹 단추 묶음 · 처음 값 · 보호구역 · 토양형 색 · 면 겹 채우기.
* 부르는 곳 = `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"
| "soils"
| "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: "soils",
icon: "leaf",
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<string, string> = {
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 SOIL_COLORS = [1, 2, 3, 4, 5, 6].map((n) => `--draw-map-soil-${n}`);
/** 도로 중 기존 임도로 짐작하는 구분. */
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([]);
}