feat(지도): 634-2 · 634-3 아이콘 표 두 장 · 묶음 메뉴 · 한 행 배선 · 호버 = 표(요구 546)

- 겹 표(map_layer_list) = 그룹1 22 줄 표 차례 · title = 호버제목 · group(수계망도 · 도로 · 토지·건물 · 제한·재해) · pages · 숨김 표 걷음 · 도로300m 한 줄(「도로」 묶음 · 기능 남김)
- 겹 줄(map_layer_bar) = 단독 넷 | 묶음 넷(여럿 고르기 메뉴 · 켠 수 배지 · 안 닫힘) | 그래픽 카드 · sync 가 단추 · 메뉴 줄 막힘 · 글을 맞춤(why = 페이지 까닭)
- 도구 표(map_toolbar) = 그룹2 17 줄 title · mapToolGroups 가 글을 표로 맞춤 · 기본 단추 생성 · 지형 칩 막힘 「수치지형도/라이다 — 까닭」
- 띠 = 한 자료 · 겹 둘(GL fillEdge 거름 · 옛 캔버스 · 범례 줄 따로) · B05 띠 받기(D03 과 같은 서버 길 · bandCollection 공용)
- D03 = 겹 줄 · 도구줄 한 행(createMapToolRow) · 222px 기댐 · 줄바꿈 CSS 걷음 · 측점 · 기울기 호버 덮어쓰기 걷음 · LAS 올리기 호버 걷음
- B05 = 세로 두 줄 → 한 행 · 호버 덮어쓰기 걷음(도구줄 · 1m 등고선 · 3D · 추천) · 겹 막힘 querySelector 덮어쓰기 → 공용 sync · 임도(업로드) 보임 + 「받은 임도 자료 없음」 막힘 · 스냅 간격 날 select → 고르기 메뉴(하나)
- 켬끔 기억 열쇠 · 측점 · 기울기 처음 값 · 그래픽 카드 취향 열쇠 둘 그대로
- 낡은 시험 고침(568_1 · 622_2 · 624_2 · 624_3 · 553_4 · 625_draw · b05_routeedit_initial · 626_3 · 390_4 · 346_2 · 353_1 · 581_2 · 584_1 · 584_3) — 표 꼴 row() · 호버 글 = 표 · 한 행 · 고르기 메뉴로 바뀐 글을 잡음 · 새 지킴 634_2

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-01 11:40:26 +09:00
co-authored by Claude Opus 5.5
parent 2265ac4f7c
commit 61733d064d
34 changed files with 527 additions and 428 deletions
+67 -115
View File
@@ -1,10 +1,10 @@
/* =============================================================================
* map_layer_list.ts
* 공용 지도 겹 목록(PLAN 624-2 · 요구 530 · 결정 A② · A⑧ · A⑨) — 예정노선(D03) · B05 평면이 같이 씀.
* 겹 = 열쇠 · 아이콘 · 이름 · 처음 값(`on` = 예정노선) · 단추 묶음(바탕 · 물 · 길 · 구역 · 사이 나눔선).
* 페이지 표(`MAP_LAYER_PAGES`) = 보일 겹(숨길 겹) · 처음 켬(없으면 겹의 `on`) · 켬끔 취향 열쇠.
* 겹 = 요구 546 그룹1 표 한 줄 — 열쇠 · 아이콘 · 호버제목 · 아이콘 묶음(`LAYER_GROUPS` · 없음 = 단독) · 보일 페이지 · 처음 값(`on` = 예정노선).
* 페이지 표(`MAP_LAYER_PAGES`) = 처음 켬(없으면 겹의 `on`) · 켬끔 취향 열쇠.
* 선 겹 꼴 · 보호구역 색 · 산사태위험 범례 = GL 꼴(`map_layer_specs`) · 범례(`map_legend`) · 옛 캔버스가 같이 읽음.
* 쓰는 법 = 겹 줄 `createMapLayerBar({ page })`(`map_layer_bar`) · 새 겹 = `GROUPS` 한 줄 + 꼴 한 벌.
* 쓰는 법 = 겹 줄 `createMapLayerBar({ page })`(`map_layer_bar`) · 새 겹 = `LAYERS` 한 줄 + 꼴 한 벌.
* ========================================================================== */
import type { IconName } from "@ui/ui_template_icons";
@@ -32,7 +32,8 @@ export type MapLayerKey =
| "faults"
| "lineaments"
| "zeroOrder"
| "bands"
| "bands" // 계류 100 m 띠(자료 = 띠 둘 한 벌 · `_band` 로 가름)
| "roadBands" // 도로 300 m 띠(같은 자료 · 「도로」 묶음)
| "building"
| "place";
@@ -42,130 +43,81 @@ export interface RoadPick {
line?: number;
}
/** 지도가 있는 페이지. */
export type MapPage = "d03" | "b05";
/** 아이콘 묶음(요구 546 표 「아이콘 그룹명」) — 묶음 단추 = 아이콘 + ▾ + 켠 수 · 여럿 고르기 메뉴. */
export type LayerGroup = "water" | "road" | "land" | "limit";
export const LAYER_GROUPS: Record<LayerGroup, { name: string; icon: IconName }> = {
water: { name: "수계망도", icon: "streams" },
road: { name: "도로", icon: "signpost" },
land: { name: "토지·건물", icon: "parcelHouse" },
limit: { name: "제한·재해", icon: "ban" },
};
export interface LayerDef {
key: MapLayerKey;
icon: IconName;
/** 호버제목(요구 546 표) — 마우스 글 · 메뉴 줄 글. */
title: string;
/** 아이콘 묶음 — null = 단독 단추. */
group: LayerGroup | null;
/** 보일 페이지(표 「사용」). */
pages: readonly MapPage[];
/** 예정노선 처음 값 — 다른 페이지는 `MAP_LAYER_PAGES[page].on`. */
on: boolean;
}
/** 단추 묶음(사이 나눔선) — 바탕 · 물 · 길 · 구역. 「지금」 겹 처음 값 = 끔(`_분석_440-C` 1-4). */
export const GROUPS: readonly (readonly LayerDef[])[] = [
[
{ key: "satellite", icon: "satellite", title: "위성사진", on: true },
{ key: "hillshade", icon: "sun", title: "음영기복(2 m 지형 · 산줄기 · 골)", on: false },
{ key: "contour", icon: "mountain", title: "도엽 등고선(굵은 선 = 계곡선 25 m)", on: true },
{ key: "slope", icon: "angle", title: "급경사 색(기울기 % 범위 · 범례 ⚙ 로 바꿈)", on: false },
],
[
{ key: "stream", icon: "droplet", title: "세류선(가는 파랑) · 하천(굵은 파랑)", on: true },
{ key: "water", icon: "ripple", title: "물 면(도엽 실폭하천 · 저수지 등)", on: false },
{ key: "road", icon: "road", title: "기존 도로(도엽 도로중심선)", on: false },
{
key: "nationalRoad",
icon: "route",
title:
"임도망도(산림청 전국 국가임도 · 간선 · 작업 · 산불 색 · 지방 · 민간임도는 「임도(업로드)」로)",
on: false,
},
{
key: "forestRoadFile",
icon: "map",
title:
"임도(업로드)(산림청 시군구 SHP 등 사용자가 올린 지방 · 민간임도 = 밝은 분홍 실선 · 왼쪽 「위치」 칸에서 올리고 묶음 고름 · 지움)",
on: false,
},
],
[
{ key: "cadastral", icon: "borderAll", title: "지적도(참고용 · 법적 효력 없음)", on: false },
{
key: "zones",
icon: "shield",
title: "보호 · 규제 구역(브이월드 여덟 종 · 참고용)",
on: false,
},
{
key: "soils",
icon: "leaf",
title: "산림입지도(토양형 · 산림청 산림입지토양도 1:25,000 · 줌 10 이상 · 참고용)",
on: false,
},
{
key: "ecoForest",
icon: "trees",
title: "경제림육성단지(산림청 2014 · 국유 · 사유 테두리 · 참고용)",
on: false,
},
{
key: "ecology",
icon: "plant",
title: "생태자연도(환경부 · 1~3 등급 · 별도관리 · 참고용)",
on: false,
},
{
key: "landslide",
icon: "alert",
title: "산사태위험지도(1~5 등급 · 행안부 생활안전지도 · 참고용)",
on: false,
},
{
key: "landslideZones",
icon: "alertOctagon",
title: "산사태취약지역(시도 지정 현황 · 점 + 지번 필지 테두리 · 참고용)",
on: false,
},
{ key: "faults", icon: "bolt", title: "단층(브이월드 지질 · 참고용)", on: false },
{
key: "lineaments",
icon: "lineDashed",
title: "지질구조선(브이월드 지질 · 참고용)",
on: false,
},
{
key: "zeroOrder",
icon: "arrowsJoin",
title: "0차곡(지형 추정 골짜기 머리 · 위 집수 0.1~1 ha · 곡두 통과 피함)",
on: false,
},
{
key: "bands",
icon: "borderOuter",
title: "계류 100 m · 도로 300 m 띠(타당성평가)",
on: false,
},
{ key: "building", icon: "building", title: "건물(도엽 · 하류 민가 · 인가)", on: false },
{ key: "place", icon: "mapPin", title: "지명(도엽 계곡 · 령 · 부락)", on: false },
],
const BOTH: readonly MapPage[] = ["d03", "b05"];
const row = (
key: MapLayerKey,
icon: IconName,
title: string,
group: LayerGroup | null,
on = false,
): LayerDef => ({ key, icon, title, group, pages: BOTH, on });
/** 그룹1 표(요구 546 · 차례 = 단추 차례) — 도로 300 m 띠는 표 밖 한 줄(「도로」 묶음 · 기능 남김). */
export const LAYERS: readonly LayerDef[] = [
row("satellite", "satellite", "위성사진", null, true),
row("hillshade", "sun", "음영기복도", null),
row("contour", "mountain", "등고선", null, true),
row("slope", "angle", "급경사", null),
row("stream", "droplet", "세류", "water", true),
row("water", "ripple", "하천", "water"),
row("zeroOrder", "arrowsJoin", "0차곡", "water"),
row("bands", "borderOuter", "계류100m", "water"),
row("road", "road", "도로", "road"),
row("nationalRoad", "route", "임도망도", "road"),
row("forestRoadFile", "map", "임도(업로드)", "road"),
row("roadBands", "roadBand", "도로300m", "road"),
row("cadastral", "borderAll", "지적도", "land"),
row("building", "building", "건물", "land"),
row("place", "mapPin", "지명", "land"),
row("zones", "shield", "보호규제구역", "limit"),
row("soils", "leaf", "산림입지도", "limit"),
row("ecoForest", "trees", "국유림·사유림", "limit"),
row("ecology", "plant", "생태자연도", "limit"),
row("landslide", "alert", "산사태위험지도", "limit"),
row("landslideZones", "alertOctagon", "산사태취약지역", "limit"),
row("faults", "bolt", "단층", "limit"),
row("lineaments", "lineament", "지질구조선", "limit"),
];
export const LAYERS: readonly LayerDef[] = GROUPS.flat();
/** 지도가 있는 페이지. */
export type MapPage = "d03" | "b05";
/** 페이지 표 — 숨길 겹 · 처음 켬(없음 = 겹의 `on`) · 켬끔 취향 열쇠. */
export const MAP_LAYER_PAGES: Record<
MapPage,
{ hide: readonly MapLayerKey[]; on?: readonly MapLayerKey[]; pref: string }
> = {
d03: { hide: [], pref: "aislo:pref:d03.layers" },
// A② 처음 = 지금 그대로(등고선 · 하천) · A⑨ 띠(타당성) · 임도(업로드) 숨김
b05: {
hide: ["bands", "forestRoadFile"],
on: ["contour", "stream"],
pref: "aislo:pref:b05.layers",
},
/** 페이지 표 — 처음 켬(없음 = 겹의 `on`) · 켬끔 취향 열쇠. */
export const MAP_LAYER_PAGES: Record<MapPage, { on?: readonly MapLayerKey[]; pref: string }> = {
d03: { pref: "aislo:pref:d03.layers" },
// A② 처음 = 지금 그대로(등고선 · 하천)
b05: { on: ["contour", "stream"], pref: "aislo:pref:b05.layers" },
};
/** 그 페이지에 보일 겹 묶음(빈 묶음 뺌). */
export const pageGroups = (page: MapPage): LayerDef[][] =>
GROUPS.map((group) =>
group.filter((def) => !MAP_LAYER_PAGES[page].hide.includes(def.key)),
).filter((group) => group.length);
/** 그 페이지에 보일 겹(표 차례). */
export const pageLayers = (page: MapPage): LayerDef[] =>
LAYERS.filter((def) => def.pages.includes(page));
/** 켬끔 취향 — 없거나 깨지면 페이지 처음 값 · 숨긴 겹 = 끔. */
/** 켬끔 취향 — 없거나 깨지면 페이지 처음 값 · 안 보일 겹 = 끔. */
export function readLayerPref(page: MapPage): Record<MapLayerKey, boolean> {
const { hide, on: first, pref } = MAP_LAYER_PAGES[page];
const { on: first, pref } = MAP_LAYER_PAGES[page];
const on = Object.fromEntries(
LAYERS.map((def) => [def.key, first ? first.includes(def.key) : def.on]),
) as Record<MapLayerKey, boolean>;
@@ -176,7 +128,7 @@ export function readLayerPref(page: MapPage): Record<MapLayerKey, boolean> {
} catch {
// 깨진 값 = 처음 값
}
for (const key of hide) on[key] = false;
for (const def of LAYERS) if (!def.pages.includes(page)) on[def.key] = false;
return on;
}