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 (
{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);