feat(B04/B05): 지도 레이어 기본 표시값 정의 + 명칭 정리

2026-08-01 사용자 지정 기본값을 한곳(BACKGROUND_DEFAULT_ON / GIS_DEFAULT_ON /
CONTOUR_LABEL_DEFAULT_ON)에 모아 정의한다.

- 켜짐: 백지도, 위성, 시군구, 읍면동, 세류(전체)
- 꺼짐: 하이브리드, 연속지적도, 등고선, 도엽등고선, 표고점, 성절토, 옹벽석축, 등고라벨
- 유수방향 레이어는 화면에서 제외(목록에서 삭제)
- 명칭: 세류(하천중심선) -> 세류(전체), 배수유역 재산정 -> 유역 분석
- 유역 갈래 기본값(1차 꺼짐 / 2차 켜짐 / 유역방향 꺼짐 / 평균흐름 켜짐)은 기존과 동일
- B05 배수유역도 초기 보기는 도로 기준 줌인으로 되돌림(B04 하단 지도는 배경 전체 보기 유지)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-01 13:01:02 +09:00
co-authored by Claude Opus 5
parent 5da0fb1cd6
commit 306268b616
4 changed files with 67 additions and 18 deletions
+34 -10
View File
@@ -30,6 +30,7 @@ export interface SurfaceMapViewer {
}
const BACKGROUND_LAYERS = ["white", "satellite", "hybrid"] as const;
// 유수방향은 화면에 쓰지 않기로 해 목록에서 뺐다(2026-08-01 사용자 지시).
const GIS_LAYERS = [
"지적도",
"행정구역_시군구",
@@ -40,11 +41,34 @@ const GIS_LAYERS = [
"도엽_표고점",
"도엽_성절토",
"도엽_옹벽석축",
"도엽_유수방향",
] as const;
type BackgroundLayer = (typeof BACKGROUND_LAYERS)[number];
type GisLayer = (typeof GIS_LAYERS)[number];
/* ── 처음 열었을 때 켜져 있을 레이어 (2026-08-01 사용자 지정) ──────────────
* 지도가 복잡해지지 않도록 실제로 자주 보는 것만 켠 채로 시작한다.
* 나머지는 버튼으로 그때그때 켠다. */
const BACKGROUND_DEFAULT_ON: Record<BackgroundLayer, boolean> = {
white: true,
satellite: true,
hybrid: false,
};
const GIS_DEFAULT_ON: Record<GisLayer, boolean> = {
지적도: false,
행정구역_시군구: true,
행정구역_읍면동: true,
등고선: false,
도엽_등고선: false,
도엽_하천중심선: true,
도엽_표고점: false,
도엽_성절토: false,
도엽_옹벽석축: false,
};
/** 등고 라벨(계곡선 수치) 기본 표시 여부. */
const CONTOUR_LABEL_DEFAULT_ON = false;
const GIS_LAYER_COLORS: Record<GisLayer, string> = {
: "#f97316",
_시군구: "#7c3aed",
@@ -55,7 +79,6 @@ const GIS_LAYER_COLORS: Record<GisLayer, string> = {
_표고점: "#f9a8d4",
_성절토: "#f43f5e",
_옹벽석축: "#0f766e",
_유수방향: "#0891b2",
};
// 등고 라벨 표기 대상 레이어와 표고 속성 키 (gpkg=CTRLN_HG, 도엽=등고수치)
@@ -152,10 +175,11 @@ export function createSurfaceMapViewer(): SurfaceMapViewer {
let normalizer: Normalizer | null = null;
// 사전 투영된 렌더용 레이어. 원본 GeoJSON은 변형하지 않으며 투영 후에는 참조를 잡아두지 않는다.
const preparedLayers = new Map<GisLayer, PreparedLayer>();
const activeBackgrounds = new Set<BackgroundLayer>(BACKGROUND_LAYERS);
// gpkg 등고선은 기본 꺼짐(도엽 등고선이 기본 표기), 등고 라벨은 기본 켜짐 (2026-07-26 사용자 지시)
const activeGisLayers = new Set<GisLayer>(GIS_LAYERS.filter((layer) => layer !== "등고선"));
let showContourLabels = true;
const activeBackgrounds = new Set<BackgroundLayer>(
BACKGROUND_LAYERS.filter((layer) => BACKGROUND_DEFAULT_ON[layer]),
);
const activeGisLayers = new Set<GisLayer>(GIS_LAYERS.filter((layer) => GIS_DEFAULT_ON[layer]));
let showContourLabels = CONTOUR_LABEL_DEFAULT_ON;
let scale = 1;
let offsetX = 0;
let offsetY = 0;
@@ -215,7 +239,6 @@ export function createSurfaceMapViewer(): SurfaceMapViewer {
도엽_표고점: L("B04_Surface_Map_SheetElevPoint"),
도엽_성절토: L("B04_Surface_Map_SheetCutFill"),
도엽_옹벽석축: L("B04_Surface_Map_SheetWall"),
도엽_유수방향: L("B04_Surface_Map_SheetFlowDir"),
};
GIS_LAYERS.forEach((layer) => {
gisButtons.append(
@@ -223,12 +246,13 @@ export function createSurfaceMapViewer(): SurfaceMapViewer {
);
});
// 등고 라벨 보기/숨기기 (기본 켜짐 — 등고선·도엽 등고선의 계곡선 수치 표기)
// 등고 라벨 보기/숨기기 (등고선·도엽 등고선의 계곡선 수치 표기)
const contourLabelButton = document.createElement("button");
contourLabelButton.type = "button";
contourLabelButton.className = "b04-map__layer-button is-active";
contourLabelButton.className =
"b04-map__layer-button" + (CONTOUR_LABEL_DEFAULT_ON ? " is-active" : "");
contourLabelButton.textContent = L("B04_Surface_Map_ContourLabel");
contourLabelButton.setAttribute("aria-pressed", "true");
contourLabelButton.setAttribute("aria-pressed", String(CONTOUR_LABEL_DEFAULT_ON));
contourLabelButton.addEventListener("click", () => {
showContourLabels = !showContourLabels;
contourLabelButton.classList.toggle("is-active", showContourLabels);
@@ -82,7 +82,7 @@ export function createWatershedOverlay(onChange: () => void): WatershedOverlay {
const button = document.createElement("button");
button.type = "button";
button.className = "b04-map__layer-button b04-map__layer-button--gis";
button.textContent = "배수유역 재산정";
button.textContent = "유역 분석";
button.style.setProperty("--b04-layer-color", "#dc2626");
button.setAttribute("aria-pressed", "false");
button.title =
@@ -473,13 +473,13 @@ export function createWatershedOverlay(onChange: () => void): WatershedOverlay {
// 저장분이 아직 없는 것은 오류가 아니다 — 무엇을 눌러야 하는지 알려 준다.
say(
refresh
? `배수유역 재산정 실패: ${message}`
: "저장된 배수유역 분석이 없습니다. [배수유역 재산정]을 누르세요.",
? `유역 분석 실패: ${message}`
: "저장된 배수유역 분석이 없습니다. [유역 분석]을 누르세요.",
);
} finally {
busy = false;
button.disabled = false;
button.textContent = "배수유역 재산정";
button.textContent = "유역 분석";
onChange();
}
}
@@ -475,14 +475,39 @@ export function createDrainagePanel(): DrainagePanel {
boundaryEditor.setEditMode(boundaryMode);
});
/** 확보한 배경 지도(브이월드) 전체가 보이도록 맞춘다.
/** 노선 전체가 보이도록 배율·중심을 맞춘다(초기 보기 = 도로 기준 줌인).
*
* 노선 범위에 맞춰 확대하면 배경을 넓게 받아도 화면에 보이는 범위가 늘 같았
* (2026-08-01 사용자 지). 노선 주변 지형까지 함께 보고 판단하도록 배경 전체를 띄운다. */
* B05는 노선 주변 배수유역을 보는 화면이라 도로에 맞춰 확대한 상태로 연
* (2026-08-01 사용자 지). 배경 전체를 보려면 휠로 축소하면 된다.
* 노선이 없으면 배경 전체를 그대로 보여준다. */
function fitToRoute(): void {
scale = 1;
offsetX = 0;
offsetY = 0;
if (!meta || routePoints.length < 2) return;
const rect = viewport.getBoundingClientRect();
const width = Math.max(rect.width, 1);
const height = Math.max(rect.height, 1);
const mapRect = computeMapRect(meta, width, height);
let minX = Infinity;
let minY = Infinity;
let maxX = -Infinity;
let maxY = -Infinity;
routePoints.forEach((point) => {
if (point.x < minX) minX = point.x;
if (point.x > maxX) maxX = point.x;
if (point.y < minY) minY = point.y;
if (point.y > maxY) maxY = point.y;
});
const routeWidth = Math.max(maxX - minX, 1);
const routeHeight = Math.max(maxY - minY, 1);
scale = Math.min(meta.width_meters / routeWidth, meta.height_meters / routeHeight) * 0.85;
const centerX = (minX + maxX) / 2;
const centerY = (minY + maxY) / 2;
const baseX = mapRect.x + ((centerX - meta.x_min) / meta.width_meters) * mapRect.width;
const baseY = mapRect.y + (1 - (centerY - meta.y_min) / meta.height_meters) * mapRect.height;
offsetX = -(baseX - width / 2) * scale;
offsetY = -(baseY - height / 2) * scale;
}
async function loadLayers(): Promise<void> {
+1 -1
View File
@@ -662,7 +662,7 @@ export const ui_locales = {
B04_Surface_Map_Contour: ["등고선", "Contour lines"],
B04_Surface_Map_SheetContour: ["도엽 등고선", "Sheet contours"],
B04_Surface_Map_ContourLabel: ["등고 라벨", "Contour labels"],
B04_Surface_Map_SheetStream: ["세류(하천중심선)", "Stream centerline"],
B04_Surface_Map_SheetStream: ["세류(전체)", "Streams (all)"],
B04_Surface_Map_SheetElevPoint: ["표고점", "Spot elevation"],
B04_Surface_Map_SheetCutFill: ["성절토", "Cut/fill slope"],
B04_Surface_Map_SheetWall: ["옹벽석축", "Retaining wall"],