/* ============================================================================= * map_layer_data.ts * 공용 지도 겹 자료 도움(PLAN 569-2 · 581 · 586-2 · 624-3) — D03 예정노선 · B05 평면이 같이 씀(D03 `UI_LayerDefs` 에서 옮김). * 도로 소로 · 미분류 구분 · 물 면 도엽 이름 · 넓이 글 · 그림 읽기 · GL 자료(지물 색 붙이기 · 그림 귀퉁이 · 음영 그늘 그림). * ========================================================================== */ import type { GlImage } from "@ui/ui_template_gl_map"; import { themeColor } from "@ui/ui_template_palette"; import type { GeoJsonCollection, PlanBand, PlanRaster } from "./map_layer_api"; /** 도로 중 소로 · 미분류 구분(옛 임도 짐작 · 겹 걷음 · 페이지 자료 셈만 씀). */ 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`; export const loadImage = (src: string): Promise => new Promise((done, fail) => { const image = new Image(); image.onload = () => done(image); image.onerror = () => fail(new Error("그림을 읽지 못했습니다")); image.src = src; }); /** GL 자료 — 지물마다 그릴 색(`_color`) · 덧 속성을 붙인 사본. */ export function glTagged( collection: GeoJsonCollection, color: (props: Record) => string, extra: Record = {}, ): GeoJsonCollection { return { ...collection, features: (collection.features ?? []).map((feature) => ({ ...feature, properties: { ...feature.properties, ...extra, _color: color(feature.properties ?? {}) }, })), }; } /** 띠 GL 자료(581-1 · 634-2) — 도로 300 m 먼저 · 계류 100 m 위 · `_band` = 겹 가름(계류 = `bands` · 도로 = `roadBands`). */ export function bandCollection(info: { stream: PlanBand; road: PlanBand }): GeoJsonCollection { const band = (name: "stream" | "road", fallback: string): GeoJsonCollection => glTagged(info[name].collection, () => themeColor(`--draw-map-band-${name}`, fallback), { _band: name, }); return { features: [ ...(band("road", "#facc15").features ?? []), ...(band("stream", "#22d3ee").features ?? []), ], }; } /** GL 그림 자리 — 서버 경위도 귀퉁이(왼쪽 아래부터 돎) → 왼쪽 위부터 · 없으면 null. */ export function glImage(raster: PlanRaster, url = raster.image): GlImage | null { const box = raster.box; return box?.length === 4 ? { url, corners: [box[3], box[2], box[1], box[0]] } : null; } /** 음영 그늘 그림 — 위성 위 「곱하기 0.75」와 같은 검은 반투명(밝기 g → 투명도 0.75 · (1 − g)). */ export function shadeImage(image: HTMLImageElement): string { const canvas = document.createElement("canvas"); canvas.width = image.naturalWidth; canvas.height = image.naturalHeight; const context = canvas.getContext("2d")!; context.drawImage(image, 0, 0); const pixels = context.getImageData(0, 0, canvas.width, canvas.height); const px = pixels.data; for (let i = 0; i < px.length; i += 4) { px[i + 3] = Math.round(0.75 * (255 - px[i]) * (px[i + 3] / 255)); px[i] = px[i + 1] = px[i + 2] = 0; } context.putImageData(pixels, 0, 0); return canvas.toDataURL("image/png"); }