Files
Aislo/B05_Profile/B05_Profile_UI_Drainage_Gl.ts
T
eomsangdonandClaude Opus 5.5 21afc379e4 feat(B04·B05): 하단 지도 · 평면 노선 편집 · 배수유역도 바탕 MapLibre 적용 — 공용 GL 부품 · TM 주인 카메라 · 처음 = 켬 · WebGL 못 쓰면 옛 캔버스 (PLAN 589-2 · 요구 448)
- 공용 부품 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>
2026-10-29 21:45:41 +09:00

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,
};
}