From f93f2674df556d244c85378f36702132140da914 Mon Sep 17 00:00:00 2001 From: ljpark Date: Fri, 26 Jun 2026 17:23:03 +0900 Subject: [PATCH] =?UTF-8?q?sn:=20=EC=A7=80=EB=8F=84=20component=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/fo/public/images/help.png | Bin 3501 -> 3405 bytes apps/sn/public/images/help.png | Bin 3501 -> 3405 bytes .../business_components/src/ui/map/Gis.js | 9 ++- .../src/ui/map/GisSelect.js | 28 +++++-- .../business_components/src/ui/map/GisTree.js | 75 +++++++++++++----- .../src/ui/map/lib/gis.core.js | 21 +++-- .../src/ui/map/lib/gis.layer.js | 5 +- .../src/ui/map/lib/gis.select.js | 4 +- .../src/ui/map/lib/gis.style.js | 3 +- 9 files changed, 110 insertions(+), 35 deletions(-) diff --git a/apps/fo/public/images/help.png b/apps/fo/public/images/help.png index e872802e4e12ed8a2fb477733c6fe10ec5ee5a24..977ef9d38afff3335c9359b5f4db710e5009c502 100644 GIT binary patch delta 3402 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 delta 3499 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 Z@qhOa8*nqIly3k4002ovPDHLkV1iMjys`iQ diff --git a/apps/sn/public/images/help.png b/apps/sn/public/images/help.png index e872802e4e12ed8a2fb477733c6fe10ec5ee5a24..977ef9d38afff3335c9359b5f4db710e5009c502 100644 GIT binary patch delta 3402 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 delta 3499 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 Z@qhOa8*nqIly3k4002ovPDHLkV1iMjys`iQ diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index 4b903d087..2eea996df 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -49,12 +49,14 @@ function Gis({ searchView: SearchView, detailView: DetailView, onFeatureSelect, + canSelectFeature, setIsSearchModalOpen, onCheckedLayersChange, searchBar: SearchBar, waterDepth: WaterDepth = null, layerSettingView: LayerSettingView = null, inlineLayerSettingView, + treeSectionClassName = "", layerIconVisible, isPlaying = false, setIsPlaying = () => { }, @@ -73,6 +75,7 @@ function Gis({ zoomGuide, showFloatingSearchButton = true, treeTogglePosition = "center", + detachedTreeToggleButton = false, showZoomScaleInfo = false, }) { const mapRef = useRef(); @@ -111,7 +114,7 @@ function Gis({ (data) => { onFeatureSelect?.(data); // 선택된 피쳐 데이터 콜백 호출 }, - [mapInstanceRef], + [onFeatureSelect], ); // 지도 생성 및 초기화 @@ -315,7 +318,9 @@ function Gis({ onProgressChange={handleProgressChange} layerSettingView={LayerSettingView} inlineLayerSettingView={inlineLayerSettingView} + treeSectionClassName={treeSectionClassName} toggleButtonPosition={treeTogglePosition} + detachedToggleButton={detachedTreeToggleButton} /> {typeof WaterDepth === "function" && ( @@ -354,7 +359,7 @@ function Gis({ )} - + diff --git a/packages/common/business_components/src/ui/map/GisSelect.js b/packages/common/business_components/src/ui/map/GisSelect.js index 38729b2b2..ac862c2f1 100644 --- a/packages/common/business_components/src/ui/map/GisSelect.js +++ b/packages/common/business_components/src/ui/map/GisSelect.js @@ -1,25 +1,43 @@ -import React, { useContext, useEffect } from "react"; +import React, { useContext, useEffect, useRef } from "react"; import { GisContext } from "./Gis"; import GisApp from "./lib/gis.core"; import GisOlSelect from "./lib/gis.select"; -export default React.memo(function GisSelect({ onFeatureSelect }) { +export default React.memo(function GisSelect({ onFeatureSelect, canSelectFeature }) { const { gisMap } = useContext(GisContext); + const onFeatureSelectRef = useRef(onFeatureSelect); + const canSelectFeatureRef = useRef(canSelectFeature); + + useEffect(() => { + onFeatureSelectRef.current = onFeatureSelect; + }, [onFeatureSelect]); + + useEffect(() => { + canSelectFeatureRef.current = canSelectFeature; + }, [canSelectFeature]); useEffect(() => { if (!gisMap) return; // 아직 초기화 안 됐으면 아무것도 안 함 - // 이미 select가 있으면 재생성하지 않음 const select = new GisOlSelect(gisMap); GisApp.Module.select = select; // 피처 선택 시 콜백 등록 select.getSelectedInfo(function (features) { if (features?.[0]) { - onFeatureSelect(features[0]); + const isSelectable = canSelectFeatureRef.current?.(features[0]); + if (isSelectable === false) { + select.resetSelectedFeature?.(); + return; + } + onFeatureSelectRef.current?.(features[0]); } }); - }, [gisMap, onFeatureSelect]); + + return () => { + select.removeInteration?.(); + }; + }, [gisMap]); // const select = new GisOlSelect(gisMap); // GisApp.Module.select = select; diff --git a/packages/common/business_components/src/ui/map/GisTree.js b/packages/common/business_components/src/ui/map/GisTree.js index 5f9510bb5..cdf67af25 100644 --- a/packages/common/business_components/src/ui/map/GisTree.js +++ b/packages/common/business_components/src/ui/map/GisTree.js @@ -29,6 +29,7 @@ import GisApp from "./lib/gis.core"; * - onProgressChange(total, current): 애니메이션 진행상태 콜백 * - layerSettingView: (선택) 좌측 레이어 트리 하단에 표시할 설정 영역 * - layerIconVisible: (선택) 레이어 행 우측 액션 아이콘 노출 여부를 결정하는 함수 + * - detachedToggleButton: (선택) 접기/펼치기 버튼을 패널 밖 고정 핸들로 노출할지 여부 */ export default React.memo(function GisTree({ checkedNodes = [], @@ -46,7 +47,10 @@ export default React.memo(function GisTree({ layerSettingView: LayerSettingView = null, layerIconVisible, inlineLayerSettingView, + treeSectionClassName = "", toggleButtonPosition = "center", + showLayerPanelTitle = true, + detachedToggleButton = false, }) { const { gisCore, treeData } = useGisContext(); @@ -442,10 +446,11 @@ export default React.memo(function GisTree({ * 레이어 정보로부터 범례 이미지 경로 또는 동적 생성 이미지를 반환합니다. */ const makeLegendSrc = (layerinfo) => { - if (!layerinfo.gobjTy) return null; if (layerinfo.iconNm) { return GisApp.CONTEXT_LAYER_PATH + layerinfo.iconNm; - } else { + } + if (!layerinfo.gobjTy) return null; + else { return makeLegendImage(layerinfo).src; } }; @@ -512,7 +517,10 @@ export default React.memo(function GisTree({ if (!treeData || treeData.length === 0) return null; const toggleButtonPositionClass = - toggleButtonPosition === "top" ? "top-[5.5rem]" : "top-1/2 -translate-y-1/2"; + toggleButtonPosition === "top" ? "top-4" : "top-1/2 -translate-y-1/2"; + const thinScrollbarClass = + "[scrollbar-width:thin] [&::-webkit-scrollbar]:w-[4px]"; + const treeSectionClassNames = [treeSectionClassName, thinScrollbarClass].filter(Boolean).join(" "); return ( <> @@ -524,13 +532,18 @@ export default React.memo(function GisTree({ }`} >
-

시설물

-
+ {showLayerPanelTitle && ( + <> +

시설물

+
+ + )} {/* DnD Tree */} - +
+ child.parent === node.id && child.data.exprsYn === "Y" ); + const nodeLabel = String(node?.data?.lyrNm ?? "").trim(); + const isSinkholeRootTreeNode = + String(node.id) === "SN_SINKHOLE" || + nodeLabel === "지반침하정보"; + const isFloodHistoryRootTreeNode = nodeLabel === "침수흔적도"; + const hideTreeLegendIcon = + isSinkholeRootTreeNode || isFloodHistoryRootTreeNode; const showLayerActionButton = layerIconVisible?.(node); const showLayerActionIcon = depth > 0 && imageVisible; @@ -550,9 +570,10 @@ export default React.memo(function GisTree({ return (
{hasVisibleChildren ? ( @@ -563,7 +584,7 @@ export default React.memo(function GisTree({ title={isOpen ? "닫기" : "열기"} /> ) : ( - + )} handleCheck(node.id)} className="cursor-pointer" /> - {node.icon && {node.icon}} - + {!hideTreeLegendIcon && node.icon && {node.icon}} + {node.text} {showLayerActionButton && (
{LayerSettingView && (
)} + {!detachedToggleButton && ( + + )} +
+ {detachedToggleButton && visible && ( -
+ )} {!visible && ( )} diff --git a/packages/common/business_components/src/ui/map/lib/gis.core.js b/packages/common/business_components/src/ui/map/lib/gis.core.js index b136cdcee..e9e652183 100644 --- a/packages/common/business_components/src/ui/map/lib/gis.core.js +++ b/packages/common/business_components/src/ui/map/lib/gis.core.js @@ -45,7 +45,7 @@ GisApp.GOOGLE_3D_URL = `https://tile.googleapis.com/v1/3dtiles/root.json?key=${G // NGII 정보(배경지도) //GisApp.NGII_WMTS_URL = process.env.NEXT_PUBLIC_NGII_URL; -GisApp.NGII_WMTS_URL = `/ngii-server/openapi/Gettile.do?apikey=${process.env.NEXT_PUBLIC_NGII_PARAM}`; +GisApp.NGII_WMTS_PARAM = process.env.NEXT_PUBLIC_NGII_PARAM ?? ''; GisApp.EMAP_URL = '/emap/'; GisApp.EMAP_SATELLITE_URL = '/emap/satellite/'; @@ -79,6 +79,17 @@ GisApp.setLayerInfos = function (layerInfos) { GisApp.layerInfos = layerInfos; }; +GisApp.getNgiiWmtsUrl = function (layerName, matrix, x, y) { + const layerAwareParams = /(^|&)layer=[^&]*/i.test(GisApp.NGII_WMTS_PARAM) + ? GisApp.NGII_WMTS_PARAM.replace(/(^|&)layer=[^&]*/i, `$1layer=${layerName}`) + : `${GisApp.NGII_WMTS_PARAM}&layer=${layerName}`; + + return `/ngii-server/openapi/Gettile.do?apikey=${layerAwareParams}` + .replace("{z}", matrix) + .replace("{x}", x) + .replace("{y}", y); +}; + /** * GisApp core * @requires gis.layer.js @@ -161,7 +172,7 @@ GisApp.core.prototype = { }); const wmtsSource = new TileWMTS({ - //url: GisApp.NGII_WMTS_URL, + // url은 setTileUrlFunction에서 layer별로 동적으로 만든다. projection: projection, tileGrid: tileGrid, crossOrigin: "anonymous", @@ -175,11 +186,7 @@ GisApp.core.prototype = { wmtsSource.setTileUrlFunction((tileCoord) => { const [z, x, y] = tileCoord; const matrix = matrixIds[z]; - // console.log(GisApp.NGII_WMTS_URL.replace("{z}", matrix).replace("{x}", x).replace("{y}", y)); - return GisApp.NGII_WMTS_URL - .replace("{z}", matrix) - .replace("{x}", x) - .replace("{y}", y); + return GisApp.getNgiiWmtsUrl(_layer, matrix, x, y); }); return new TileLayer({ diff --git a/packages/common/business_components/src/ui/map/lib/gis.layer.js b/packages/common/business_components/src/ui/map/lib/gis.layer.js index 314b93338..97d3eb6db 100644 --- a/packages/common/business_components/src/ui/map/lib/gis.layer.js +++ b/packages/common/business_components/src/ui/map/lib/gis.layer.js @@ -742,10 +742,13 @@ GisOlLayer.prototype = { const extent = this.combineExtents(geometries.map(g => g?.getExtent())); let map = GisApp.Module.core.map; + const targetZoom = 16; + const currentZoom = Number(map.getView().getZoom()); + const maxZoom = Number.isFinite(currentZoom) && currentZoom > targetZoom ? currentZoom : targetZoom; map.getView().fit(extent, { duration: 300, padding: [50, 50, 50, 50], - maxZoom: 16 + maxZoom: maxZoom }); } } else { diff --git a/packages/common/business_components/src/ui/map/lib/gis.select.js b/packages/common/business_components/src/ui/map/lib/gis.select.js index a1dab024e..e05edb9af 100644 --- a/packages/common/business_components/src/ui/map/lib/gis.select.js +++ b/packages/common/business_components/src/ui/map/lib/gis.select.js @@ -132,13 +132,15 @@ GisOlSelect.prototype = { const selectedLayer = e.target?.getLayer(feature) || feature.getLayer?.(); const clckZoom = Number(selectedLayer?.get('clckZoom')); const targetZoom = Number.isFinite(clckZoom) && clckZoom > 0 ? clckZoom : 16; + const currentZoom = Number(map.getView().getZoom()); + const maxZoom = Number.isFinite(currentZoom) && currentZoom > targetZoom ? currentZoom : targetZoom; const geometry = feature.getGeometry(); if (geometry) { const extent = geometry.getExtent(); map.getView().fit(extent, { duration: 300, padding: [50, 50, 50, 50], - maxZoom: targetZoom + maxZoom: maxZoom }); } } diff --git a/packages/common/business_components/src/ui/map/lib/gis.style.js b/packages/common/business_components/src/ui/map/lib/gis.style.js index 4ad75c386..767244e73 100644 --- a/packages/common/business_components/src/ui/map/lib/gis.style.js +++ b/packages/common/business_components/src/ui/map/lib/gis.style.js @@ -343,12 +343,13 @@ GisOlStyle.prototype = { //레이어 스타일 반환 getLayerStyle: function (layer, options) { let gobjTy = layer.get('gobjTy'); - if (!gobjTy) return; + if (!gobjTy || typeof this[gobjTy] !== 'function') return; let style = this[gobjTy](layer, options); return style; }, getLayerStyleFunction: function (layer, options) { let gobjTy = layer.get('gobjTy'); + if (!gobjTy || typeof this[gobjTy] !== 'function') return; let styleFunction = function (feature, resolution) { return this[gobjTy](layer, options, feature, resolution); }.bind(this);