feat(D03 겹): 노선분석 지도 산사태위험지도 겹 — 행안부 생활안전지도 WMS · 키 없으면 그 겹만 꺼짐 (PLAN 584-1 · 요구 444)
- 서버: IF_0046_WMS 겹 A2SM_SANSATAI 를 받은 범위 경위도 상자로 한 장(2 m/px · 한 변 2,048) → 작업 좌표계 상자로 옮겨 그림 · 덮으면 저장본 · 키 없음 = 503 「키 없음」 · 받기 실패 · 그림 아님 = 502 - 키 = .env SAFEMAP_API_KEY 새 칸(값 비움) - 화면: 단추 하나(alert) · 묶음 셋째 · 처음 끔 · 바탕 캔버스 · 범례 1~5 등급(--draw-map-landslide-1~5) - 켬끔 취향 읽기를 LayerDefs 로 옮김(700줄 한도) - test_553_4: 겹 목록에 landslide 더함(새 겹 · 기대 목록 갱신) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,11 +5,11 @@
|
||||
*
|
||||
* 겹 = 바탕(위성 · 음영기복 · 도엽 등고선(계곡선 25 m 굵게 · 수치) · 급경사 색) ·
|
||||
* 물 · 길(세류선 · 하천 · 물 면(실폭하천 · 저수지) · 기존 도로 · 기존 임도 짐작(소로 · 미분류)) ·
|
||||
* 구역(지적도 · 보호 · 규제 구역 여덟 종 · 산림입지도(토양형) · 선 겹(단층 · 지질구조선 · 0차곡) ·
|
||||
* 구역(지적도 · 보호 · 규제 구역 여덟 종 · 산림입지도(토양형) · 산사태위험지도 · 선 겹(단층 · 지질구조선 · 0차곡) ·
|
||||
* 계류 100 m · 도로 300 m 띠 · 건물 · 지명).
|
||||
* 처음 켬 = 위성 · 등고선 · 세류 · 고른 값 = 화면 취향(`aislo:pref:d03.layers`) 기억.
|
||||
* 받기 = 켤 때만(한 번 받으면 다시 안 받음) · 지형을 안 받았으면 단추를 막음 · 받기 실패 = 그 겹만 꺼짐 + 알림.
|
||||
* 그리기 차례(아래 → 위) = 위성(`scene.image`) → 음영기복 → 급경사 → 산림입지도 → 보호구역 → 띠 → 지적 → 물 면 →
|
||||
* 그리기 차례(아래 → 위) = 위성(`scene.image`) → 음영기복 → 급경사 → 산사태위험지도 → 산림입지도 → 보호구역 → 띠 → 지적 → 물 면 →
|
||||
* 건물 → 등고선 → 도로 → 임도 짐작 → 세류 → 선 겹 → 지명 → 그은 선.
|
||||
* 겹 모두 = 바탕 캔버스(`base` · 582) · 등고선 = 줌 단계 색인 · 글 폭 기억.
|
||||
* 범례 = 켠 겹의 받은 범위 안 수 · 넓이(왼쪽 아래).
|
||||
@@ -21,7 +21,7 @@ import { createIconButton } from "@ui/ui_template_button";
|
||||
import { createMapTools } from "@ui/ui_template_map_tools";
|
||||
import { themeColor } from "@ui/ui_template_palette";
|
||||
import { fetchCachedSheetLayer } from "../A00_Common/b_asset_cache";
|
||||
import { readByKey, writeByKey } from "../A00_Common/b_page_state";
|
||||
import { writeByKey } from "../A00_Common/b_page_state";
|
||||
import {
|
||||
fetchVWorldMeta,
|
||||
getVWorldMapUrl,
|
||||
@@ -56,6 +56,7 @@ import {
|
||||
import {
|
||||
FOREST_ROAD_KINDS,
|
||||
GROUPS,
|
||||
LANDSLIDE_LEGEND,
|
||||
LAYERS,
|
||||
LINE_KEYS,
|
||||
LINE_LAYERS,
|
||||
@@ -66,6 +67,7 @@ import {
|
||||
fillLayer,
|
||||
hectare,
|
||||
loadImage,
|
||||
readPref,
|
||||
stroke,
|
||||
type LineLayerKey,
|
||||
type PlanLayerKey,
|
||||
@@ -91,6 +93,7 @@ type Prepared = Record<LineLayerKey, PreparedLines> & {
|
||||
road: { all: PreparedLayer; small: PreparedLayer };
|
||||
slope: { raster: PlanRaster; image: HTMLImageElement };
|
||||
hillshade: { raster: PlanRaster; image: HTMLImageElement };
|
||||
landslide: { raster: PlanRaster; image: HTMLImageElement };
|
||||
water: PreparedLayer;
|
||||
zones: { kinds: Map<string, PreparedLayer>; summary: PlanZones["summary"] };
|
||||
/** 토양형(코드 차례) → 지물 · 색 값. */
|
||||
@@ -120,21 +123,6 @@ export interface PlanLayers {
|
||||
};
|
||||
}
|
||||
|
||||
function readPref(): Record<PlanLayerKey, boolean> {
|
||||
const on = Object.fromEntries(LAYERS.map((def) => [def.key, def.on])) as Record<
|
||||
PlanLayerKey,
|
||||
boolean
|
||||
>;
|
||||
try {
|
||||
const saved = JSON.parse(readByKey(PREF_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;
|
||||
}
|
||||
|
||||
export function createPlanLayers(opts: {
|
||||
projectId: string;
|
||||
map: PlanMap;
|
||||
@@ -142,7 +130,7 @@ export function createPlanLayers(opts: {
|
||||
points: () => Vertex[];
|
||||
}): PlanLayers {
|
||||
const { projectId, map } = opts;
|
||||
const on = readPref();
|
||||
const on = readPref(PREF_KEY);
|
||||
const data: Partial<Prepared> = {};
|
||||
const pending = new Map<DataKey | "satellite", Promise<void>>();
|
||||
let meta: VWorldMeta | null = null;
|
||||
@@ -186,6 +174,7 @@ export function createPlanLayers(opts: {
|
||||
color: band.color,
|
||||
})),
|
||||
);
|
||||
if (on.landslide && data.landslide) rows.push(LANDSLIDE_LEGEND);
|
||||
if (on.zones && data.zones) {
|
||||
const items = Object.entries(data.zones.summary)
|
||||
.filter(([, kind]) => kind.count || kind.failed)
|
||||
@@ -366,7 +355,7 @@ export function createPlanLayers(opts: {
|
||||
pick(collection, (props) => FOREST_ROAD_KINDS.has(String(props["도로구분"]))),
|
||||
),
|
||||
};
|
||||
} else if (key === "slope" || key === "hillshade") {
|
||||
} else if (key === "slope" || key === "hillshade" || key === "landslide") {
|
||||
const raster = await fetchPlanRaster(projectId, key);
|
||||
data[key] = { raster, image: await loadImage(raster.image) };
|
||||
} else if (key === "water") {
|
||||
@@ -461,6 +450,14 @@ export function createPlanLayers(opts: {
|
||||
drawRaster(context, map, data.slope);
|
||||
},
|
||||
},
|
||||
{
|
||||
base: true,
|
||||
draw: (context, map) => {
|
||||
if (!on.landslide || !data.landslide) return;
|
||||
context.globalAlpha = 0.6;
|
||||
drawRaster(context, map, data.landslide);
|
||||
},
|
||||
},
|
||||
{
|
||||
base: true,
|
||||
draw: (context, map) => {
|
||||
@@ -659,6 +656,7 @@ export function createPlanLayers(opts: {
|
||||
band_road_m2: data.bands.info.road.area_m2,
|
||||
}),
|
||||
...(data.hillshade && { hillshade_px: data.hillshade.image.naturalWidth }),
|
||||
...(data.landslide && { landslide_px: data.landslide.image.naturalWidth }),
|
||||
...Object.fromEntries(
|
||||
LINE_KEYS.flatMap((key) => {
|
||||
const got = data[key];
|
||||
|
||||
Reference in New Issue
Block a user