/* ============================================================================= * B05_Profile_UI_Drainage_Gl.ts * 배수유역도 GL 바탕(PLAN 589-2) — 위성(조각 · 줌인 선명) · 세부유역 채움 · 분수령 · 도엽(등고선 · 하천)을 * MapLibre 로(캔버스와 같은 차례 · 색). 유역 번호 · 상류 세류 · 노선 · 관 · 강도 · 화살표 · 띠 · 측점 = * 위 캔버스 그대로 · 고르기 판정 그대로. 판(`_Drainage_Panel`)은 만들기 · 자료 · 그리기 때 부름만. * ========================================================================== */ import { createToggle } from "@ui/ui_template_toggle"; import { themeColor } from "@ui/ui_template_palette"; import { createTmGl, GL_TOGGLE_TITLE, lineSpecs, rasterSpec, satelliteData, } from "../B04_PreProcess/B04_PreProcess_UI_MapGl"; import { normalizedToScreen, type GeoJsonCollection, type ViewState, } from "../B04_PreProcess/B04_PreProcess_UI_MapRender"; import { ringsToPolygons } from "../B04_PreProcess/logic/B04_PreProcess_UI_MapGl_Frame"; import { basinColor, DRAINAGE_LAYERS, layerColor, type DrainageLayer, } from "./B05_Profile_UI_Drainage_Parts"; import type { GlSpec } from "@ui/ui_template_gl_map"; import type { DrainageScene } from "./B05_Profile_UI_Drainage_Render"; type Key = "satellite" | "basins" | "ridge" | DrainageLayer; export interface DrainageGl { /** 그리기마다 — 켜져 있으면 바탕 겹을 맞추고 참(캔버스는 그 겹을 쉼). */ draw: ( view: ViewState, scene: Pick< DrainageScene, "meta" | "normalizer" | "basins" | "selectedBasin" | "mainBoundary" | "activeLayers" >, satellite: boolean, ) => boolean; setLayer: (layer: DrainageLayer, data: GeoJsonCollection | null) => void; setProject: (projectId: string | null) => void; dispose: () => void; } export function createDrainageGl( viewport: HTMLElement, buttons: HTMLElement, backgroundImage: HTMLImageElement, redraw: () => void, ): DrainageGl { const toggle = createToggle({ kind: "chip", label: "GPU", size: 20, onChange: () => tm.toggle(), }); toggle.root.title = GL_TOGGLE_TITLE; toggle.root.dataset.layer = "gl"; toggle.root.classList.add("b05-drainage__layer-button"); buttons.append(toggle.root); const mark = (on: boolean): void => { toggle.set(on); toggle.root.classList.toggle("is-active", on); }; const tm = createTmGl({ parent: viewport, specs: () => [ rasterSpec("satellite"), { key: "basins", id: "basins", type: "fill", source: "basins", paint: { "fill-color": ["get", "_color"] }, } as GlSpec, ...lineSpecs("basins", ["get", "_color"], 1.6), ...lineSpecs("ridge", themeColor("--map-basin-outline", "#92400e"), 1.8, { dash: [7, 4], }), ...DRAINAGE_LAYERS.flatMap((layer) => lineSpecs(layer, layerColor(layer), layer === "도엽_등고선" ? 0.7 : 1.5, { marker: "dot", }), ), ], themeNames: ["--color-surface", "--map-basin-outline", "--map-sheet-contour", "--map-basin-1"], onChange: (on) => { mark(on); redraw(); }, }); mark(tm.on()); let shown: unknown[] = []; return { draw(view, frame, satellite) { backgroundImage.hidden = !satellite || tm.on(); if (!tm.on()) return false; const { gl } = tm; gl.setVisible("satellite", satellite); gl.setVisible("basins", true); gl.setVisible("ridge", true); for (const layer of DRAINAGE_LAYERS) gl.setVisible(layer, frame.activeLayers.has(layer)); const key = [ frame.basins, frame.selectedBasin, frame.mainBoundary, themeColor("--map-basin-1", ""), ]; if (key.some((value, index) => value !== shown[index])) { shown = key; gl.setData("basins", { features: frame.basins.map((basin) => { const color = basinColor(basin.index); const rings = basin.polygon_rings_lonlat?.length ? basin.polygon_rings_lonlat : [basin.polygon_lonlat]; return { type: "Feature", properties: { // 하나를 고르면 나머지는 옅게(캔버스와 같은 셈) _color: frame.selectedBasin === null || frame.selectedBasin === basin.index ? color : color.replace(/0\.45\)$/, "0.18)"), }, geometry: { type: "MultiPolygon", coordinates: ringsToPolygons(rings) }, }; }), }); const ring = frame.mainBoundary; gl.setData( "ridge", ring.length > 2 ? { features: [ { type: "Feature", properties: {}, geometry: { type: "LineString", coordinates: [...ring, ring[0]] }, }, ], } : null, ); } tm.follow( frame.meta, frame.normalizer, (nx, ny) => normalizedToScreen(view, nx, ny), view.width, view.height, ); return true; }, setLayer: (layer, data) => tm.gl.setData(layer, data), setProject: (projectId) => tm.gl.setData("satellite", projectId ? satelliteData(projectId) : null), dispose: tm.dispose, }; }