- D03_Api 겹 받기 → map_layer_api.ts(D03_Api 가 다시 내놓음) - UI_SlopeBands · logic SlopeBands · UI_SatLight → map_slope_bands · logic/map_slope_bands · map_sat_light - 범례 줄 legendRows → map_legend.ts(받은 자료 꼴 LegendData · D03 LayerLegend 가 다시 내놓음) - glImage · glTagged · shadeImage · loadImage · WATER_KEYS · FOREST_ROAD_KINDS · hectare → map_layer_data.ts - 시험 경로 고침 — test_603(TS 자리) · test_606_1(밝기 파일) · test_584_3(범례 글 = 공용 파일도 읽음) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
116 lines
5.1 KiB
TypeScript
116 lines
5.1 KiB
TypeScript
/* =============================================================================
|
|
* D03_PlanAnalysis_UI_MapGl.ts
|
|
* D03 GL 바탕(PLAN 586-1 · 586-2 · 624-2 · 요구 445 B · 448) — 바탕 겹 스물을 MapLibre(WebGL · BSD-3)로.
|
|
* 겹 꼴 · 차례 = 공용 `A00_Common/map/map_layer_specs`(624-2 · B05 평면과 같은 꼴) · 여기는 D03 지도 세우기만:
|
|
* 고정 소스(위성 조각 z6~19 · 등고선 615 줌별 벡터 조각 · 산림 조각 다섯 622-2) · 받는 동안 서클 · 임도망 고름 · 위성 밝기.
|
|
* 글 = 화면 Noto Sans KR(OFL · 자체 보관)을 그 자리에서 그림 — 꼴에 글꼴 주소가 없으면 MapLibre 가 모든 글자를 화면 글꼴로.
|
|
* 그은 선 · 재기 · 고르기 · 누름 = 위 캔버스 그대로(위에 겹침).
|
|
* 카메라 주인 = D03 화면(`ViewState` · 돌림) 하나 — 여기는 따라가기만 · 조작은 안 받음.
|
|
* 자료 = 지금 받는 GeoJSON 그대로(조각내기는 MapLibre 일꾼 스레드) · 색 = 받는 쪽이 지물에 붙인 `_color`.
|
|
* 그래픽 카드 · WebGL 이 없거나 잃으면 `onFail` — 부르는 쪽이 옛 캔버스로.
|
|
* 지도 세우기 · 자료 올리기 · 카메라 = 공용 부품 `ui_template_gl_map`(589-2).
|
|
* ========================================================================== */
|
|
|
|
import type { Map as MapLibreMap } from "maplibre-gl";
|
|
import {
|
|
contourTileSource,
|
|
createGlMap,
|
|
GL_FONT,
|
|
satelliteTileSource,
|
|
type GlImage,
|
|
} from "@ui/ui_template_gl_map";
|
|
import type { GeoJsonCollection } from "../B04_PreProcess/B04_PreProcess_UI_MapRender";
|
|
import { forestSources, TRACKED_SOURCES } from "../A00_Common/map/map_forest_layers";
|
|
import {
|
|
MAP_THEME_NAMES,
|
|
mapLayerSpecs,
|
|
pickFilter,
|
|
type GlSource,
|
|
} from "../A00_Common/map/map_layer_specs";
|
|
import type { PlanLayerKey, RoadPick } from "./D03_PlanAnalysis_UI_LayerDefs";
|
|
import { SAT_LIGHT_DEFAULT } from "../A00_Common/map/map_sat_light";
|
|
|
|
export type { GlImage };
|
|
|
|
/** GL 글꼴 미리 받음 — 받기 전에 그린 글자는 대체 글꼴로 굳음(못 받아도 그림). */
|
|
export const glFont = (weight: string, text: string): Promise<unknown> =>
|
|
document.fonts.load(`${weight} 24px "${GL_FONT}"`, text || "가").catch(() => null);
|
|
|
|
export interface GlBase {
|
|
/** 자료 담기(지도 없어도 · 켤 때 올림) — null = 비움. */
|
|
setData: (source: GlSource, data: GeoJsonCollection | GlImage | null) => void;
|
|
setVisible: (key: PlanLayerKey, on: boolean) => void;
|
|
/** 올린 임도망 고름(605-2) — 그 묶음 · 선만 굵게 강조 · null = 없앰. */
|
|
setRoadPick: (pick: RoadPick | null) => void;
|
|
/** 위성 밝기 상한(0~1) — 바로 반영 · 지도를 다시 세워도 유지. */
|
|
setSatelliteLight: (level: number) => void;
|
|
/** 켬 = 지도를 `parent` 맨 앞에 만듦(참 = 섬 · 거짓 = WebGL 못 씀) · null = 버림. */
|
|
enable: (parent: HTMLElement | null) => boolean;
|
|
map: () => MapLibreMap | null;
|
|
/** 화면을 따라감(경위도 가운데 · 줌 · 돌림 도) — 같은 화면에 바로 그림. */
|
|
camera: (center: [number, number], zoom: number, bearingDeg: number) => void;
|
|
resize: () => void;
|
|
}
|
|
|
|
/** `track` = 받는 일을 진행 서클에 셈(등고 · 산림 조각 받는 동안). */
|
|
export function createGlBase(
|
|
projectId: string,
|
|
track: <T>(job: Promise<T>) => Promise<T>,
|
|
onFail: (why: string) => void,
|
|
): GlBase {
|
|
let light = SAT_LIGHT_DEFAULT;
|
|
let pick: RoadPick | null = null;
|
|
const base = createGlMap<PlanLayerKey>({
|
|
specs: () => mapLayerSpecs(light, pick),
|
|
fixed: {
|
|
satellite: satelliteTileSource(projectId),
|
|
contour: contourTileSource(projectId),
|
|
...forestSources(projectId),
|
|
},
|
|
themeNames: MAP_THEME_NAMES,
|
|
className: "d03-plan__gl",
|
|
onFail,
|
|
});
|
|
/** 등고 · 산림 조각 받는 동안 서클 하나 — 다 받거나 지도가 쉬면(`idle`) · 지도를 버리면 끝. */
|
|
let settle: (() => void) | null = null;
|
|
const done = (): void => {
|
|
settle?.();
|
|
settle = null;
|
|
};
|
|
let watched: MapLibreMap | null = null;
|
|
function watch(map: MapLibreMap): void {
|
|
watched = map;
|
|
map.on("sourcedataloading", (event) => {
|
|
if (!TRACKED_SOURCES.includes(event.sourceId) || settle) return;
|
|
void track(new Promise<void>((resolve) => (settle = resolve)));
|
|
});
|
|
map.on("sourcedata", (event) => {
|
|
const loaded = (id: string) => !map.getSource(id) || map.isSourceLoaded(id);
|
|
if (TRACKED_SOURCES.includes(event.sourceId) && TRACKED_SOURCES.every(loaded)) done();
|
|
});
|
|
map.on("idle", done);
|
|
}
|
|
return {
|
|
...base,
|
|
enable(parent) {
|
|
const on = base.enable(parent);
|
|
const map = base.map();
|
|
if (!map) done();
|
|
else if (map !== watched) watch(map);
|
|
return on;
|
|
},
|
|
setRoadPick(next) {
|
|
pick = next;
|
|
const map = base.map();
|
|
for (const id of ["forestRoadFile-pick-halo", "forestRoadFile-pick"])
|
|
if (map?.getLayer(id)) map.setFilter(id, pickFilter(pick));
|
|
},
|
|
setSatelliteLight(next) {
|
|
light = next;
|
|
const map = base.map();
|
|
if (map?.getLayer("satellite"))
|
|
map.setPaintProperty("satellite", "raster-brightness-max", next);
|
|
},
|
|
};
|
|
}
|