diff --git a/apps/fo/public/images/help.png b/apps/fo/public/images/help.png index e872802e4..977ef9d38 100644 Binary files a/apps/fo/public/images/help.png and b/apps/fo/public/images/help.png differ diff --git a/apps/sn/public/images/help.png b/apps/sn/public/images/help.png index e872802e4..977ef9d38 100644 Binary files a/apps/sn/public/images/help.png and b/apps/sn/public/images/help.png differ 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);