refactor(공용 지도): 겹 목록 · GL 겹 꼴 · 겹 줄 · 범례 · 산림 조각 겹을 새 공용 폴더 A00_Common/map 으로 — D03 은 부르기만 · 겉모습 · 동작 그대로 (PLAN 624-2 · 요구 530 · 결정 A⑧)
- map_layer_list = 겹 스물셋 · 처음 값 · 페이지 표(d03 · b05 보일 겹 · 처음 켬 · 취향 열쇠 — A② · A⑨) - map_layer_specs = GL 겹 꼴 · 차례(D03 UI_MapGl 에서 옮김 · 선 겹 꼴은 LINE_LAYERS 한 벌) - map_layer_bar = 겹 단추 줄 · 켬끔 · 취향 · 그래픽 카드 단추 / map_legend = 범례 칸 · 조각 겹 범례 - map_forest_layers = D03_UI_ForestLayers 옮김(이름만) - D03 UI_Layers 683 → 639 줄 · UI_MapGl 423 → 115 줄 · UI_LayerDefs 는 캔버스 도움 + 옛 이름 다시 내놓음 - 시험 길 고침(553-4 · 581-2 · 584-1 · 584-3 · 606-1 · 615 · 622-2) — 옮긴 파일 자리 · 553-4 겹 목록은 622-2 때 낡은 것(국유림 임도 · 경제림 · 생태자연도 빠짐) 같이 고침 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+7
-7
@@ -1,24 +1,24 @@
|
|||||||
/* =============================================================================
|
/* =============================================================================
|
||||||
* D03_PlanAnalysis_UI_ForestLayers.ts
|
* map_forest_layers.ts
|
||||||
* D03 받은 산림 · 환경 자료 겹(PLAN 622-2 · 요구 529) — 서버 줌별 조각(622-1)을 GL 고정 소스로(위성 · 등고선 615 꼴).
|
* 공용 지도 — 받은 산림 · 환경 자료 겹(PLAN 622-2 · 624-2 · 요구 529 · 530) — 서버 줌별 조각(622-1)을 GL 고정 소스로(위성 · 등고선 615 꼴).
|
||||||
* 벡터 = 국유림 임도(forest_road · 간선 · 작업 · 산불 색 · 줌 따라 굵기) · 산림입지도(forest_soil · 토양 무리 색 ·
|
* 벡터 = 국유림 임도(forest_road · 간선 · 작업 · 산불 색 · 줌 따라 굵기) · 산림입지도(forest_soil · 토양 무리 색 ·
|
||||||
* 반투명 · z10 아래 숨김) · 경제림(eco_forest · 국유 · 사유 테두리) /
|
* 반투명 · z10 아래 숨김) · 경제림(eco_forest · 국유 · 사유 테두리) /
|
||||||
* 그림 = 생태자연도(ecology) · 산사태위험지도(landslide · 옛 캔버스는 받은 범위 한 장 그대로).
|
* 그림 = 생태자연도(ecology) · 산사태위험지도(landslide · 옛 캔버스는 받은 범위 한 장 그대로).
|
||||||
* 조각만 쓰는 겹(`TILE_ONLY`) = 받기 없음 · 옛 캔버스(그래픽 카드 끔)엔 안 보임.
|
* 조각만 쓰는 겹(`TILE_ONLY`) = 받기 없음 · 옛 캔버스(그래픽 카드 끔)엔 안 보임.
|
||||||
* 토양 무리 색(`soilColor`) = 조각(`code` = SLTP_CD 두 자리) · 브이월드 요약(기호 「B₁」) 같은 색 — 범례 · 옛 캔버스도 이것.
|
* 토양 무리 색(`soilColor`) = 조각(`code` = SLTP_CD 두 자리) · 브이월드 요약(기호 「B₁」) 같은 색 — 범례 · 옛 캔버스도 이것.
|
||||||
* 부르는 곳 = `D03_PlanAnalysis_UI_MapGl`(꼴 · 소스) · `UI_Layers`(받기 거름) · `UI_LayerLegend`(범례).
|
* 부르는 곳 = `map_layer_specs`(꼴) · D03 `UI_MapGl`(소스) · `UI_Layers`(받기 거름) · `map_legend`(범례).
|
||||||
* ========================================================================== */
|
* ========================================================================== */
|
||||||
|
|
||||||
import type { ExpressionSpecification, SourceSpecification } from "maplibre-gl";
|
import type { ExpressionSpecification, SourceSpecification } from "maplibre-gl";
|
||||||
import type { LegendItem } from "@ui/ui_template_flow_legend";
|
import type { LegendItem } from "@ui/ui_template_flow_legend";
|
||||||
import { tileSource, type GlSpec } from "@ui/ui_template_gl_map";
|
import { tileSource, type GlSpec } from "@ui/ui_template_gl_map";
|
||||||
import { themeColor } from "@ui/ui_template_palette";
|
import { themeColor } from "@ui/ui_template_palette";
|
||||||
import type { PlanLayerKey } from "./D03_PlanAnalysis_UI_LayerDefs";
|
import type { MapLayerKey } from "./map_layer_list";
|
||||||
|
|
||||||
/** 조각만 쓰는 겹 — 받을 자료 없음(위성 포함). */
|
/** 조각만 쓰는 겹 — 받을 자료 없음(위성 포함). */
|
||||||
const TILE_ONLY = ["satellite", "nationalRoad", "ecoForest", "ecology"] as const;
|
const TILE_ONLY = ["satellite", "nationalRoad", "ecoForest", "ecology"] as const;
|
||||||
export type TileOnlyKey = (typeof TILE_ONLY)[number];
|
export type TileOnlyKey = (typeof TILE_ONLY)[number];
|
||||||
export const tileOnly = (key: PlanLayerKey): key is TileOnlyKey =>
|
export const tileOnly = (key: MapLayerKey): key is TileOnlyKey =>
|
||||||
(TILE_ONLY as readonly string[]).includes(key);
|
(TILE_ONLY as readonly string[]).includes(key);
|
||||||
|
|
||||||
/** 받는 동안 서클에 세는 조각 소스(등고선 포함). */
|
/** 받는 동안 서클에 세는 조각 소스(등고선 포함). */
|
||||||
@@ -89,7 +89,7 @@ const ROUND = { "line-join": "round", "line-cap": "round" } as const;
|
|||||||
const zoomWidth = (z8: number, z12: number, z15: number): ExpressionSpecification =>
|
const zoomWidth = (z8: number, z12: number, z15: number): ExpressionSpecification =>
|
||||||
["interpolate", ["linear"], ["zoom"], 8, z8, 12, z12, 15, z15] as ExpressionSpecification;
|
["interpolate", ["linear"], ["zoom"], 8, z8, 12, z12, 15, z15] as ExpressionSpecification;
|
||||||
|
|
||||||
type Spec = GlSpec<PlanLayerKey>;
|
type Spec = GlSpec<MapLayerKey>;
|
||||||
const raster = (key: "landslide" | "ecology", opacity: number): Spec =>
|
const raster = (key: "landslide" | "ecology", opacity: number): Spec =>
|
||||||
({
|
({
|
||||||
key,
|
key,
|
||||||
@@ -172,7 +172,7 @@ export function forestRoadSpecs(): Spec[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 범례 — 켠 조각 겹(산사태위험 = 범례 자리 `LANDSLIDE_LEGEND` 따로). */
|
/** 범례 — 켠 조각 겹(산사태위험 = 범례 자리 `LANDSLIDE_LEGEND` 따로). */
|
||||||
export function forestLegendRows(on: Record<PlanLayerKey, boolean>): LegendItem[][] {
|
export function forestLegendRows(on: Record<MapLayerKey, boolean>): LegendItem[][] {
|
||||||
const item = (label: string, color: string, kind?: LegendItem["kind"]): LegendItem => ({
|
const item = (label: string, color: string, kind?: LegendItem["kind"]): LegendItem => ({
|
||||||
label,
|
label,
|
||||||
color: `var(${color})`,
|
color: `var(${color})`,
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/* =============================================================================
|
||||||
|
* map_layer_bar.ts
|
||||||
|
* 공용 지도 겹 줄(PLAN 569-2 · 586-2 · 624-2 · 요구 530) — 겹 단추 한 줄(공용 지도 도구줄 `createMapTools` · 묶음 사이 나눔선) +
|
||||||
|
* 끝에 그래픽 카드 단추(GL ↔ 옛 캔버스).
|
||||||
|
* 보일 겹 · 처음 켬 · 취향 열쇠 = 페이지 표(`map_layer_list` `MAP_LAYER_PAGES`) · 누르면 켬끔 뒤집고 취향에 적고 `onToggle`.
|
||||||
|
* 막힘 = 지형을 아직 안 받음(`sync({ ready: false })`) · 조각만 쓰는 겹은 옛 캔버스에서 「안 보임」 글.
|
||||||
|
* 받기 · 그리기 · 범례는 페이지 몫 — 켬끔 값(`on`)은 같은 객체를 같이 씀.
|
||||||
|
* 쓰는 법 = `createMapLayerBar({ page: "d03", glOn, onToggle, onGl })` → `{ root, bar, on, toggle, save, sync }`.
|
||||||
|
* ========================================================================== */
|
||||||
|
|
||||||
|
import { createIconButton } from "@ui/ui_template_button";
|
||||||
|
import { createMapTools } from "@ui/ui_template_map_tools";
|
||||||
|
import { writeByKey } from "../b_page_state";
|
||||||
|
import { tileOnly } from "./map_forest_layers";
|
||||||
|
import {
|
||||||
|
MAP_LAYER_PAGES,
|
||||||
|
pageGroups,
|
||||||
|
readLayerPref,
|
||||||
|
type MapLayerKey,
|
||||||
|
type MapPage,
|
||||||
|
} from "./map_layer_list";
|
||||||
|
|
||||||
|
export interface MapLayerBar {
|
||||||
|
/** 도구줄 감싼 것(자리는 페이지 CSS). */
|
||||||
|
root: HTMLElement;
|
||||||
|
/** 도구줄 자체(폭 재기). */
|
||||||
|
bar: HTMLElement;
|
||||||
|
/** 켬끔 — 페이지가 바꾸면 `save` · 받기 실패로 끄는 것은 안 적음. */
|
||||||
|
on: Record<MapLayerKey, boolean>;
|
||||||
|
/** 뒤집고 적고 `onToggle`. */
|
||||||
|
toggle: (key: MapLayerKey) => void;
|
||||||
|
save: () => void;
|
||||||
|
/** 단추 눌림 · 막힘 · 글을 켬끔 값에 맞춤. */
|
||||||
|
sync: (state: { ready: boolean; glOn: boolean }) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createMapLayerBar(opts: {
|
||||||
|
page: MapPage;
|
||||||
|
glOn: boolean;
|
||||||
|
onToggle: (key: MapLayerKey) => void;
|
||||||
|
/** 그래픽 카드 단추 누름. */
|
||||||
|
onGl: () => void;
|
||||||
|
}): MapLayerBar {
|
||||||
|
const { page } = opts;
|
||||||
|
const on = readLayerPref(page);
|
||||||
|
const save = (): void => writeByKey(MAP_LAYER_PAGES[page].pref, JSON.stringify(on));
|
||||||
|
const toggle = (key: MapLayerKey): void => {
|
||||||
|
on[key] = !on[key];
|
||||||
|
save();
|
||||||
|
opts.onToggle(key);
|
||||||
|
};
|
||||||
|
const defs = pageGroups(page);
|
||||||
|
const buttons = new Map<MapLayerKey, HTMLButtonElement>();
|
||||||
|
const groups = defs.map((group) =>
|
||||||
|
group.map((def) => {
|
||||||
|
const button = createIconButton({
|
||||||
|
icon: def.icon,
|
||||||
|
title: def.title,
|
||||||
|
size: 32,
|
||||||
|
pressed: on[def.key],
|
||||||
|
onClick: () => toggle(def.key),
|
||||||
|
});
|
||||||
|
button.dataset.layer = def.key;
|
||||||
|
buttons.set(def.key, button);
|
||||||
|
return button;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const glButton = createIconButton({
|
||||||
|
icon: "gauge",
|
||||||
|
title: "그래픽 카드 그림(바탕 겹 · 글을 GPU 로 · 끔 = 옛 캔버스 그림)",
|
||||||
|
size: 32,
|
||||||
|
pressed: opts.glOn,
|
||||||
|
onClick: opts.onGl,
|
||||||
|
});
|
||||||
|
glButton.dataset.layer = "gl";
|
||||||
|
groups.push([glButton]);
|
||||||
|
const tools = createMapTools({ label: "지도 겹", groups });
|
||||||
|
|
||||||
|
function sync({ ready, glOn }: { ready: boolean; glOn: boolean }): void {
|
||||||
|
for (const def of defs.flat()) {
|
||||||
|
const button = buttons.get(def.key)!;
|
||||||
|
button.setAttribute("aria-pressed", String(on[def.key]));
|
||||||
|
button.disabled = !ready;
|
||||||
|
const title = !ready
|
||||||
|
? `${def.title} — 지형 받기 뒤 보임`
|
||||||
|
: tileOnly(def.key) && !glOn
|
||||||
|
? `${def.title} — 옛 그림(그래픽 카드 끔)엔 안 보임`
|
||||||
|
: def.title;
|
||||||
|
button.title = title;
|
||||||
|
button.setAttribute("aria-label", title);
|
||||||
|
}
|
||||||
|
glButton.setAttribute("aria-pressed", String(glOn));
|
||||||
|
}
|
||||||
|
|
||||||
|
return { root: tools.root, bar: tools.bar, on, toggle, save, sync };
|
||||||
|
}
|
||||||
@@ -0,0 +1,221 @@
|
|||||||
|
/* =============================================================================
|
||||||
|
* map_layer_list.ts
|
||||||
|
* 공용 지도 겹 목록(PLAN 624-2 · 요구 530 · 결정 A② · A⑧ · A⑨) — 예정노선(D03) · B05 평면이 같이 씀.
|
||||||
|
* 겹 = 열쇠 · 아이콘 · 이름 · 처음 값(`on` = 예정노선) · 단추 묶음(바탕 · 물 · 길 · 구역 · 사이 나눔선).
|
||||||
|
* 페이지 표(`MAP_LAYER_PAGES`) = 보일 겹(숨길 겹) · 처음 켬(없으면 겹의 `on`) · 켬끔 취향 열쇠.
|
||||||
|
* 선 겹 꼴 · 보호구역 색 · 산사태위험 범례 = GL 꼴(`map_layer_specs`) · 범례(`map_legend`) · 옛 캔버스가 같이 읽음.
|
||||||
|
* 쓰는 법 = 겹 줄 `createMapLayerBar({ page })`(`map_layer_bar`) · 새 겹 = `GROUPS` 한 줄 + 꼴 한 벌.
|
||||||
|
* ========================================================================== */
|
||||||
|
|
||||||
|
import type { IconName } from "@ui/ui_template_icons";
|
||||||
|
import type { LegendItem } from "@ui/ui_template_flow_legend";
|
||||||
|
import { readByKey } from "../b_page_state";
|
||||||
|
|
||||||
|
export type MapLayerKey =
|
||||||
|
| "satellite"
|
||||||
|
| "hillshade"
|
||||||
|
| "contour"
|
||||||
|
| "slope"
|
||||||
|
| "stream"
|
||||||
|
| "water"
|
||||||
|
| "road"
|
||||||
|
| "forestRoad"
|
||||||
|
| "forestRoadFile"
|
||||||
|
| "nationalRoad"
|
||||||
|
| "ecoForest"
|
||||||
|
| "ecology"
|
||||||
|
| "cadastral"
|
||||||
|
| "zones"
|
||||||
|
| "soils"
|
||||||
|
| "landslide"
|
||||||
|
| "landslideZones"
|
||||||
|
| "faults"
|
||||||
|
| "lineaments"
|
||||||
|
| "zeroOrder"
|
||||||
|
| "bands"
|
||||||
|
| "building"
|
||||||
|
| "place";
|
||||||
|
|
||||||
|
/** 올린 임도망 고름(605-2) — 묶음 하나 또는 그 안의 선 하나(지도 강조). */
|
||||||
|
export interface RoadPick {
|
||||||
|
bundle: string;
|
||||||
|
line?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LayerDef {
|
||||||
|
key: MapLayerKey;
|
||||||
|
icon: IconName;
|
||||||
|
title: string;
|
||||||
|
/** 예정노선 처음 값 — 다른 페이지는 `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: "forestRoad",
|
||||||
|
icon: "route",
|
||||||
|
title: "기존 임도 짐작(도엽 도로 소로 · 미분류 = 주황 점선 · 정식 임도망도 아님)",
|
||||||
|
on: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "forestRoadFile",
|
||||||
|
icon: "map",
|
||||||
|
title:
|
||||||
|
"올린 임도망도(산림청 시군구 SHP = 밝은 분홍 실선 · 왼쪽 「위치」 칸에서 올리고 묶음 고름 · 지움)",
|
||||||
|
on: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "nationalRoad",
|
||||||
|
icon: "flag",
|
||||||
|
title:
|
||||||
|
"국유림 임도(산림청 전국 국가임도 · 간선 · 작업 · 산불 색 · 지방 · 민간임도 없음 · 참고용)",
|
||||||
|
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 },
|
||||||
|
],
|
||||||
|
];
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 그 페이지에 보일 겹 묶음(빈 묶음 뺌). */
|
||||||
|
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 function readLayerPref(page: MapPage): Record<MapLayerKey, boolean> {
|
||||||
|
const { hide, 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>;
|
||||||
|
try {
|
||||||
|
const saved = JSON.parse(readByKey(pref) ?? "{}") as Partial<Record<MapLayerKey, unknown>>;
|
||||||
|
for (const def of LAYERS)
|
||||||
|
if (typeof saved[def.key] === "boolean") on[def.key] = saved[def.key] as boolean;
|
||||||
|
} catch {
|
||||||
|
// 깨진 값 = 처음 값
|
||||||
|
}
|
||||||
|
for (const key of hide) on[key] = false;
|
||||||
|
return on;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 보호구역 종류 → 그림 색 값(서버 `ZONE_KINDS` 열쇠). */
|
||||||
|
export const ZONE_COLORS: Record<string, string> = {
|
||||||
|
forest: "--draw-map-zone-forest",
|
||||||
|
baekdu: "--draw-map-zone-baekdu",
|
||||||
|
water: "--draw-map-zone-water",
|
||||||
|
park: "--draw-map-zone-park",
|
||||||
|
heritage: "--draw-map-zone-heritage",
|
||||||
|
wetland: "--draw-map-zone-wetland",
|
||||||
|
wildlife: "--draw-map-zone-wildlife",
|
||||||
|
greenbelt: "--draw-map-zone-greenbelt",
|
||||||
|
};
|
||||||
|
/** 산사태위험지도 범례(1 등급 = 가장 위험 · 색 = WMS 그림 색). */
|
||||||
|
export const LANDSLIDE_LEGEND: LegendItem[] = [1, 2, 3, 4, 5].map((grade) => ({
|
||||||
|
label: `산사태 ${grade}등급`,
|
||||||
|
color: `var(--draw-map-landslide-grade-${grade})`,
|
||||||
|
}));
|
||||||
|
/** 선 겹 — 이름 · 그림 꼴(서버 `LINE_KINDS` 열쇠 = `kind` · 없으면 0차곡). 새 선 겹 = 여기 한 줄. */
|
||||||
|
export type LineLayerKey = "faults" | "lineaments" | "zeroOrder";
|
||||||
|
export const LINE_LAYERS: Record<
|
||||||
|
LineLayerKey,
|
||||||
|
{ label: string; kind?: string; color: string; width: number; dash: number[] }
|
||||||
|
> = {
|
||||||
|
faults: { label: "단층", kind: "faults", color: "--draw-map-fault", width: 2.4, dash: [] },
|
||||||
|
lineaments: {
|
||||||
|
label: "지질구조선",
|
||||||
|
kind: "lineaments",
|
||||||
|
color: "--draw-map-lineament",
|
||||||
|
width: 1.8,
|
||||||
|
dash: [8, 4],
|
||||||
|
},
|
||||||
|
zeroOrder: { label: "0차곡", color: "--draw-map-zero-order", width: 1.8, dash: [4, 3] },
|
||||||
|
};
|
||||||
|
export const LINE_KEYS = Object.keys(LINE_LAYERS) as LineLayerKey[];
|
||||||
@@ -0,0 +1,317 @@
|
|||||||
|
/* =============================================================================
|
||||||
|
* map_layer_specs.ts
|
||||||
|
* 공용 지도 GL 겹 꼴(PLAN 586-2 · 624-2 · 요구 530) — 바탕 겹 스물의 MapLibre 꼴 · 차례(아래 → 위):
|
||||||
|
* 위성 · 그림 겹(음영 · 급경사 · 산사태위험 · 생태자연도) · 면 겹(산림입지 · 경제림 · 보호구역 · 띠 · 물 면 · 건물) ·
|
||||||
|
* 선 겹(지적 · 등고선 · 도로 · 임도 짐작 · 올린 임도망도 · 국유림 임도 · 세류 · 단층 · 지질구조선 · 0차곡 · 산사태취약 필지) ·
|
||||||
|
* 점 · 글(산사태취약 · 지명 · 등고 수치).
|
||||||
|
* 자료 = 받는 쪽이 지물에 붙인 `_color` · 고정 소스(위성 · 등고 · 산림 조각) 이름 = `map_forest_layers` · `ui_template_gl_map`.
|
||||||
|
* 카메라 · 지도 세우기는 페이지 몫(D03 `UI_MapGl.createGlBase` · B05 `createTmGl`) — 여기는 꼴만.
|
||||||
|
* ========================================================================== */
|
||||||
|
|
||||||
|
import type { ExpressionSpecification, FilterSpecification } from "maplibre-gl";
|
||||||
|
import { GL_FONT, type GlSpec as GlMapSpec } from "@ui/ui_template_gl_map";
|
||||||
|
import { themeColor } from "@ui/ui_template_palette";
|
||||||
|
import { forestAreaSpecs, forestRasterSpecs, forestRoadSpecs } from "./map_forest_layers";
|
||||||
|
import {
|
||||||
|
LINE_KEYS,
|
||||||
|
LINE_LAYERS,
|
||||||
|
type LineLayerKey,
|
||||||
|
type MapLayerKey,
|
||||||
|
type RoadPick,
|
||||||
|
} from "./map_layer_list";
|
||||||
|
|
||||||
|
/** 자료 소스(임도 짐작 = 도로 소스를 거름 · 위성 = 조각). */
|
||||||
|
export type GlSource = Exclude<MapLayerKey, "satellite" | "forestRoad">;
|
||||||
|
|
||||||
|
const GL_FONT_BOLD = [`${GL_FONT} SemiBold`, GL_FONT];
|
||||||
|
|
||||||
|
type GlSpec = GlMapSpec<MapLayerKey>;
|
||||||
|
|
||||||
|
const HALO = "rgba(0, 0, 0, 0.55)";
|
||||||
|
const ROUND = { "line-join": "round", "line-cap": "round" } as const;
|
||||||
|
const COLOR: ExpressionSpecification = ["get", "_color"];
|
||||||
|
/** 등고 조각 굵은 선(서버가 줌마다 정함) — 가는 선은 모두라 굵은 선은 두 번 겹쳐 짙음(캔버스처럼). */
|
||||||
|
const BOLD = ["==", ["get", "k"], 1] as unknown as FilterSpecification;
|
||||||
|
/** 캔버스 점선(px) → MapLibre 점선(선 굵기 배수). */
|
||||||
|
const dash = (px: number[], width: number): number[] => px.map((v) => v / width);
|
||||||
|
|
||||||
|
/** 선 겹 — 검은 테두리 + 선(점선이면 둘 다 같은 px 무늬). */
|
||||||
|
function haloLine(
|
||||||
|
key: MapLayerKey,
|
||||||
|
source: GlSource,
|
||||||
|
color: string,
|
||||||
|
width: number,
|
||||||
|
pattern: number[] = [],
|
||||||
|
halo = { color: HALO, width: width + 1.6 },
|
||||||
|
filter?: FilterSpecification,
|
||||||
|
): GlSpec[] {
|
||||||
|
const line = (id: string, paint: Record<string, unknown>, w: number): GlSpec =>
|
||||||
|
({
|
||||||
|
key,
|
||||||
|
id,
|
||||||
|
type: "line",
|
||||||
|
source,
|
||||||
|
...(filter ? { filter } : {}),
|
||||||
|
layout: ROUND,
|
||||||
|
paint: {
|
||||||
|
...paint,
|
||||||
|
"line-width": w,
|
||||||
|
...(pattern.length ? { "line-dasharray": dash(pattern, w) } : {}),
|
||||||
|
},
|
||||||
|
}) as GlSpec;
|
||||||
|
return [
|
||||||
|
line(`${key}-halo`, { "line-color": halo.color }, halo.width),
|
||||||
|
line(key, { "line-color": color }, width),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 면 겹 — 채움 + 테두리(캔버스 `fillLayer` 처럼 테두리 투명도 0.9). */
|
||||||
|
function fillEdge(
|
||||||
|
key: MapLayerKey,
|
||||||
|
source: GlSource,
|
||||||
|
fill: string | ExpressionSpecification,
|
||||||
|
opacity: number | ExpressionSpecification,
|
||||||
|
edge: { color: string | ExpressionSpecification; width: number; dash?: number[] },
|
||||||
|
): GlSpec[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key,
|
||||||
|
id: key,
|
||||||
|
type: "fill",
|
||||||
|
source,
|
||||||
|
paint: { "fill-color": fill, "fill-opacity": opacity },
|
||||||
|
} as GlSpec,
|
||||||
|
{
|
||||||
|
key,
|
||||||
|
id: `${key}-edge`,
|
||||||
|
type: "line",
|
||||||
|
source,
|
||||||
|
layout: ROUND,
|
||||||
|
paint: {
|
||||||
|
"line-color": edge.color,
|
||||||
|
"line-width": edge.width,
|
||||||
|
"line-opacity": 0.9,
|
||||||
|
...(edge.dash ? { "line-dasharray": dash(edge.dash, edge.width) } : {}),
|
||||||
|
},
|
||||||
|
} as GlSpec,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 겹 차례(아래 → 위)와 꼴 — 옛 캔버스(D03 `UI_LayerPaint`)의 색 · 굵기 · 투명도 · 차례 그대로 · `light` = 위성 밝기 상한. */
|
||||||
|
export function mapLayerSpecs(light: number, pick: RoadPick | null): GlSpec[] {
|
||||||
|
const contour = themeColor("--draw-map-contour", "#ffd68c");
|
||||||
|
const place = themeColor("--draw-map-place", "#f8fafc");
|
||||||
|
const landslideZone = themeColor("--draw-map-landslide-zone", "#e11d48");
|
||||||
|
const raster = (
|
||||||
|
key: "satellite" | "hillshade" | "slope",
|
||||||
|
opacity: number,
|
||||||
|
extra: Record<string, number> = {},
|
||||||
|
): GlSpec =>
|
||||||
|
({
|
||||||
|
key,
|
||||||
|
id: key,
|
||||||
|
type: "raster",
|
||||||
|
source: key,
|
||||||
|
paint: { "raster-opacity": opacity, "raster-fade-duration": 0, ...extra },
|
||||||
|
}) as GlSpec;
|
||||||
|
const lineKind = (key: LineLayerKey): GlSpec[] => {
|
||||||
|
const { color, width, dash } = LINE_LAYERS[key];
|
||||||
|
return haloLine(key, key, themeColor(color, "#f43f5e"), width, dash);
|
||||||
|
};
|
||||||
|
return [
|
||||||
|
// 위성 = 조금 어둡고 연하게(요구 475 · 노선 선 대비) — 밝기 상한은 사용자 조절
|
||||||
|
raster("satellite", 1, { "raster-brightness-max": light, "raster-saturation": SAT_SATURATION }),
|
||||||
|
raster("hillshade", 1), // 위성 위 = 「곱하기」와 같은 검은 그늘 그림(받는 쪽이 바꿈)
|
||||||
|
raster("slope", 0.55),
|
||||||
|
...forestRasterSpecs(), // 산사태위험 · 생태자연도 = 조각 그림(622-2)
|
||||||
|
...forestAreaSpecs(), // 산림입지도 · 경제림 = 벡터 조각
|
||||||
|
...fillEdge("zones", "zones", COLOR, 0.22, { color: COLOR, width: 1.6 }),
|
||||||
|
...fillEdge("bands", "bands", COLOR, ["case", ["==", ["get", "_band"], "road"], 0.16, 0.18], {
|
||||||
|
color: COLOR,
|
||||||
|
width: 1.2,
|
||||||
|
dash: [6, 4],
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
key: "cadastral",
|
||||||
|
id: "cadastral",
|
||||||
|
type: "line",
|
||||||
|
source: "cadastral",
|
||||||
|
layout: ROUND,
|
||||||
|
paint: {
|
||||||
|
"line-color": themeColor("--draw-map-parcel", "#f0abfc"),
|
||||||
|
"line-width": 0.8,
|
||||||
|
"line-opacity": 0.8,
|
||||||
|
},
|
||||||
|
} as GlSpec,
|
||||||
|
...((): GlSpec[] => {
|
||||||
|
const water = themeColor("--draw-map-water", "#0ea5e9");
|
||||||
|
return fillEdge("water", "water", water, 0.45, { color: water, width: 1 });
|
||||||
|
})(),
|
||||||
|
...fillEdge("building", "building", themeColor("--draw-map-building", "#fb7185"), 0.7, {
|
||||||
|
color: HALO,
|
||||||
|
width: 0.6,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
key: "contour",
|
||||||
|
id: "contour-minor",
|
||||||
|
type: "line",
|
||||||
|
source: "contour",
|
||||||
|
"source-layer": "contour",
|
||||||
|
layout: ROUND,
|
||||||
|
paint: { "line-color": contour, "line-width": 0.8, "line-opacity": 0.7 },
|
||||||
|
} as GlSpec,
|
||||||
|
{
|
||||||
|
key: "contour",
|
||||||
|
id: "contour-index",
|
||||||
|
type: "line",
|
||||||
|
source: "contour",
|
||||||
|
"source-layer": "contour",
|
||||||
|
filter: BOLD,
|
||||||
|
layout: ROUND,
|
||||||
|
paint: { "line-color": contour, "line-width": 1.6, "line-opacity": 0.7 },
|
||||||
|
} as GlSpec,
|
||||||
|
{
|
||||||
|
key: "contour",
|
||||||
|
id: "contour-label",
|
||||||
|
type: "symbol",
|
||||||
|
source: "contour",
|
||||||
|
"source-layer": "contour",
|
||||||
|
filter: BOLD,
|
||||||
|
layout: {
|
||||||
|
"symbol-placement": "line",
|
||||||
|
"symbol-spacing": 320,
|
||||||
|
"text-field": ["to-string", ["get", "h"]],
|
||||||
|
"text-font": [GL_FONT],
|
||||||
|
"text-size": 10,
|
||||||
|
"text-max-angle": 30,
|
||||||
|
"text-padding": 2,
|
||||||
|
},
|
||||||
|
paint: {
|
||||||
|
"text-color": contour,
|
||||||
|
"text-halo-color": "rgba(0, 0, 0, 0.6)",
|
||||||
|
"text-halo-width": 1.5,
|
||||||
|
},
|
||||||
|
} as GlSpec,
|
||||||
|
...haloLine("road", "road", themeColor("--draw-map-road", "#f8fafc"), 1.6, [], {
|
||||||
|
color: HALO,
|
||||||
|
width: 3.2,
|
||||||
|
}),
|
||||||
|
...haloLine(
|
||||||
|
"forestRoad",
|
||||||
|
"road",
|
||||||
|
themeColor("--draw-map-forest-road", "#f59e0b"),
|
||||||
|
2,
|
||||||
|
[7, 4],
|
||||||
|
{ color: HALO, width: 3.6 },
|
||||||
|
[
|
||||||
|
"in",
|
||||||
|
["get", "도로구분"],
|
||||||
|
["literal", ["소로", "미분류"]],
|
||||||
|
] as unknown as FilterSpecification,
|
||||||
|
),
|
||||||
|
...haloLine(
|
||||||
|
"forestRoadFile",
|
||||||
|
"forestRoadFile",
|
||||||
|
themeColor("--draw-map-forest-road-file", "#ff2fd0"),
|
||||||
|
2.4,
|
||||||
|
[],
|
||||||
|
{ color: HALO, width: 4 },
|
||||||
|
),
|
||||||
|
...forestRoadSpecs(), // 국유림 임도 = 벡터 조각
|
||||||
|
// 고른 묶음 · 선 강조(605-2) — 같은 자료에서 걸러 한 겹 더 굵게
|
||||||
|
...["-pick-halo", "-pick"].map(
|
||||||
|
(tail, i) =>
|
||||||
|
({
|
||||||
|
key: "forestRoadFile",
|
||||||
|
id: `forestRoadFile${tail}`,
|
||||||
|
type: "line",
|
||||||
|
source: "forestRoadFile",
|
||||||
|
filter: pickFilter(pick),
|
||||||
|
layout: ROUND,
|
||||||
|
paint: {
|
||||||
|
"line-color": i ? themeColor("--draw-map-sketch", "#ffe94d") : HALO,
|
||||||
|
"line-width": i ? 4.2 : 7.4,
|
||||||
|
},
|
||||||
|
}) as GlSpec,
|
||||||
|
),
|
||||||
|
{
|
||||||
|
key: "stream",
|
||||||
|
id: "stream",
|
||||||
|
type: "line",
|
||||||
|
source: "stream",
|
||||||
|
layout: ROUND,
|
||||||
|
paint: {
|
||||||
|
"line-color": themeColor("--draw-map-stream", "#38bdf8"),
|
||||||
|
"line-width": ["case", ["==", ["get", "구분"], "세류"], 1, 2.4],
|
||||||
|
},
|
||||||
|
} as GlSpec,
|
||||||
|
...LINE_KEYS.flatMap(lineKind),
|
||||||
|
// 산사태취약 — 필지 = 「이 필지 안 어딘가」 테두리만 · 점 = 지정 자리
|
||||||
|
...haloLine(
|
||||||
|
"landslideZones",
|
||||||
|
"landslideZones",
|
||||||
|
landslideZone,
|
||||||
|
1.8,
|
||||||
|
[6, 3],
|
||||||
|
{ color: "rgba(255, 255, 255, 0.7)", width: 3.2 },
|
||||||
|
["!=", ["geometry-type"], "Point"] as unknown as FilterSpecification,
|
||||||
|
),
|
||||||
|
{
|
||||||
|
key: "landslideZones",
|
||||||
|
id: "landslideZones-point",
|
||||||
|
type: "circle",
|
||||||
|
source: "landslideZones",
|
||||||
|
filter: ["==", ["geometry-type"], "Point"],
|
||||||
|
paint: {
|
||||||
|
"circle-radius": 5,
|
||||||
|
"circle-color": landslideZone,
|
||||||
|
"circle-stroke-color": "#ffffff",
|
||||||
|
"circle-stroke-width": 1.5,
|
||||||
|
},
|
||||||
|
} as GlSpec,
|
||||||
|
{
|
||||||
|
key: "place",
|
||||||
|
id: "place-dot",
|
||||||
|
type: "circle",
|
||||||
|
source: "place",
|
||||||
|
paint: { "circle-radius": 2.5, "circle-color": place },
|
||||||
|
} as GlSpec,
|
||||||
|
{
|
||||||
|
key: "place",
|
||||||
|
id: "place",
|
||||||
|
type: "symbol",
|
||||||
|
source: "place",
|
||||||
|
layout: {
|
||||||
|
"text-field": ["get", "name"],
|
||||||
|
"text-font": GL_FONT_BOLD,
|
||||||
|
"text-size": 11,
|
||||||
|
"text-anchor": "bottom",
|
||||||
|
"text-offset": [0, -0.36],
|
||||||
|
"text-padding": 2,
|
||||||
|
},
|
||||||
|
paint: {
|
||||||
|
"text-color": place,
|
||||||
|
"text-halo-color": "rgba(0, 0, 0, 0.7)",
|
||||||
|
"text-halo-width": 1.5,
|
||||||
|
},
|
||||||
|
} as GlSpec,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 고른 묶음 · 선만 거름(고름 없음 = 아무것도 안 맞는 거름). */
|
||||||
|
export function pickFilter(pick: RoadPick | null): FilterSpecification {
|
||||||
|
const all: unknown[] = ["all", ["==", ["get", "__bundle"], pick?.bundle ?? ""]];
|
||||||
|
if (pick?.line !== undefined) all.push(["==", ["get", "__line"], pick.line]);
|
||||||
|
return all as unknown as FilterSpecification;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 겹 색 지문 — 밝음 · 어두움이 바뀌면 색을 다시. */
|
||||||
|
export const MAP_THEME_NAMES = [
|
||||||
|
"--draw-map-contour",
|
||||||
|
"--draw-map-road",
|
||||||
|
"--draw-map-stream",
|
||||||
|
"--draw-map-parcel",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
/** 위성 채도(−1~1) — 조금 연하게. */
|
||||||
|
const SAT_SATURATION = -0.3;
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/* =============================================================================
|
||||||
|
* map_legend.ts
|
||||||
|
* 공용 지도 범례(PLAN 569-2 · 622-2 · 624-2 · 요구 530) — 켠 겹 범례 칸 하나(줄마다 `createLegend`) + 페이지가 붙이는 덧 줄.
|
||||||
|
* 조각 겹 범례(산사태위험 · 국유림 임도 · 경제림 · 생태자연도) = 받을 자료 없음 → 켬끔만 보고 여기서 만듦(`tileLegendRows`).
|
||||||
|
* 받은 자료 범례(수 · 넓이 · 급경사 범위)는 받는 페이지 몫 — 줄을 만들어 `show` 에 넘김.
|
||||||
|
* 쓰는 법 = `const legend = createMapLegend("d03-plan__legend")` → `legend.show(rows, [덧 줄])` · 줄 · 덧 없으면 숨김.
|
||||||
|
* ========================================================================== */
|
||||||
|
|
||||||
|
import { createLegend, type LegendItem } from "@ui/ui_template_flow_legend";
|
||||||
|
import { forestLegendRows } from "./map_forest_layers";
|
||||||
|
import { LANDSLIDE_LEGEND, type MapLayerKey } from "./map_layer_list";
|
||||||
|
|
||||||
|
export interface MapLegend {
|
||||||
|
root: HTMLElement;
|
||||||
|
show: (rows: LegendItem[][], tail?: HTMLElement[]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 켠 조각 겹 범례 — 산사태위험 다음 산림 셋. */
|
||||||
|
export const tileLegendRows = (on: Record<MapLayerKey, boolean>): LegendItem[][] => [
|
||||||
|
...(on.landslide ? [LANDSLIDE_LEGEND] : []),
|
||||||
|
...forestLegendRows(on),
|
||||||
|
];
|
||||||
|
|
||||||
|
/** `className` = 자리 · 꼴(페이지 CSS). */
|
||||||
|
export function createMapLegend(className: string): MapLegend {
|
||||||
|
const root = document.createElement("div");
|
||||||
|
root.className = className;
|
||||||
|
root.title = "켠 겹 범례 — 받은 지형 범위 안 수 · 넓이";
|
||||||
|
return {
|
||||||
|
root,
|
||||||
|
show(rows, tail = []) {
|
||||||
|
root.replaceChildren(...rows.map((row) => createLegend(row)), ...tail);
|
||||||
|
root.hidden = !rows.length && !tail.length;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,14 +1,12 @@
|
|||||||
/* =============================================================================
|
/* =============================================================================
|
||||||
* D03_PlanAnalysis_UI_LayerDefs.ts
|
* D03_PlanAnalysis_UI_LayerDefs.ts
|
||||||
* D03 바탕 겹 정의(PLAN 569-2 · 581-1 · 581-2 · 584-1 · 584-3 · 586-2) — 겹 단추 묶음 · 처음 값 · 보호구역 · 토양형 색 ·
|
* D03 바탕 겹 받기 · 옛 캔버스 도움(PLAN 569-2 · 581-1 · 581-2 · 584-1 · 584-3 · 586-2 · 624-2) — 임도 짐작 구분 · 물 면 ·
|
||||||
* 산사태위험지도 범례 · 선 겹(단층 · 지질구조선 · 0차곡) 꼴 · 켬끔 취향 읽기 · 면 겹 채우기 · 선 긋기 · 그림 겹 놓기 ·
|
* 면 겹 채우기 · 선 긋기 · 그림 겹 놓기 · GL 바탕 자료(지물 색 붙이기 · 그림 귀퉁이 · 음영 그늘 그림).
|
||||||
* GL 바탕 자료(지물 색 붙이기 · 그림 귀퉁이 · 음영 그늘 그림).
|
* 겹 목록 · 처음 값 · 켬끔 취향 · 선 겹 꼴 · 보호구역 색 = 공용 `A00_Common/map/map_layer_list`(624-2).
|
||||||
* 부르는 곳 = `D03_PlanAnalysis_UI_Layers.ts`(받기 · 그리기 차례 · 범례).
|
* 부르는 곳 = `D03_PlanAnalysis_UI_Layers.ts`(받기 · 그리기 차례 · 범례).
|
||||||
* ========================================================================== */
|
* ========================================================================== */
|
||||||
|
|
||||||
import type { IconName } from "@ui/ui_template_icons";
|
import type { MapLayerKey } from "../A00_Common/map/map_layer_list";
|
||||||
import type { LegendItem } from "@ui/ui_template_flow_legend";
|
|
||||||
import { readByKey } from "../A00_Common/b_page_state";
|
|
||||||
import {
|
import {
|
||||||
drawPreparedLayer,
|
drawPreparedLayer,
|
||||||
normalizedToScreen,
|
normalizedToScreen,
|
||||||
@@ -20,172 +18,15 @@ import type { PlanRaster } from "./D03_PlanAnalysis_Api";
|
|||||||
import type { ScreenMap } from "./D03_PlanAnalysis_UI_Map";
|
import type { ScreenMap } from "./D03_PlanAnalysis_UI_Map";
|
||||||
import type { GlImage } from "./D03_PlanAnalysis_UI_MapGl";
|
import type { GlImage } from "./D03_PlanAnalysis_UI_MapGl";
|
||||||
|
|
||||||
export type PlanLayerKey =
|
/** 겹 목록 · 페이지 표 = 공용 `map_layer_list`(624-2) — 옛 이름으로 다시 내놓음(캔버스 · 조각 · 임도망 목록이 읽음). */
|
||||||
| "satellite"
|
export type PlanLayerKey = MapLayerKey;
|
||||||
| "hillshade"
|
export {
|
||||||
| "contour"
|
LINE_KEYS,
|
||||||
| "slope"
|
LINE_LAYERS,
|
||||||
| "stream"
|
ZONE_COLORS,
|
||||||
| "water"
|
type RoadPick,
|
||||||
| "road"
|
} from "../A00_Common/map/map_layer_list";
|
||||||
| "forestRoad"
|
|
||||||
| "forestRoadFile"
|
|
||||||
| "nationalRoad"
|
|
||||||
| "ecoForest"
|
|
||||||
| "ecology"
|
|
||||||
| "cadastral"
|
|
||||||
| "zones"
|
|
||||||
| "soils"
|
|
||||||
| "landslide"
|
|
||||||
| "landslideZones"
|
|
||||||
| "faults"
|
|
||||||
| "lineaments"
|
|
||||||
| "zeroOrder"
|
|
||||||
| "bands"
|
|
||||||
| "building"
|
|
||||||
| "place";
|
|
||||||
|
|
||||||
/** 올린 임도망 고름(605-2) — 묶음 하나 또는 그 안의 선 하나(지도 강조). */
|
|
||||||
export interface RoadPick {
|
|
||||||
bundle: string;
|
|
||||||
line?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LayerDef {
|
|
||||||
key: PlanLayerKey;
|
|
||||||
icon: IconName;
|
|
||||||
title: string;
|
|
||||||
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: "forestRoad",
|
|
||||||
icon: "route",
|
|
||||||
title: "기존 임도 짐작(도엽 도로 소로 · 미분류 = 주황 점선 · 정식 임도망도 아님)",
|
|
||||||
on: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "forestRoadFile",
|
|
||||||
icon: "map",
|
|
||||||
title:
|
|
||||||
"올린 임도망도(산림청 시군구 SHP = 밝은 분홍 실선 · 왼쪽 「위치」 칸에서 올리고 묶음 고름 · 지움)",
|
|
||||||
on: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "nationalRoad",
|
|
||||||
icon: "flag",
|
|
||||||
title:
|
|
||||||
"국유림 임도(산림청 전국 국가임도 · 간선 · 작업 · 산불 색 · 지방 · 민간임도 없음 · 참고용)",
|
|
||||||
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 },
|
|
||||||
],
|
|
||||||
];
|
|
||||||
export const LAYERS: readonly LayerDef[] = GROUPS.flat();
|
|
||||||
/** 보호구역 종류 → 그림 색 값(서버 `ZONE_KINDS` 열쇠). */
|
|
||||||
export const ZONE_COLORS: Record<string, string> = {
|
|
||||||
forest: "--draw-map-zone-forest",
|
|
||||||
baekdu: "--draw-map-zone-baekdu",
|
|
||||||
water: "--draw-map-zone-water",
|
|
||||||
park: "--draw-map-zone-park",
|
|
||||||
heritage: "--draw-map-zone-heritage",
|
|
||||||
wetland: "--draw-map-zone-wetland",
|
|
||||||
wildlife: "--draw-map-zone-wildlife",
|
|
||||||
greenbelt: "--draw-map-zone-greenbelt",
|
|
||||||
};
|
|
||||||
/** 산사태위험지도 범례(1 등급 = 가장 위험 · 색 = WMS 그림 색). */
|
|
||||||
export const LANDSLIDE_LEGEND: LegendItem[] = [1, 2, 3, 4, 5].map((grade) => ({
|
|
||||||
label: `산사태 ${grade}등급`,
|
|
||||||
color: `var(--draw-map-landslide-grade-${grade})`,
|
|
||||||
}));
|
|
||||||
/** 선 겹 — 이름 · 그림 꼴(서버 `LINE_KINDS` 열쇠 = `kind` · 없으면 0차곡). 새 선 겹 = 여기 한 줄. */
|
|
||||||
export type LineLayerKey = "faults" | "lineaments" | "zeroOrder";
|
|
||||||
export const LINE_LAYERS: Record<
|
|
||||||
LineLayerKey,
|
|
||||||
{ label: string; kind?: string; color: string; width: number; dash: number[] }
|
|
||||||
> = {
|
|
||||||
faults: { label: "단층", kind: "faults", color: "--draw-map-fault", width: 2.4, dash: [] },
|
|
||||||
lineaments: {
|
|
||||||
label: "지질구조선",
|
|
||||||
kind: "lineaments",
|
|
||||||
color: "--draw-map-lineament",
|
|
||||||
width: 1.8,
|
|
||||||
dash: [8, 4],
|
|
||||||
},
|
|
||||||
zeroOrder: { label: "0차곡", color: "--draw-map-zero-order", width: 1.8, dash: [4, 3] },
|
|
||||||
};
|
|
||||||
export const LINE_KEYS = Object.keys(LINE_LAYERS) as LineLayerKey[];
|
|
||||||
/** 도로 중 기존 임도로 짐작하는 구분. */
|
/** 도로 중 기존 임도로 짐작하는 구분. */
|
||||||
export const FOREST_ROAD_KINDS = new Set(["소로", "미분류"]);
|
export const FOREST_ROAD_KINDS = new Set(["소로", "미분류"]);
|
||||||
/** 물 면 = 도엽 수계(E) 면 셋. */
|
/** 물 면 = 도엽 수계(E) 면 셋. */
|
||||||
@@ -283,22 +124,6 @@ export function drawRaster(
|
|||||||
context.drawImage(raster.image, 0, 0);
|
context.drawImage(raster.image, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 켬끔 취향(`key`) — 없거나 깨지면 처음 값. */
|
|
||||||
export function readPref(key: string): Record<PlanLayerKey, boolean> {
|
|
||||||
const on = Object.fromEntries(LAYERS.map((def) => [def.key, def.on])) as Record<
|
|
||||||
PlanLayerKey,
|
|
||||||
boolean
|
|
||||||
>;
|
|
||||||
try {
|
|
||||||
const saved = JSON.parse(readByKey(key) ?? "{}") as Partial<Record<PlanLayerKey, unknown>>;
|
|
||||||
for (const def of LAYERS)
|
|
||||||
if (typeof saved[def.key] === "boolean") on[def.key] = saved[def.key] as boolean;
|
|
||||||
} catch {
|
|
||||||
// 깨진 값 = 처음 값
|
|
||||||
}
|
|
||||||
return on;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** GL 자료 — 지물마다 그릴 색(`_color`) · 덧 속성을 붙인 사본. */
|
/** GL 자료 — 지물마다 그릴 색(`_color`) · 덧 속성을 붙인 사본. */
|
||||||
export function glTagged(
|
export function glTagged(
|
||||||
collection: GeoJsonCollection,
|
collection: GeoJsonCollection,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* D03_PlanAnalysis_UI_LayerLegend.ts
|
* D03_PlanAnalysis_UI_LayerLegend.ts
|
||||||
* D03 바탕 겹 받은 자료 꼴 · 범례 줄 · 겹별 수(PLAN 569-2 · 581 · 584-1 · 584-3) — 켠 겹의 받은 범위 안 수 · 넓이.
|
* D03 바탕 겹 받은 자료 꼴 · 범례 줄 · 겹별 수(PLAN 569-2 · 581 · 584-1 · 584-3) — 켠 겹의 받은 범위 안 수 · 넓이.
|
||||||
* 급경사 = 고른 범위(603)마다 % · ° · 비율(1 % 칸 수로 바로 셈).
|
* 급경사 = 고른 범위(603)마다 % · ° · 비율(1 % 칸 수로 바로 셈).
|
||||||
|
* 범례 칸 · 조각 겹 범례 = 공용 `A00_Common/map/map_legend`(624-2) · 여기는 받은 자료 줄만.
|
||||||
* 부르는 곳 = `D03_PlanAnalysis_UI_Layers.ts`(받기 · 그리기).
|
* 부르는 곳 = `D03_PlanAnalysis_UI_Layers.ts`(받기 · 그리기).
|
||||||
* ========================================================================== */
|
* ========================================================================== */
|
||||||
|
|
||||||
@@ -16,15 +17,14 @@ import type {
|
|||||||
PlanZones,
|
PlanZones,
|
||||||
} from "./D03_PlanAnalysis_Api";
|
} from "./D03_PlanAnalysis_Api";
|
||||||
import {
|
import {
|
||||||
LANDSLIDE_LEGEND,
|
|
||||||
LINE_KEYS,
|
LINE_KEYS,
|
||||||
LINE_LAYERS,
|
LINE_LAYERS,
|
||||||
ZONE_COLORS,
|
ZONE_COLORS,
|
||||||
hectare,
|
|
||||||
type LineLayerKey,
|
type LineLayerKey,
|
||||||
type PlanLayerKey,
|
type MapLayerKey,
|
||||||
} from "./D03_PlanAnalysis_UI_LayerDefs";
|
} from "../A00_Common/map/map_layer_list";
|
||||||
import { forestLegendRows } from "./D03_PlanAnalysis_UI_ForestLayers";
|
import { tileLegendRows } from "../A00_Common/map/map_legend";
|
||||||
|
import { hectare } from "./D03_PlanAnalysis_UI_LayerDefs";
|
||||||
import {
|
import {
|
||||||
SLOPE_MAX_PCT,
|
SLOPE_MAX_PCT,
|
||||||
bandRatios,
|
bandRatios,
|
||||||
@@ -111,13 +111,12 @@ function landslideZoneRow({ info }: Prepared["landslideZones"]): LegendItem[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function legendRows(
|
export function legendRows(
|
||||||
on: Record<PlanLayerKey, boolean>,
|
on: Record<MapLayerKey, boolean>,
|
||||||
data: Partial<Prepared>,
|
data: Partial<Prepared>,
|
||||||
): LegendItem[][] {
|
): LegendItem[][] {
|
||||||
const rows: LegendItem[][] = [];
|
const rows: LegendItem[][] = [];
|
||||||
if (on.slope && data.slope) rows.push(slopeRow(data.slope));
|
if (on.slope && data.slope) rows.push(slopeRow(data.slope));
|
||||||
if (on.landslide) rows.push(LANDSLIDE_LEGEND); // GL = 조각(받기 없음 · 622-2)
|
rows.push(...tileLegendRows(on)); // 조각 겹 = 받기 없음(622-2 · 공용 범례)
|
||||||
rows.push(...forestLegendRows(on));
|
|
||||||
if (on.landslideZones && data.landslideZones) rows.push(landslideZoneRow(data.landslideZones));
|
if (on.landslideZones && data.landslideZones) rows.push(landslideZoneRow(data.landslideZones));
|
||||||
if (on.zones && data.zones) {
|
if (on.zones && data.zones) {
|
||||||
const items = Object.entries(data.zones.summary)
|
const items = Object.entries(data.zones.summary)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* =============================================================================
|
/* =============================================================================
|
||||||
* D03_PlanAnalysis_UI_Layers.ts
|
* D03_PlanAnalysis_UI_Layers.ts
|
||||||
* D03 평면 바탕 겹 켜고 끔(PLAN 569-2 · 581-1 · 581-2 · 584-1 · 584-3 · 요구 395-2 · 440-8 · 442 · 443) — 지도 왼쪽 위 아이콘 한 줄
|
* D03 평면 바탕 겹 켜고 끔(PLAN 569-2 · 581-1 · 581-2 · 584-1 · 584-3 · 624-2 · 요구 395-2 · 440-8 · 442 · 443) — 지도 왼쪽 위 아이콘 한 줄
|
||||||
* (공용 지도 도구줄 `createMapTools` · 넘치면 가로 밀기 571-1 · 오른쪽 평면 도구줄과 따로).
|
* (공용 겹 줄 `A00_Common/map/map_layer_bar` · 넘치면 가로 밀기 571-1 · 오른쪽 평면 도구줄과 따로).
|
||||||
*
|
*
|
||||||
* 겹 = 바탕(위성 · 음영기복 · 도엽 등고선(계곡선 25 m 굵게 · 수치) · 급경사 색) ·
|
* 겹 = 바탕(위성 · 음영기복 · 도엽 등고선(계곡선 25 m 굵게 · 수치) · 급경사 색) ·
|
||||||
* 물 · 길(세류선 · 하천 · 물 면(실폭하천 · 저수지) · 기존 도로 · 기존 임도 짐작(소로 · 미분류) ·
|
* 물 · 길(세류선 · 하천 · 물 면(실폭하천 · 저수지) · 기존 도로 · 기존 임도 짐작(소로 · 미분류) ·
|
||||||
@@ -9,28 +9,29 @@
|
|||||||
* 구역(지적도 · 보호 · 규제 구역 여덟 종 · 산림입지도(토양형) · 산사태위험지도 · 산사태취약지역(시도 표 점 · 지번 필지 테두리) ·
|
* 구역(지적도 · 보호 · 규제 구역 여덟 종 · 산림입지도(토양형) · 산사태위험지도 · 산사태취약지역(시도 표 점 · 지번 필지 테두리) ·
|
||||||
* 선 겹(단층 · 지질구조선 · 0차곡) ·
|
* 선 겹(단층 · 지질구조선 · 0차곡) ·
|
||||||
* 계류 100 m · 도로 300 m 띠 · 건물 · 지명).
|
* 계류 100 m · 도로 300 m 띠 · 건물 · 지명).
|
||||||
* 처음 켬 = 위성 · 등고선 · 세류 · 고른 값 = 화면 취향(`aislo:pref:d03.layers`) 기억.
|
* 겹 목록 · 처음 켬(위성 · 등고선 · 세류) · 취향(`aislo:pref:d03.layers`) = 공용 `map_layer_list` 페이지 표 「d03」.
|
||||||
* 받기 = 켤 때 · 받은 지형 범위 지문(`load(have)` · 페이지가 지형면을 다 읽은 뒤 넘김)이 바뀌면 차례 +1 · 켠 겹 모두 다시 ·
|
* 받기 = 켤 때 · 받은 지형 범위 지문(`load(have)` · 페이지가 지형면을 다 읽은 뒤 넘김)이 바뀌면 차례 +1 · 켠 겹 모두 다시 ·
|
||||||
* 옛 차례 답은 버림(475) · 도엽 선 겹(등고 · 세류 · 물 면 · 도로 · 건물 · 지명) = 보이는 도엽 조각(606 · `UI_SheetTiles`) · 지형을 안 받았으면 단추를 막음 ·
|
* 옛 차례 답은 버림(475) · 도엽 선 겹(등고 · 세류 · 물 면 · 도로 · 건물 · 지명) = 보이는 도엽 조각(606 · `UI_SheetTiles`) · 지형을 안 받았으면 단추를 막음 ·
|
||||||
* 받기 실패 = 그 겹만 꺼짐 + 알림 · 받는 일은 페이지 서클에 셈(`track`).
|
* 받기 실패 = 그 겹만 꺼짐 + 알림 · 받는 일은 페이지 서클에 셈(`track`).
|
||||||
* 그리기 차례(아래 → 위) = 위성(GL 조각만 · 옛 캔버스 = 위성 없음 · 590-2) → 음영기복 → 급경사 → 산사태위험지도 → 산림입지도 → 보호구역 → 띠 → 지적 → 물 면 →
|
* 그리기 차례(아래 → 위) = 위성(GL 조각만 · 옛 캔버스 = 위성 없음 · 590-2) → 음영기복 → 급경사 → 산사태위험지도 → 산림입지도 → 보호구역 → 띠 → 지적 → 물 면 →
|
||||||
* 건물 → 등고선 → 도로 → 임도 짐작 → 올린 임도망도 → 세류 → 선 겹 → 산사태취약지역 → 지명 → 그은 선.
|
* 건물 → 등고선 → 도로 → 임도 짐작 → 올린 임도망도 → 세류 → 선 겹 → 산사태취약지역 → 지명 → 그은 선.
|
||||||
* 겹 모두 = 바탕 캔버스(`base` · 582) · 등고선 = 줌 단계 색인 · 글 폭 기억.
|
* 겹 모두 = 바탕 캔버스(`base` · 582) · 등고선 = 줌 단계 색인 · 글 폭 기억.
|
||||||
* 범례 = 켠 겹의 받은 범위 안 수 · 넓이(왼쪽 아래 · `D03_PlanAnalysis_UI_LayerLegend`).
|
* 범례 = 켠 겹의 받은 범위 안 수 · 넓이(왼쪽 아래 · 칸 = 공용 `map_legend` · 줄 = `D03_PlanAnalysis_UI_LayerLegend`).
|
||||||
* 급경사 = 서버 값 그림을 고른 범위대로 칠함 · 범례 ⚙ → 범위 모달(603 · `D03_PlanAnalysis_UI_SlopeBands`).
|
* 급경사 = 서버 값 그림을 고른 범위대로 칠함 · 범례 ⚙ → 범위 모달(603 · `D03_PlanAnalysis_UI_SlopeBands`).
|
||||||
* GL 바탕(586-2 · 처음 = 켬 · 끝 단추 끔 = 옛 캔버스 · `aislo:pref:d03.canvas`) — 겹 스물 · 등고 글 · 지명을
|
* GL 바탕(586-2 · 처음 = 켬 · 끝 단추 끔 = 옛 캔버스 · `aislo:pref:d03.canvas`) — 겹 스물 · 등고 글 · 지명을
|
||||||
* MapLibre 가 그림(`D03_PlanAnalysis_UI_MapGl`) · 캔버스 바탕 겹은 쉼(등고 간격 셈만) · 받기 · 범례는 같음 ·
|
* MapLibre 가 그림(`D03_PlanAnalysis_UI_MapGl`) · 캔버스 바탕 겹은 쉼(등고 간격 셈만) · 받기 · 범례는 같음 ·
|
||||||
* WebGL 못 씀 · 잃음 = 저절로 옛 캔버스 + 알림(취향은 안 바꿈).
|
* WebGL 못 씀 · 잃음 = 저절로 옛 캔버스 + 알림(취향은 안 바꿈).
|
||||||
* 받은 산림 · 환경 자료(622-2) = 국유림 임도 · 경제림 · 생태자연도(조각만 · 받기 없음) · 산림입지도 · 산사태위험(GL = 조각 ·
|
* 받은 산림 · 환경 자료(622-2) = 국유림 임도 · 경제림 · 생태자연도(조각만 · 받기 없음) · 산림입지도 · 산사태위험(GL = 조각 ·
|
||||||
* 옛 캔버스 = 지금 받기 그대로) — 꼴 · 범례 = `D03_PlanAnalysis_UI_ForestLayers`.
|
* 옛 캔버스 = 지금 받기 그대로) — 꼴 · 범례 = 공용 `A00_Common/map/map_forest_layers`.
|
||||||
* ========================================================================== */
|
* ========================================================================== */
|
||||||
|
|
||||||
import { showToast } from "@ui/ui_template_elements";
|
import { showToast } from "@ui/ui_template_elements";
|
||||||
import { createLegend } from "@ui/ui_template_flow_legend";
|
|
||||||
import { createIconButton } from "@ui/ui_template_button";
|
|
||||||
import { createMapTools } from "@ui/ui_template_map_tools";
|
|
||||||
import { themeColor } from "@ui/ui_template_palette";
|
import { themeColor } from "@ui/ui_template_palette";
|
||||||
import { readByKey, writeByKey } from "../A00_Common/b_page_state";
|
import { readByKey, writeByKey } from "../A00_Common/b_page_state";
|
||||||
|
import { soilColor, tileOnly, type TileOnlyKey } from "../A00_Common/map/map_forest_layers";
|
||||||
|
import { createMapLayerBar } from "../A00_Common/map/map_layer_bar";
|
||||||
|
import { LAYERS, LINE_LAYERS, ZONE_COLORS, type RoadPick } from "../A00_Common/map/map_layer_list";
|
||||||
|
import { createMapLegend } from "../A00_Common/map/map_legend";
|
||||||
import { fetchVWorldMeta, type VWorldMeta } from "../B04_PreProcess/B04_PreProcess_Api_Fetch";
|
import { fetchVWorldMeta, type VWorldMeta } from "../B04_PreProcess/B04_PreProcess_Api_Fetch";
|
||||||
import {
|
import {
|
||||||
createNormalizer,
|
createNormalizer,
|
||||||
@@ -57,23 +58,16 @@ import {
|
|||||||
} from "./D03_PlanAnalysis_Api";
|
} from "./D03_PlanAnalysis_Api";
|
||||||
import {
|
import {
|
||||||
FOREST_ROAD_KINDS,
|
FOREST_ROAD_KINDS,
|
||||||
GROUPS,
|
|
||||||
LAYERS,
|
|
||||||
LINE_LAYERS,
|
|
||||||
WATER_KEYS,
|
WATER_KEYS,
|
||||||
ZONE_COLORS,
|
|
||||||
glImage,
|
glImage,
|
||||||
glTagged,
|
glTagged,
|
||||||
loadImage,
|
loadImage,
|
||||||
readPref,
|
|
||||||
shadeImage,
|
shadeImage,
|
||||||
type PlanLayerKey,
|
type PlanLayerKey,
|
||||||
type RoadPick,
|
|
||||||
} from "./D03_PlanAnalysis_UI_LayerDefs";
|
} from "./D03_PlanAnalysis_UI_LayerDefs";
|
||||||
import { layerCounts, legendRows, type Prepared } from "./D03_PlanAnalysis_UI_LayerLegend";
|
import { layerCounts, legendRows, type Prepared } from "./D03_PlanAnalysis_UI_LayerLegend";
|
||||||
import { createCanvasLayers } from "./D03_PlanAnalysis_UI_LayerPaint";
|
import { createCanvasLayers } from "./D03_PlanAnalysis_UI_LayerPaint";
|
||||||
import { createGlBase, glFont, type GlImage } from "./D03_PlanAnalysis_UI_MapGl";
|
import { createGlBase, glFont, type GlImage } from "./D03_PlanAnalysis_UI_MapGl";
|
||||||
import { soilColor, tileOnly, type TileOnlyKey } from "./D03_PlanAnalysis_UI_ForestLayers";
|
|
||||||
import { createSatLightRow, readSatLight } from "./D03_PlanAnalysis_UI_SatLight";
|
import { createSatLightRow, readSatLight } from "./D03_PlanAnalysis_UI_SatLight";
|
||||||
import {
|
import {
|
||||||
createSheetTiles,
|
createSheetTiles,
|
||||||
@@ -90,7 +84,6 @@ import {
|
|||||||
} from "./D03_PlanAnalysis_UI_SlopeBands";
|
} from "./D03_PlanAnalysis_UI_SlopeBands";
|
||||||
import type { PlanMap, PlanMapLayer } from "./D03_PlanAnalysis_UI_Map";
|
import type { PlanMap, PlanMapLayer } from "./D03_PlanAnalysis_UI_Map";
|
||||||
|
|
||||||
const PREF_KEY = "aislo:pref:d03.layers";
|
|
||||||
/** 옛 캔버스로 그림("1") — 처음 = GL. */
|
/** 옛 캔버스로 그림("1") — 처음 = GL. */
|
||||||
const CANVAS_PREF_KEY = "aislo:pref:d03.canvas";
|
const CANVAS_PREF_KEY = "aislo:pref:d03.canvas";
|
||||||
/** 옛 캔버스 알림 — 위성 = GL 조각만(590-2). */
|
/** 옛 캔버스 알림 — 위성 = GL 조각만(590-2). */
|
||||||
@@ -137,7 +130,6 @@ export function createPlanLayers(opts: {
|
|||||||
track: <T>(job: Promise<T>) => Promise<T>;
|
track: <T>(job: Promise<T>) => Promise<T>;
|
||||||
}): PlanLayers {
|
}): PlanLayers {
|
||||||
const { projectId, map, track } = opts;
|
const { projectId, map, track } = opts;
|
||||||
const on = readPref(PREF_KEY);
|
|
||||||
const data: Partial<Prepared> = {};
|
const data: Partial<Prepared> = {};
|
||||||
const pending = new Map<DataKey | "satellite", Promise<void>>();
|
const pending = new Map<DataKey | "satellite", Promise<void>>();
|
||||||
let meta: VWorldMeta | null = null;
|
let meta: VWorldMeta | null = null;
|
||||||
@@ -152,6 +144,22 @@ export function createPlanLayers(opts: {
|
|||||||
useGl(false);
|
useGl(false);
|
||||||
});
|
});
|
||||||
let glOn = readByKey(CANVAS_PREF_KEY) !== "1";
|
let glOn = readByKey(CANVAS_PREF_KEY) !== "1";
|
||||||
|
/** 겹 줄 · 켬끔 · 취향 = 공용 겹 목록(624-2 · 페이지 표 「d03」). */
|
||||||
|
const layerBar = createMapLayerBar({
|
||||||
|
page: "d03",
|
||||||
|
glOn,
|
||||||
|
onToggle: (key) => {
|
||||||
|
void ensure(key);
|
||||||
|
sync();
|
||||||
|
map.redrawBase();
|
||||||
|
},
|
||||||
|
onGl: () => {
|
||||||
|
writeByKey(CANVAS_PREF_KEY, glOn ? "1" : "0");
|
||||||
|
useGl(!glOn);
|
||||||
|
if (!glOn) showToast(NO_SATELLITE_NOTE, "info");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { on, toggle } = layerBar;
|
||||||
/** 도엽 조각(606) — 조각이 오면 그 겹만 합친 자료로 다시 얹음. */
|
/** 도엽 조각(606) — 조각이 오면 그 겹만 합친 자료로 다시 얹음. */
|
||||||
const tiles = createSheetTiles({
|
const tiles = createSheetTiles({
|
||||||
projectId,
|
projectId,
|
||||||
@@ -166,78 +174,34 @@ export function createPlanLayers(opts: {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const buttons = new Map<PlanLayerKey, HTMLButtonElement>();
|
|
||||||
const groups = GROUPS.map((group) =>
|
|
||||||
group.map((def) => {
|
|
||||||
const button = createIconButton({
|
|
||||||
icon: def.icon,
|
|
||||||
title: def.title,
|
|
||||||
size: 32,
|
|
||||||
pressed: on[def.key],
|
|
||||||
onClick: () => toggle(def.key),
|
|
||||||
});
|
|
||||||
button.dataset.layer = def.key;
|
|
||||||
buttons.set(def.key, button);
|
|
||||||
return button;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
const glButton = createIconButton({
|
|
||||||
icon: "gauge",
|
|
||||||
title: "그래픽 카드 그림(바탕 겹 · 글을 GPU 로 · 끔 = 옛 캔버스 그림)",
|
|
||||||
size: 32,
|
|
||||||
pressed: glOn,
|
|
||||||
onClick: () => {
|
|
||||||
writeByKey(CANVAS_PREF_KEY, glOn ? "1" : "0");
|
|
||||||
useGl(!glOn);
|
|
||||||
if (!glOn) showToast(NO_SATELLITE_NOTE, "info");
|
|
||||||
},
|
|
||||||
});
|
|
||||||
glButton.dataset.layer = "gl";
|
|
||||||
/** GL ↔ 옛 캔버스 — 못 켜면 캔버스(알림은 `onFail`). */
|
/** GL ↔ 옛 캔버스 — 못 켜면 캔버스(알림은 `onFail`). */
|
||||||
function useGl(want: boolean): void {
|
function useGl(want: boolean): void {
|
||||||
glOn = want && map.setGl(gl);
|
glOn = want && map.setGl(gl);
|
||||||
if (!glOn) map.setGl(null);
|
if (!glOn) map.setGl(null);
|
||||||
glButton.setAttribute("aria-pressed", String(glOn));
|
|
||||||
if (!glOn) void Promise.all(LAYERS.map((def) => ensure(def.key))); // 산사태위험 한 장(622-2)
|
if (!glOn) void Promise.all(LAYERS.map((def) => ensure(def.key))); // 산사태위험 한 장(622-2)
|
||||||
sync();
|
sync();
|
||||||
map.redrawBase();
|
map.redrawBase();
|
||||||
}
|
}
|
||||||
groups.push([glButton]);
|
const bar = layerBar.root;
|
||||||
const tools = createMapTools({ label: "지도 겹", groups });
|
|
||||||
const bar = tools.root;
|
|
||||||
bar.classList.add("d03-plan__layers");
|
bar.classList.add("d03-plan__layers");
|
||||||
// 571-1 — 오른쪽 평면 도구줄이 이 줄 오른쪽부터(CSS `--d03-layers-w`)
|
// 571-1 — 오른쪽 평면 도구줄이 이 줄 오른쪽부터(CSS `--d03-layers-w`)
|
||||||
new ResizeObserver(() =>
|
new ResizeObserver(() =>
|
||||||
map.root.style.setProperty("--d03-layers-w", `${tools.bar.offsetWidth}px`),
|
map.root.style.setProperty("--d03-layers-w", `${layerBar.bar.offsetWidth}px`),
|
||||||
).observe(tools.bar);
|
).observe(layerBar.bar);
|
||||||
const legend = document.createElement("div");
|
const legendBox = createMapLegend("d03-plan__legend");
|
||||||
legend.className = "d03-plan__legend";
|
const legend = legendBox.root;
|
||||||
legend.title = "켠 겹 범례 — 받은 지형 범위 안 수 · 넓이";
|
|
||||||
|
|
||||||
/** 받음 — 위성 · 산림 조각 = GL 조각(받을 것 없음 · 옛 캔버스 = 안 그림) · 산사태위험 = GL 이면 조각(622-2). */
|
/** 받음 — 위성 · 산림 조각 = GL 조각(받을 것 없음 · 옛 캔버스 = 안 그림) · 산사태위험 = GL 이면 조각(622-2). */
|
||||||
const loadedOf = (key: PlanLayerKey): boolean =>
|
const loadedOf = (key: PlanLayerKey): boolean =>
|
||||||
tileOnly(key) || (glOn && key === "landslide") || !!data[dataKey(key)];
|
tileOnly(key) || (glOn && key === "landslide") || !!data[dataKey(key)];
|
||||||
|
|
||||||
function sync(): void {
|
function sync(): void {
|
||||||
for (const def of LAYERS) {
|
layerBar.sync({ ready: !!meta, glOn });
|
||||||
const button = buttons.get(def.key)!;
|
|
||||||
button.setAttribute("aria-pressed", String(on[def.key]));
|
|
||||||
button.disabled = !meta;
|
|
||||||
const title = !meta
|
|
||||||
? `${def.title} — 지형 받기 뒤 보임`
|
|
||||||
: tileOnly(def.key) && !glOn
|
|
||||||
? `${def.title} — 옛 그림(그래픽 카드 끔)엔 안 보임`
|
|
||||||
: def.title;
|
|
||||||
button.title = title;
|
|
||||||
button.setAttribute("aria-label", title);
|
|
||||||
}
|
|
||||||
for (const def of LAYERS) gl.setVisible(def.key, on[def.key]);
|
for (const def of LAYERS) gl.setVisible(def.key, on[def.key]);
|
||||||
gl.setData("hillshade", hillshade && (on.satellite ? hillshade.shade : hillshade.plain));
|
gl.setData("hillshade", hillshade && (on.satellite ? hillshade.shade : hillshade.plain));
|
||||||
const rows = meta ? legendRows(on, data) : [];
|
|
||||||
const light = meta && glOn && on.satellite; // 옛 캔버스 = 위성 없음
|
const light = meta && glOn && on.satellite; // 옛 캔버스 = 위성 없음
|
||||||
legend.replaceChildren(...rows.map((row) => createLegend(row)), ...(light ? [lightRow] : []));
|
legendBox.show(meta ? legendRows(on, data) : [], light ? [lightRow] : []);
|
||||||
if (meta && on.slope && data.slope) legend.firstElementChild?.append(gear);
|
if (meta && on.slope && data.slope) legend.firstElementChild?.append(gear);
|
||||||
legend.hidden = !rows.length && !light;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 위성 밝기(요구 475) — 위성 켠 동안 범례 맨 아래 한 줄 · 바로 반영. */
|
/** 위성 밝기(요구 475) — 위성 켠 동안 범례 맨 아래 한 줄 · 바로 반영. */
|
||||||
@@ -279,14 +243,6 @@ export function createPlanLayers(opts: {
|
|||||||
sync();
|
sync();
|
||||||
useGl(glOn);
|
useGl(glOn);
|
||||||
|
|
||||||
function toggle(key: PlanLayerKey): void {
|
|
||||||
on[key] = !on[key];
|
|
||||||
writeByKey(PREF_KEY, JSON.stringify(on));
|
|
||||||
void ensure(key);
|
|
||||||
sync();
|
|
||||||
map.redrawBase();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 켜진 겹의 자료를 받음(한 번) — 실패 = 그 겹만 끔(기억은 안 바꿈 · 다음에 다시 받음) + 알림. */
|
/** 켜진 겹의 자료를 받음(한 번) — 실패 = 그 겹만 끔(기억은 안 바꿈 · 다음에 다시 받음) + 알림. */
|
||||||
function ensure(key: PlanLayerKey): Promise<void> {
|
function ensure(key: PlanLayerKey): Promise<void> {
|
||||||
if (!meta || !on[key] || loadedOf(key)) return Promise.resolve();
|
if (!meta || !on[key] || loadedOf(key)) return Promise.resolve();
|
||||||
@@ -366,7 +322,7 @@ export function createPlanLayers(opts: {
|
|||||||
gl.setRoadPick(null);
|
gl.setRoadPick(null);
|
||||||
}
|
}
|
||||||
on.forestRoadFile = !!info;
|
on.forestRoadFile = !!info;
|
||||||
writeByKey(PREF_KEY, JSON.stringify(on));
|
layerBar.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteRoad(bundle: string): Promise<boolean> {
|
async function deleteRoad(bundle: string): Promise<boolean> {
|
||||||
|
|||||||
@@ -1,47 +1,37 @@
|
|||||||
/* =============================================================================
|
/* =============================================================================
|
||||||
* D03_PlanAnalysis_UI_MapGl.ts
|
* D03_PlanAnalysis_UI_MapGl.ts
|
||||||
* D03 GL 바탕(PLAN 586-1 · 586-2 · 요구 445 B · 448) — 바탕 겹 스물을 MapLibre(WebGL · BSD-3)로:
|
* D03 GL 바탕(PLAN 586-1 · 586-2 · 624-2 · 요구 445 B · 448) — 바탕 겹 스물을 MapLibre(WebGL · BSD-3)로.
|
||||||
* 위성 조각(서버 대리 · 서버 기억 z6~19) · 그림 겹(음영 · 급경사 · 산사태위험 = image) ·
|
* 겹 꼴 · 차례 = 공용 `A00_Common/map/map_layer_specs`(624-2 · B05 평면과 같은 꼴) · 여기는 D03 지도 세우기만:
|
||||||
* 면 겹(산림입지 · 보호구역 · 띠 · 물 면 · 건물 = fill) · 선 겹(지적 · 등고선 · 도로 · 임도 짐작 · 올린 임도망도 ·
|
* 고정 소스(위성 조각 z6~19 · 등고선 615 줌별 벡터 조각 · 산림 조각 다섯 622-2) · 받는 동안 서클 · 임도망 고름 · 위성 밝기.
|
||||||
* 세류 · 단층 · 지질구조선 · 0차곡 · 산사태취약 필지 = line · 점선 포함) · 점(산사태취약 · 지명 = circle) ·
|
* 글 = 화면 Noto Sans KR(OFL · 자체 보관)을 그 자리에서 그림 — 꼴에 글꼴 주소가 없으면 MapLibre 가 모든 글자를 화면 글꼴로.
|
||||||
* 글(등고 수치 · 지명 = symbol · 겹치면 뺌 · 글꼴 = 화면 Noto Sans KR(OFL · 자체 보관)을 그 자리에서 그림 —
|
|
||||||
* 꼴에 글꼴 주소가 없으면 MapLibre 가 모든 글자를 화면 글꼴로 그림 · 글꼴 조각 파일 없음).
|
|
||||||
* 그은 선 · 재기 · 고르기 · 누름 = 위 캔버스 그대로(위에 겹침).
|
* 그은 선 · 재기 · 고르기 · 누름 = 위 캔버스 그대로(위에 겹침).
|
||||||
* 카메라 주인 = D03 화면(`ViewState` · 돌림) 하나 — 여기는 따라가기만 · 조작은 안 받음.
|
* 카메라 주인 = D03 화면(`ViewState` · 돌림) 하나 — 여기는 따라가기만 · 조작은 안 받음.
|
||||||
* 자료 = 지금 받는 GeoJSON 그대로(조각내기는 MapLibre 일꾼 스레드) · 색 = 받는 쪽이 지물에 붙인 `_color`.
|
* 자료 = 지금 받는 GeoJSON 그대로(조각내기는 MapLibre 일꾼 스레드) · 색 = 받는 쪽이 지물에 붙인 `_color`.
|
||||||
* 등고선 = 줌별 벡터 조각(615 · 위성처럼 고정 소스 · 가는 선 = 모두 · 굵은 선 · 글 = `k` 1) · 받는 동안 서클.
|
|
||||||
* 받은 산림 · 환경 자료(622-2) = 같은 꼴 고정 소스 다섯(`D03_PlanAnalysis_UI_ForestLayers`) · 받는 동안 서클.
|
|
||||||
* 그래픽 카드 · WebGL 이 없거나 잃으면 `onFail` — 부르는 쪽이 옛 캔버스로.
|
* 그래픽 카드 · WebGL 이 없거나 잃으면 `onFail` — 부르는 쪽이 옛 캔버스로.
|
||||||
* 지도 세우기 · 자료 올리기 · 카메라 = 공용 부품 `ui_template_gl_map`(589-2) · 여기는 겹 꼴만.
|
* 지도 세우기 · 자료 올리기 · 카메라 = 공용 부품 `ui_template_gl_map`(589-2).
|
||||||
* ========================================================================== */
|
* ========================================================================== */
|
||||||
|
|
||||||
import type { ExpressionSpecification, FilterSpecification, Map as MapLibreMap } from "maplibre-gl";
|
import type { Map as MapLibreMap } from "maplibre-gl";
|
||||||
import {
|
import {
|
||||||
contourTileSource,
|
contourTileSource,
|
||||||
createGlMap,
|
createGlMap,
|
||||||
GL_FONT,
|
GL_FONT,
|
||||||
satelliteTileSource,
|
satelliteTileSource,
|
||||||
type GlImage,
|
type GlImage,
|
||||||
type GlSpec as GlMapSpec,
|
|
||||||
} from "@ui/ui_template_gl_map";
|
} from "@ui/ui_template_gl_map";
|
||||||
import { themeColor } from "@ui/ui_template_palette";
|
|
||||||
import type { GeoJsonCollection } from "../B04_PreProcess/B04_PreProcess_UI_MapRender";
|
import type { GeoJsonCollection } from "../B04_PreProcess/B04_PreProcess_UI_MapRender";
|
||||||
import type { PlanLayerKey, RoadPick } from "./D03_PlanAnalysis_UI_LayerDefs";
|
import { forestSources, TRACKED_SOURCES } from "../A00_Common/map/map_forest_layers";
|
||||||
import {
|
import {
|
||||||
forestAreaSpecs,
|
MAP_THEME_NAMES,
|
||||||
forestRasterSpecs,
|
mapLayerSpecs,
|
||||||
forestRoadSpecs,
|
pickFilter,
|
||||||
forestSources,
|
type GlSource,
|
||||||
TRACKED_SOURCES,
|
} from "../A00_Common/map/map_layer_specs";
|
||||||
} from "./D03_PlanAnalysis_UI_ForestLayers";
|
import type { PlanLayerKey, RoadPick } from "./D03_PlanAnalysis_UI_LayerDefs";
|
||||||
import { SAT_LIGHT_DEFAULT } from "./D03_PlanAnalysis_UI_SatLight";
|
import { SAT_LIGHT_DEFAULT } from "./D03_PlanAnalysis_UI_SatLight";
|
||||||
|
|
||||||
export { GL_FONT, type GlImage };
|
export type { GlImage };
|
||||||
|
|
||||||
/** 자료 소스(임도 짐작 = 도로 소스를 거름 · 위성 = 조각). */
|
|
||||||
export type GlSource = Exclude<PlanLayerKey, "satellite" | "forestRoad">;
|
|
||||||
|
|
||||||
const GL_FONT_BOLD = [`${GL_FONT} SemiBold`, GL_FONT];
|
|
||||||
/** GL 글꼴 미리 받음 — 받기 전에 그린 글자는 대체 글꼴로 굳음(못 받아도 그림). */
|
/** GL 글꼴 미리 받음 — 받기 전에 그린 글자는 대체 글꼴로 굳음(못 받아도 그림). */
|
||||||
export const glFont = (weight: string, text: string): Promise<unknown> =>
|
export const glFont = (weight: string, text: string): Promise<unknown> =>
|
||||||
document.fonts.load(`${weight} 24px "${GL_FONT}"`, text || "가").catch(() => null);
|
document.fonts.load(`${weight} 24px "${GL_FONT}"`, text || "가").catch(() => null);
|
||||||
@@ -62,304 +52,6 @@ export interface GlBase {
|
|||||||
resize: () => void;
|
resize: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type GlSpec = GlMapSpec<PlanLayerKey>;
|
|
||||||
|
|
||||||
const HALO = "rgba(0, 0, 0, 0.55)";
|
|
||||||
const ROUND = { "line-join": "round", "line-cap": "round" } as const;
|
|
||||||
const COLOR: ExpressionSpecification = ["get", "_color"];
|
|
||||||
/** 등고 조각 굵은 선(서버가 줌마다 정함) — 가는 선은 모두라 굵은 선은 두 번 겹쳐 짙음(캔버스처럼). */
|
|
||||||
const BOLD = ["==", ["get", "k"], 1] as unknown as FilterSpecification;
|
|
||||||
/** 캔버스 점선(px) → MapLibre 점선(선 굵기 배수). */
|
|
||||||
const dash = (px: number[], width: number): number[] => px.map((v) => v / width);
|
|
||||||
|
|
||||||
/** 선 겹 — 검은 테두리 + 선(점선이면 둘 다 같은 px 무늬). */
|
|
||||||
function haloLine(
|
|
||||||
key: PlanLayerKey,
|
|
||||||
source: GlSource,
|
|
||||||
color: string,
|
|
||||||
width: number,
|
|
||||||
pattern: number[] = [],
|
|
||||||
halo = { color: HALO, width: width + 1.6 },
|
|
||||||
filter?: FilterSpecification,
|
|
||||||
): GlSpec[] {
|
|
||||||
const line = (id: string, paint: Record<string, unknown>, w: number): GlSpec =>
|
|
||||||
({
|
|
||||||
key,
|
|
||||||
id,
|
|
||||||
type: "line",
|
|
||||||
source,
|
|
||||||
...(filter ? { filter } : {}),
|
|
||||||
layout: ROUND,
|
|
||||||
paint: {
|
|
||||||
...paint,
|
|
||||||
"line-width": w,
|
|
||||||
...(pattern.length ? { "line-dasharray": dash(pattern, w) } : {}),
|
|
||||||
},
|
|
||||||
}) as GlSpec;
|
|
||||||
return [
|
|
||||||
line(`${key}-halo`, { "line-color": halo.color }, halo.width),
|
|
||||||
line(key, { "line-color": color }, width),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 면 겹 — 채움 + 테두리(캔버스 `fillLayer` 처럼 테두리 투명도 0.9). */
|
|
||||||
function fillEdge(
|
|
||||||
key: PlanLayerKey,
|
|
||||||
source: GlSource,
|
|
||||||
fill: string | ExpressionSpecification,
|
|
||||||
opacity: number | ExpressionSpecification,
|
|
||||||
edge: { color: string | ExpressionSpecification; width: number; dash?: number[] },
|
|
||||||
): GlSpec[] {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
key,
|
|
||||||
id: key,
|
|
||||||
type: "fill",
|
|
||||||
source,
|
|
||||||
paint: { "fill-color": fill, "fill-opacity": opacity },
|
|
||||||
} as GlSpec,
|
|
||||||
{
|
|
||||||
key,
|
|
||||||
id: `${key}-edge`,
|
|
||||||
type: "line",
|
|
||||||
source,
|
|
||||||
layout: ROUND,
|
|
||||||
paint: {
|
|
||||||
"line-color": edge.color,
|
|
||||||
"line-width": edge.width,
|
|
||||||
"line-opacity": 0.9,
|
|
||||||
...(edge.dash ? { "line-dasharray": dash(edge.dash, edge.width) } : {}),
|
|
||||||
},
|
|
||||||
} as GlSpec,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 겹 차례(아래 → 위)와 꼴 — 캔버스 `D03_PlanAnalysis_UI_Layers` 의 색 · 굵기 · 투명도 · 차례 그대로. */
|
|
||||||
function layerSpecs(light: number, pick: RoadPick | null): GlSpec[] {
|
|
||||||
const contour = themeColor("--draw-map-contour", "#ffd68c");
|
|
||||||
const place = themeColor("--draw-map-place", "#f8fafc");
|
|
||||||
const landslideZone = themeColor("--draw-map-landslide-zone", "#e11d48");
|
|
||||||
const raster = (
|
|
||||||
key: "satellite" | "hillshade" | "slope",
|
|
||||||
opacity: number,
|
|
||||||
extra: Record<string, number> = {},
|
|
||||||
): GlSpec =>
|
|
||||||
({
|
|
||||||
key,
|
|
||||||
id: key,
|
|
||||||
type: "raster",
|
|
||||||
source: key,
|
|
||||||
paint: { "raster-opacity": opacity, "raster-fade-duration": 0, ...extra },
|
|
||||||
}) as GlSpec;
|
|
||||||
const lineKind = (key: "faults" | "lineaments" | "zeroOrder", width: number, pattern: number[]) =>
|
|
||||||
haloLine(key, key, themeColor(LINE_COLOR[key], "#f43f5e"), width, pattern);
|
|
||||||
return [
|
|
||||||
// 위성 = 조금 어둡고 연하게(요구 475 · 노선 선 대비) — 밝기 상한은 사용자 조절
|
|
||||||
raster("satellite", 1, { "raster-brightness-max": light, "raster-saturation": SAT_SATURATION }),
|
|
||||||
raster("hillshade", 1), // 위성 위 = 「곱하기」와 같은 검은 그늘 그림(받는 쪽이 바꿈)
|
|
||||||
raster("slope", 0.55),
|
|
||||||
...forestRasterSpecs(), // 산사태위험 · 생태자연도 = 조각 그림(622-2)
|
|
||||||
...forestAreaSpecs(), // 산림입지도 · 경제림 = 벡터 조각
|
|
||||||
...fillEdge("zones", "zones", COLOR, 0.22, { color: COLOR, width: 1.6 }),
|
|
||||||
...fillEdge("bands", "bands", COLOR, ["case", ["==", ["get", "_band"], "road"], 0.16, 0.18], {
|
|
||||||
color: COLOR,
|
|
||||||
width: 1.2,
|
|
||||||
dash: [6, 4],
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
key: "cadastral",
|
|
||||||
id: "cadastral",
|
|
||||||
type: "line",
|
|
||||||
source: "cadastral",
|
|
||||||
layout: ROUND,
|
|
||||||
paint: {
|
|
||||||
"line-color": themeColor("--draw-map-parcel", "#f0abfc"),
|
|
||||||
"line-width": 0.8,
|
|
||||||
"line-opacity": 0.8,
|
|
||||||
},
|
|
||||||
} as GlSpec,
|
|
||||||
...((): GlSpec[] => {
|
|
||||||
const water = themeColor("--draw-map-water", "#0ea5e9");
|
|
||||||
return fillEdge("water", "water", water, 0.45, { color: water, width: 1 });
|
|
||||||
})(),
|
|
||||||
...fillEdge("building", "building", themeColor("--draw-map-building", "#fb7185"), 0.7, {
|
|
||||||
color: HALO,
|
|
||||||
width: 0.6,
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
key: "contour",
|
|
||||||
id: "contour-minor",
|
|
||||||
type: "line",
|
|
||||||
source: "contour",
|
|
||||||
"source-layer": "contour",
|
|
||||||
layout: ROUND,
|
|
||||||
paint: { "line-color": contour, "line-width": 0.8, "line-opacity": 0.7 },
|
|
||||||
} as GlSpec,
|
|
||||||
{
|
|
||||||
key: "contour",
|
|
||||||
id: "contour-index",
|
|
||||||
type: "line",
|
|
||||||
source: "contour",
|
|
||||||
"source-layer": "contour",
|
|
||||||
filter: BOLD,
|
|
||||||
layout: ROUND,
|
|
||||||
paint: { "line-color": contour, "line-width": 1.6, "line-opacity": 0.7 },
|
|
||||||
} as GlSpec,
|
|
||||||
{
|
|
||||||
key: "contour",
|
|
||||||
id: "contour-label",
|
|
||||||
type: "symbol",
|
|
||||||
source: "contour",
|
|
||||||
"source-layer": "contour",
|
|
||||||
filter: BOLD,
|
|
||||||
layout: {
|
|
||||||
"symbol-placement": "line",
|
|
||||||
"symbol-spacing": 320,
|
|
||||||
"text-field": ["to-string", ["get", "h"]],
|
|
||||||
"text-font": [GL_FONT],
|
|
||||||
"text-size": 10,
|
|
||||||
"text-max-angle": 30,
|
|
||||||
"text-padding": 2,
|
|
||||||
},
|
|
||||||
paint: {
|
|
||||||
"text-color": contour,
|
|
||||||
"text-halo-color": "rgba(0, 0, 0, 0.6)",
|
|
||||||
"text-halo-width": 1.5,
|
|
||||||
},
|
|
||||||
} as GlSpec,
|
|
||||||
...haloLine("road", "road", themeColor("--draw-map-road", "#f8fafc"), 1.6, [], {
|
|
||||||
color: HALO,
|
|
||||||
width: 3.2,
|
|
||||||
}),
|
|
||||||
...haloLine(
|
|
||||||
"forestRoad",
|
|
||||||
"road",
|
|
||||||
themeColor("--draw-map-forest-road", "#f59e0b"),
|
|
||||||
2,
|
|
||||||
[7, 4],
|
|
||||||
{ color: HALO, width: 3.6 },
|
|
||||||
[
|
|
||||||
"in",
|
|
||||||
["get", "도로구분"],
|
|
||||||
["literal", ["소로", "미분류"]],
|
|
||||||
] as unknown as FilterSpecification,
|
|
||||||
),
|
|
||||||
...haloLine(
|
|
||||||
"forestRoadFile",
|
|
||||||
"forestRoadFile",
|
|
||||||
themeColor("--draw-map-forest-road-file", "#ff2fd0"),
|
|
||||||
2.4,
|
|
||||||
[],
|
|
||||||
{ color: HALO, width: 4 },
|
|
||||||
),
|
|
||||||
...forestRoadSpecs(), // 국유림 임도 = 벡터 조각
|
|
||||||
// 고른 묶음 · 선 강조(605-2) — 같은 자료에서 걸러 한 겹 더 굵게
|
|
||||||
...["-pick-halo", "-pick"].map(
|
|
||||||
(tail, i) =>
|
|
||||||
({
|
|
||||||
key: "forestRoadFile",
|
|
||||||
id: `forestRoadFile${tail}`,
|
|
||||||
type: "line",
|
|
||||||
source: "forestRoadFile",
|
|
||||||
filter: pickFilter(pick),
|
|
||||||
layout: ROUND,
|
|
||||||
paint: {
|
|
||||||
"line-color": i ? themeColor("--draw-map-sketch", "#ffe94d") : HALO,
|
|
||||||
"line-width": i ? 4.2 : 7.4,
|
|
||||||
},
|
|
||||||
}) as GlSpec,
|
|
||||||
),
|
|
||||||
{
|
|
||||||
key: "stream",
|
|
||||||
id: "stream",
|
|
||||||
type: "line",
|
|
||||||
source: "stream",
|
|
||||||
layout: ROUND,
|
|
||||||
paint: {
|
|
||||||
"line-color": themeColor("--draw-map-stream", "#38bdf8"),
|
|
||||||
"line-width": ["case", ["==", ["get", "구분"], "세류"], 1, 2.4],
|
|
||||||
},
|
|
||||||
} as GlSpec,
|
|
||||||
...lineKind("faults", 2.4, []),
|
|
||||||
...lineKind("lineaments", 1.8, [8, 4]),
|
|
||||||
...lineKind("zeroOrder", 1.8, [4, 3]),
|
|
||||||
// 산사태취약 — 필지 = 「이 필지 안 어딘가」 테두리만 · 점 = 지정 자리
|
|
||||||
...haloLine(
|
|
||||||
"landslideZones",
|
|
||||||
"landslideZones",
|
|
||||||
landslideZone,
|
|
||||||
1.8,
|
|
||||||
[6, 3],
|
|
||||||
{ color: "rgba(255, 255, 255, 0.7)", width: 3.2 },
|
|
||||||
["!=", ["geometry-type"], "Point"] as unknown as FilterSpecification,
|
|
||||||
),
|
|
||||||
{
|
|
||||||
key: "landslideZones",
|
|
||||||
id: "landslideZones-point",
|
|
||||||
type: "circle",
|
|
||||||
source: "landslideZones",
|
|
||||||
filter: ["==", ["geometry-type"], "Point"],
|
|
||||||
paint: {
|
|
||||||
"circle-radius": 5,
|
|
||||||
"circle-color": landslideZone,
|
|
||||||
"circle-stroke-color": "#ffffff",
|
|
||||||
"circle-stroke-width": 1.5,
|
|
||||||
},
|
|
||||||
} as GlSpec,
|
|
||||||
{
|
|
||||||
key: "place",
|
|
||||||
id: "place-dot",
|
|
||||||
type: "circle",
|
|
||||||
source: "place",
|
|
||||||
paint: { "circle-radius": 2.5, "circle-color": place },
|
|
||||||
} as GlSpec,
|
|
||||||
{
|
|
||||||
key: "place",
|
|
||||||
id: "place",
|
|
||||||
type: "symbol",
|
|
||||||
source: "place",
|
|
||||||
layout: {
|
|
||||||
"text-field": ["get", "name"],
|
|
||||||
"text-font": GL_FONT_BOLD,
|
|
||||||
"text-size": 11,
|
|
||||||
"text-anchor": "bottom",
|
|
||||||
"text-offset": [0, -0.36],
|
|
||||||
"text-padding": 2,
|
|
||||||
},
|
|
||||||
paint: {
|
|
||||||
"text-color": place,
|
|
||||||
"text-halo-color": "rgba(0, 0, 0, 0.7)",
|
|
||||||
"text-halo-width": 1.5,
|
|
||||||
},
|
|
||||||
} as GlSpec,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 고른 묶음 · 선만 거름(고름 없음 = 아무것도 안 맞는 거름). */
|
|
||||||
function pickFilter(pick: RoadPick | null): FilterSpecification {
|
|
||||||
const all: unknown[] = ["all", ["==", ["get", "__bundle"], pick?.bundle ?? ""]];
|
|
||||||
if (pick?.line !== undefined) all.push(["==", ["get", "__line"], pick.line]);
|
|
||||||
return all as unknown as FilterSpecification;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 선 겹 색 값(캔버스 `LINE_LAYERS` 와 같음). */
|
|
||||||
const LINE_COLOR = {
|
|
||||||
faults: "--draw-map-fault",
|
|
||||||
lineaments: "--draw-map-lineament",
|
|
||||||
zeroOrder: "--draw-map-zero-order",
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
/** 겹 색 지문 — 밝음 · 어두움이 바뀌면 색을 다시. */
|
|
||||||
const THEME_NAMES = [
|
|
||||||
"--draw-map-contour",
|
|
||||||
"--draw-map-road",
|
|
||||||
"--draw-map-stream",
|
|
||||||
"--draw-map-parcel",
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
/** 위성 채도(−1~1) — 조금 연하게. */
|
|
||||||
const SAT_SATURATION = -0.3;
|
|
||||||
|
|
||||||
/** `track` = 받는 일을 진행 서클에 셈(등고 · 산림 조각 받는 동안). */
|
/** `track` = 받는 일을 진행 서클에 셈(등고 · 산림 조각 받는 동안). */
|
||||||
export function createGlBase(
|
export function createGlBase(
|
||||||
projectId: string,
|
projectId: string,
|
||||||
@@ -369,13 +61,13 @@ export function createGlBase(
|
|||||||
let light = SAT_LIGHT_DEFAULT;
|
let light = SAT_LIGHT_DEFAULT;
|
||||||
let pick: RoadPick | null = null;
|
let pick: RoadPick | null = null;
|
||||||
const base = createGlMap<PlanLayerKey>({
|
const base = createGlMap<PlanLayerKey>({
|
||||||
specs: () => layerSpecs(light, pick),
|
specs: () => mapLayerSpecs(light, pick),
|
||||||
fixed: {
|
fixed: {
|
||||||
satellite: satelliteTileSource(projectId),
|
satellite: satelliteTileSource(projectId),
|
||||||
contour: contourTileSource(projectId),
|
contour: contourTileSource(projectId),
|
||||||
...forestSources(projectId),
|
...forestSources(projectId),
|
||||||
},
|
},
|
||||||
themeNames: THEME_NAMES,
|
themeNames: MAP_THEME_NAMES,
|
||||||
className: "d03-plan__gl",
|
className: "d03-plan__gl",
|
||||||
onFail,
|
onFail,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -58,10 +58,11 @@ def test_d03_parts():
|
|||||||
paint = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_DrawPaint.ts") # 580 — 그림 나눔
|
paint = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_DrawPaint.ts") # 580 — 그림 나눔
|
||||||
assert "map.stations" in paint and "map.slopes" in paint
|
assert "map.stations" in paint and "map.slopes" in paint
|
||||||
# 569-2 · 581-1 · 581-2 — 바탕 겹 단추(공용 지도 도구줄 한 줄 · 묶음 셋) · 처음 켬 셋 · 취향 기억
|
# 569-2 · 581-1 · 581-2 — 바탕 겹 단추(공용 지도 도구줄 한 줄 · 묶음 셋) · 처음 켬 셋 · 취향 기억
|
||||||
defs = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_LayerDefs.ts")
|
defs = _src("A00_Common/map/map_layer_list.ts") # 624-2 공용 겹 목록
|
||||||
assert "createMapTools({" in layers and "createIconButton({" in layers
|
bar = _src("A00_Common/map/map_layer_bar.ts")
|
||||||
assert "createToolbar(" not in layers
|
assert "createMapTools({" in bar and "createIconButton({" in bar and "createMapLayerBar({" in layers
|
||||||
assert '"aislo:pref:d03.layers"' in layers and "writeByKey(PREF_KEY" in layers
|
assert "createToolbar(" not in layers and "createToolbar(" not in bar
|
||||||
|
assert '"aislo:pref:d03.layers"' in defs and "writeByKey(MAP_LAYER_PAGES[page].pref" in bar
|
||||||
keys = re.findall(r'key: "(\w+)",\s+icon: "\w+",\s+title:\s+"[^"]+",\s+on: (true|false)', defs)
|
keys = re.findall(r'key: "(\w+)",\s+icon: "\w+",\s+title:\s+"[^"]+",\s+on: (true|false)', defs)
|
||||||
assert keys == [
|
assert keys == [
|
||||||
("satellite", "true"),
|
("satellite", "true"),
|
||||||
@@ -73,9 +74,12 @@ def test_d03_parts():
|
|||||||
("road", "false"),
|
("road", "false"),
|
||||||
("forestRoad", "false"),
|
("forestRoad", "false"),
|
||||||
("forestRoadFile", "false"),
|
("forestRoadFile", "false"),
|
||||||
|
("nationalRoad", "false"),
|
||||||
("cadastral", "false"),
|
("cadastral", "false"),
|
||||||
("zones", "false"),
|
("zones", "false"),
|
||||||
("soils", "false"),
|
("soils", "false"),
|
||||||
|
("ecoForest", "false"),
|
||||||
|
("ecology", "false"),
|
||||||
("landslide", "false"),
|
("landslide", "false"),
|
||||||
("landslideZones", "false"),
|
("landslideZones", "false"),
|
||||||
("faults", "false"),
|
("faults", "false"),
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ def test_soils_fail_raises(tmp_path, monkeypatch) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_screen_def_and_colors() -> None:
|
def test_screen_def_and_colors() -> None:
|
||||||
defs = (ROOT / "D03_PlanAnalysis" / "D03_PlanAnalysis_UI_LayerDefs.ts").read_text("utf-8")
|
defs = (ROOT / "A00_Common" / "map" / "map_layer_list.ts").read_text("utf-8") # 624-2
|
||||||
entry = re.search(r'key: "soils",\s*icon: "(\w+)",[^}]*on: (\w+)', defs)
|
entry = re.search(r'key: "soils",\s*icon: "(\w+)",[^}]*on: (\w+)', defs)
|
||||||
assert entry and entry.group(2) == "false" # 처음 값 끔
|
assert entry and entry.group(2) == "false" # 처음 값 끔
|
||||||
icons = (ROOT / "ui_template" / "ui_template_icons.ts").read_text("utf-8")
|
icons = (ROOT / "ui_template" / "ui_template_icons.ts").read_text("utf-8")
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ def test_zero_order_no_dem(tmp_path) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_screen_defs_icons_colors() -> None:
|
def test_screen_defs_icons_colors() -> None:
|
||||||
defs = (ROOT / "D03_PlanAnalysis" / "D03_PlanAnalysis_UI_LayerDefs.ts").read_text("utf-8")
|
defs = (ROOT / "A00_Common" / "map" / "map_layer_list.ts").read_text("utf-8") # 624-2
|
||||||
icons = (ROOT / "ui_template" / "ui_template_icons.ts").read_text("utf-8")
|
icons = (ROOT / "ui_template" / "ui_template_icons.ts").read_text("utf-8")
|
||||||
theme = (ROOT / "ui_template" / "ui_template_theme.css").read_text("utf-8")
|
theme = (ROOT / "ui_template" / "ui_template_theme.css").read_text("utf-8")
|
||||||
for key, color in (
|
for key, color in (
|
||||||
|
|||||||
@@ -324,7 +324,7 @@ def test_zone_layer_saved_and_no_cadastral(tmp_path, monkeypatch) -> None:
|
|||||||
|
|
||||||
def test_screen_defs() -> None:
|
def test_screen_defs() -> None:
|
||||||
folder = ROOT / "D03_PlanAnalysis"
|
folder = ROOT / "D03_PlanAnalysis"
|
||||||
defs = (folder / "D03_PlanAnalysis_UI_LayerDefs.ts").read_text("utf-8")
|
defs = (ROOT / "A00_Common" / "map" / "map_layer_list.ts").read_text("utf-8") # 624-2
|
||||||
icons = (ROOT / "ui_template" / "ui_template_icons.ts").read_text("utf-8")
|
icons = (ROOT / "ui_template" / "ui_template_icons.ts").read_text("utf-8")
|
||||||
theme = (ROOT / "ui_template" / "ui_template_theme.css").read_text("utf-8")
|
theme = (ROOT / "ui_template" / "ui_template_theme.css").read_text("utf-8")
|
||||||
paint = (folder / "D03_PlanAnalysis_UI_LayerPaint.ts").read_text(
|
paint = (folder / "D03_PlanAnalysis_UI_LayerPaint.ts").read_text(
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from pathlib import Path
|
|||||||
ROOT = Path(__file__).resolve().parents[2]
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
read = lambda rel: (ROOT / rel).read_text(encoding="utf-8") # noqa: E731
|
read = lambda rel: (ROOT / rel).read_text(encoding="utf-8") # noqa: E731
|
||||||
|
|
||||||
MAPGL = read("D03_PlanAnalysis/D03_PlanAnalysis_UI_MapGl.ts")
|
MAPGL = read("A00_Common/map/map_layer_specs.ts") # 624-2 GL 겹 꼴 = 공용
|
||||||
LIGHT = read("D03_PlanAnalysis/D03_PlanAnalysis_UI_SatLight.ts")
|
LIGHT = read("D03_PlanAnalysis/D03_PlanAnalysis_UI_SatLight.ts")
|
||||||
LAYERS = read("D03_PlanAnalysis/D03_PlanAnalysis_UI_Layers.ts")
|
LAYERS = read("D03_PlanAnalysis/D03_PlanAnalysis_UI_Layers.ts")
|
||||||
|
|
||||||
|
|||||||
@@ -94,4 +94,5 @@ def test_screen_uses_fixed_vector_source() -> None:
|
|||||||
assert "/plan/contour/{z}/{x}/{y}.pbf" in gl
|
assert "/plan/contour/{z}/{x}/{y}.pbf" in gl
|
||||||
base = (ROOT / "D03_PlanAnalysis" / "D03_PlanAnalysis_UI_MapGl.ts").read_text(encoding="utf-8")
|
base = (ROOT / "D03_PlanAnalysis" / "D03_PlanAnalysis_UI_MapGl.ts").read_text(encoding="utf-8")
|
||||||
assert "contour: contourTileSource(projectId)" in base
|
assert "contour: contourTileSource(projectId)" in base
|
||||||
assert base.count('"source-layer": "contour"') == 3
|
specs = (ROOT / "A00_Common" / "map" / "map_layer_specs.ts").read_text(encoding="utf-8")
|
||||||
|
assert specs.count('"source-layer": "contour"') == 3 # 624-2 GL 겹 꼴 = 공용
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
ROOT = Path(__file__).resolve().parents[2]
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
D03 = ROOT / "D03_PlanAnalysis"
|
D03 = ROOT / "D03_PlanAnalysis"
|
||||||
|
MAP = ROOT / "A00_Common" / "map" # 624-2 공용 지도 부품
|
||||||
|
|
||||||
|
|
||||||
def _read(path: Path) -> str:
|
def _read(path: Path) -> str:
|
||||||
@@ -18,7 +19,7 @@ def test_fixed_sources_follow_plan_622_routes() -> None:
|
|||||||
gl = _read(ROOT / "ui_template" / "ui_template_gl_map.ts")
|
gl = _read(ROOT / "ui_template" / "ui_template_gl_map.ts")
|
||||||
assert "export function tileSource" in gl
|
assert "export function tileSource" in gl
|
||||||
assert "/plan/${path}/{z}/{x}/{y}.${ext}" in gl
|
assert "/plan/${path}/{z}/{x}/{y}.${ext}" in gl
|
||||||
forest = _read(D03 / "D03_PlanAnalysis_UI_ForestLayers.ts")
|
forest = _read(MAP / "map_forest_layers.ts")
|
||||||
for name in ("forest_road", "forest_soil", "eco_forest"):
|
for name in ("forest_road", "forest_soil", "eco_forest"):
|
||||||
assert f'vt("{name}")' in forest
|
assert f'vt("{name}")' in forest
|
||||||
assert f'...vector("{name}")' in forest or f'"source-layer": "{name}"' in forest
|
assert f'...vector("{name}")' in forest or f'"source-layer": "{name}"' in forest
|
||||||
@@ -33,7 +34,7 @@ def test_fixed_sources_follow_plan_622_routes() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_layer_list_five_off_by_default() -> None:
|
def test_layer_list_five_off_by_default() -> None:
|
||||||
defs = _read(D03 / "D03_PlanAnalysis_UI_LayerDefs.ts")
|
defs = _read(MAP / "map_layer_list.ts")
|
||||||
icons = _read(ROOT / "ui_template" / "ui_template_icons.ts")
|
icons = _read(ROOT / "ui_template" / "ui_template_icons.ts")
|
||||||
for key in ("nationalRoad", "soils", "ecoForest", "ecology", "landslide"):
|
for key in ("nationalRoad", "soils", "ecoForest", "ecology", "landslide"):
|
||||||
entry = re.search(rf'key: "{key}",\s*icon: "(\w+)",[^}}]*on: (\w+)', defs)
|
entry = re.search(rf'key: "{key}",\s*icon: "(\w+)",[^}}]*on: (\w+)', defs)
|
||||||
@@ -43,7 +44,7 @@ def test_layer_list_five_off_by_default() -> None:
|
|||||||
|
|
||||||
def test_colors_are_draw_map_tokens() -> None:
|
def test_colors_are_draw_map_tokens() -> None:
|
||||||
theme = _read(ROOT / "ui_template" / "ui_template_theme.css")
|
theme = _read(ROOT / "ui_template" / "ui_template_theme.css")
|
||||||
forest = _read(D03 / "D03_PlanAnalysis_UI_ForestLayers.ts")
|
forest = _read(MAP / "map_forest_layers.ts")
|
||||||
for token in set(re.findall(r'"(--draw-map-[\w-]+)"', forest)):
|
for token in set(re.findall(r'"(--draw-map-[\w-]+)"', forest)):
|
||||||
assert f"{token}:" in theme, token
|
assert f"{token}:" in theme, token
|
||||||
assert all(f"--draw-map-soil-{n}:" in theme for n in range(1, 7))
|
assert all(f"--draw-map-soil-{n}:" in theme for n in range(1, 7))
|
||||||
|
|||||||
Reference in New Issue
Block a user