Files
Aislo/B04_PreProcess/B04_PreProcess_UI_MapViewer_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

112 lines
4.0 KiB
TypeScript

/* =============================================================================
* B04_PreProcess_UI_MapViewer_Gl.ts
* B04 하단 지도 GL 바탕(PLAN 589-2) — 백지도 · 혼합(한 장 그림) · 위성(조각 · 줌인 선명) · 도엽 · 지적 · 행정 ·
* 등고선 겹을 MapLibre 로. 등고 글 · 배수유역 · 계획선 · 측점 · 흐름 강도 · 세부유역 · 관 = 위 캔버스 그대로.
* 지도 본체(`_MapViewer`)는 켬끔 · 자료 · 따라가기만 부름(700 줄).
* ========================================================================== */
import { getVWorldMapUrl, type VWorldMeta } from "./B04_PreProcess_Api_Fetch";
import {
BACKGROUND_LAYERS,
GIS_LAYERS,
gisLayerColor,
type BackgroundLayer,
type GisLayer,
} from "./B04_PreProcess_UI_MapLayers";
import { createLayerToggle } from "./B04_PreProcess_UI_LayerToggle";
import {
createTmGl,
GL_TOGGLE_TITLE,
lineSpecs,
metaCorners,
rasterSpec,
satelliteData,
} from "./B04_PreProcess_UI_MapGl";
import {
normalizedToScreen,
type GeoJsonCollection,
type Normalizer,
type ViewState,
} from "./B04_PreProcess_UI_MapRender";
type Key = BackgroundLayer | GisLayer;
const isContour = (layer: GisLayer): boolean => layer === "등고선" || layer === "도엽_등고선";
/** 캔버스와 같은 차례 — 등고선이 맨 밑. */
const ORDER = [...GIS_LAYERS].sort((a, b) => (isContour(a) ? -1 : isContour(b) ? 1 : 0));
export interface SurfaceMapGl {
/** 지금 GL 로 그리나 — 참이면 캔버스는 바탕 겹을 쉼 · 그림(img)은 숨김. */
on: () => boolean;
/** 켠 겹. */
sync: (backgrounds: ReadonlySet<BackgroundLayer>, layers: ReadonlySet<GisLayer>) => void;
/** 받은 겹(원본 경위도 GeoJSON) — null = 비움. */
setLayer: (layer: GisLayer, data: GeoJsonCollection | null) => void;
/** 프로젝트 · 메타 — 그림 · 조각 주소(null = 다 비움). */
setMeta: (projectId: string | null, meta: VWorldMeta | null) => void;
follow: (meta: VWorldMeta | null, normalizer: Normalizer | null, view: ViewState) => void;
dispose: () => void;
}
/** `buttons` = 켬끔 칩 자리 · `onChange` = 켬끔이 바뀜(그림 숨김 · 다시 그림). */
export function createSurfaceMapGl(
viewport: HTMLElement,
buttons: HTMLElement,
onChange: () => void,
): SurfaceMapGl {
const toggle = createLayerToggle({
label: "GPU",
title: GL_TOGGLE_TITLE,
onChange: () => tm.toggle(),
});
toggle.root.dataset.layer = "gl";
buttons.append(toggle.root);
const tm = createTmGl<Key>({
parent: viewport,
specs: () => [
...BACKGROUND_LAYERS.map((layer) => rasterSpec<Key>(layer)),
...ORDER.flatMap((layer) =>
lineSpecs<Key>(layer, gisLayerColor(layer), isContour(layer) ? 0.7 : 1.5, {
marker: layer === "도엽_표고점" ? "x" : "dot",
}),
),
],
themeNames: ["--color-surface", "--map-cadastral", "--map-sheet-contour", "--map-sheet-stream"],
onChange: (on) => {
toggle.set(on);
onChange();
},
});
toggle.set(tm.on());
return {
on: tm.on,
sync(backgrounds, layers) {
for (const layer of BACKGROUND_LAYERS) tm.gl.setVisible(layer, backgrounds.has(layer));
for (const layer of GIS_LAYERS) tm.gl.setVisible(layer, layers.has(layer));
},
setLayer: (layer, data) => tm.gl.setData(layer, data),
setMeta(projectId, meta) {
tm.gl.setData("satellite", projectId ? satelliteData(projectId) : null);
for (const layer of ["white", "hybrid"] as const)
tm.gl.setData(
layer,
projectId && meta
? {
url: location.origin + getVWorldMapUrl(projectId, layer),
corners: metaCorners(meta),
}
: null,
);
if (!meta) for (const layer of GIS_LAYERS) tm.gl.setData(layer, null);
},
follow: (meta, normalizer, view) =>
tm.follow(
meta,
normalizer,
(nx, ny) => normalizedToScreen(view, nx, ny),
view.width,
view.height,
),
dispose: tm.dispose,
};
}