- 서버: 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>
145 lines
5.1 KiB
TypeScript
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([]);
|
|
}
|