From 306268b61602f15172d1ae551d7fd48a7a3da9b6 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sat, 1 Aug 2026 13:01:02 +0900 Subject: [PATCH] =?UTF-8?q?feat(B04/B05):=20=EC=A7=80=EB=8F=84=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=20=EA=B8=B0=EB=B3=B8=20=ED=91=9C=EC=8B=9C?= =?UTF-8?q?=EA=B0=92=20=EC=A0=95=EC=9D=98=20+=20=EB=AA=85=EC=B9=AD=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../B04_wf1_Surface_UI_MapViewer.ts | 44 ++++++++++++++----- .../B04_wf1_Surface_UI_Watershed.ts | 8 ++-- .../B05_wf2_Route_UI_Drainage_Panel.ts | 31 +++++++++++-- ui_template/ui_template_locale.ts | 2 +- 4 files changed, 67 insertions(+), 18 deletions(-) diff --git a/B04_wf1_Surface/B04_wf1_Surface_UI_MapViewer.ts b/B04_wf1_Surface/B04_wf1_Surface_UI_MapViewer.ts index cb05e2ce..8c271641 100644 --- a/B04_wf1_Surface/B04_wf1_Surface_UI_MapViewer.ts +++ b/B04_wf1_Surface/B04_wf1_Surface_UI_MapViewer.ts @@ -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 = { + white: true, + satellite: true, + hybrid: false, +}; + +const GIS_DEFAULT_ON: Record = { + 지적도: false, + 행정구역_시군구: true, + 행정구역_읍면동: true, + 등고선: false, + 도엽_등고선: false, + 도엽_하천중심선: true, + 도엽_표고점: false, + 도엽_성절토: false, + 도엽_옹벽석축: false, +}; + +/** 등고 라벨(계곡선 수치) 기본 표시 여부. */ +const CONTOUR_LABEL_DEFAULT_ON = false; + const GIS_LAYER_COLORS: Record = { 지적도: "#f97316", 행정구역_시군구: "#7c3aed", @@ -55,7 +79,6 @@ const GIS_LAYER_COLORS: Record = { 도엽_표고점: "#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(); - const activeBackgrounds = new Set(BACKGROUND_LAYERS); - // gpkg 등고선은 기본 꺼짐(도엽 등고선이 기본 표기), 등고 라벨은 기본 켜짐 (2026-07-26 사용자 지시) - const activeGisLayers = new Set(GIS_LAYERS.filter((layer) => layer !== "등고선")); - let showContourLabels = true; + const activeBackgrounds = new Set( + BACKGROUND_LAYERS.filter((layer) => BACKGROUND_DEFAULT_ON[layer]), + ); + const activeGisLayers = new Set(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); diff --git a/B04_wf1_Surface/B04_wf1_Surface_UI_Watershed.ts b/B04_wf1_Surface/B04_wf1_Surface_UI_Watershed.ts index 888bf201..57dee145 100644 --- a/B04_wf1_Surface/B04_wf1_Surface_UI_Watershed.ts +++ b/B04_wf1_Surface/B04_wf1_Surface_UI_Watershed.ts @@ -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(); } } diff --git a/B05_wf2_Route/B05_wf2_Route_UI_Drainage_Panel.ts b/B05_wf2_Route/B05_wf2_Route_UI_Drainage_Panel.ts index 80a75dde..c6537f9c 100644 --- a/B05_wf2_Route/B05_wf2_Route_UI_Drainage_Panel.ts +++ b/B05_wf2_Route/B05_wf2_Route_UI_Drainage_Panel.ts @@ -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 { diff --git a/ui_template/ui_template_locale.ts b/ui_template/ui_template_locale.ts index 76f96a69..253e8b66 100644 --- a/ui_template/ui_template_locale.ts +++ b/ui_template/ui_template_locale.ts @@ -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"],