- 공용 부품 ui_template_gl_map(지도 세우기 · 자료 · 켬끔 · 색 다시 · 카메라) — D03 GL 껍데기를 올림 · D03 은 겹 꼴만(바뀜 없음) - TM 주인 따라가기(B04_PreProcess_UI_MapGl · logic/_MapGl_Frame) — 화면 가운데 1차 맞춤 닮음꼴 카메라 + 남는 늘림(웹 메르카토르 가로세로 0.43 %)은 지도 칸 CSS 행렬 · 칸 둘레 여유 8 px - B04 하단 지도 — 위성 조각(줌인 선명) · 백지도 · 혼합 · 도엽 · 지적 · 행정 · 등고선 겹 GL · 등고 글 · 배수유역 · 계획선 · 관 = 위 캔버스 - B05 배수유역도 — 위성 · 세부유역 채움(짝홀 다각형) · 분수령 · 도엽 GL · 번호 · 상류 세류 · 관 · 강도 = 위 캔버스 - B05 평면 노선 편집 — 바탕칠 · LAS · 도엽 등고선(간격 거름 · 1 m 선) · 하천(LAS 상자 자름) GL · 노선 둘레 띠 = 지도 칸 clip-path · 고른 등고선 · 글 · 노선 · 꼭짓점 = 위 캔버스 - 켬끔 칸(GPU · 세 화면 한 취향 aislo:pref:map.canvas) · WebGL 못 씀 · 잃음 = 옛 캔버스 + 알림 · 누름 · 끌기 판정 손 안 댐 - B04 층 단추 글 = MapLayers gisLayerLabel(지도 본체 700 줄) - 지킴 시험 test_589_2_gl_frame(카메라 대 캔버스 0.5 px · 칸 행렬 늘이기만 · 되돌림 1 mm · 상자 자르기 · 짝홀 다각형) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
166 lines
5.4 KiB
TypeScript
166 lines
5.4 KiB
TypeScript
/* =============================================================================
|
|
* 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<Key>({
|
|
parent: viewport,
|
|
specs: () => [
|
|
rasterSpec<Key>("satellite"),
|
|
{
|
|
key: "basins",
|
|
id: "basins",
|
|
type: "fill",
|
|
source: "basins",
|
|
paint: { "fill-color": ["get", "_color"] },
|
|
} as GlSpec<Key>,
|
|
...lineSpecs<Key>("basins", ["get", "_color"], 1.6),
|
|
...lineSpecs<Key>("ridge", themeColor("--map-basin-outline", "#92400e"), 1.8, {
|
|
dash: [7, 4],
|
|
}),
|
|
...DRAINAGE_LAYERS.flatMap((layer) =>
|
|
lineSpecs<Key>(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,
|
|
};
|
|
}
|