diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js
index 6e13d70c5..ef5c21d5b 100644
--- a/packages/common/business_components/src/ui/map/Gis.js
+++ b/packages/common/business_components/src/ui/map/Gis.js
@@ -1,5 +1,6 @@
import { Search } from 'lucide-react';
import OlMap from 'ol/Map';
+import Overlay from 'ol/Overlay';
import View from 'ol/View';
import { defaults as defaultControls, OverviewMap } from 'ol/control';
import { defaults } from 'ol/interaction';
@@ -14,27 +15,53 @@ import GisToolBar from './GisToolBar';
import GisTree from './GisTree';
import './lib/css/gis.common.css';
import GisApp from './lib/gis.core';
+// import FacilityInfoPopup from './FacilityInfoPopup';
export const GisContext = createContext({ gisCore: null, gisMap: null, treeData: null, checkedLayerIds: null });
export const useGisContext = () => useContext(GisContext);
-function Gis({ isOpen, gisMaps, isSearchModalOpen, treeData = [], isSearch = false, isMiniMap = false, setIsOpen, setFeatureData, featureData, searchView: SearchView, detailView: DetailView, onFeatureSelect, setIsSearchModalOpen }) {
+function Gis({
+ isOpen,
+ gisMaps,
+ isSearchModalOpen,
+ treeData = [],
+ isSearch = false,
+ isMiniMap = false,
+ setIsOpen,
+ setFeatureData,
+ featureData,
+ searchView: SearchView,
+ detailView: DetailView,
+ onFeatureSelect,
+ setIsSearchModalOpen,
+ onCheckedLayersChange
+}) {
const mapRef = useRef();
const [mapObject, setMapObject] = useState({});
const [tabVisible, setTabVisible] = useState(true);
+ const [layerNm, setLayerNm] = useState('')
// const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태
// const [featureData, setFeatureData] = useState(null) // 선택된 피쳐 데이터
// const [isSearchModalOpen, setIsSearchModalOpen] = useState(false); // SearchModal 열림 상태
const gisCore = useMemo(() => new GisApp.core({ targetId: mapRef.current }), []);
const mapInstanceRef = useRef(null);
const checkedLayerIds = useRef([]);
+
+
+ // 팝업 데이터를 저장할 상태와 오버레이 참조를 저장할 ref 추가
+ // const [facilityInfoPopupData, setFacilityInfoPopupData] = useState([]);
+ // const facilityOverlaysRef = useRef([]);
+ // 체크된 레이어 상태 관리
+ const [checkedLayers, setCheckedLayers] = useState([]);
+ const [checkedLayersData, setCheckedLayersData] = useState([]);
+
let gisMap = null;
const closePopup = () => {
setIsOpen(false); // 팝업 닫기
setFeatureData(null)
};
- // SearchModal, GisContent 등에서 호출
+ // Feature Click Event
const handleFeatureSelect = useCallback((data) => {
gisMap = mapInstanceRef.current;
@@ -89,8 +116,10 @@ function Gis({ isOpen, gisMaps, isSearchModalOpen, treeData = [], isSearch = fal
setFeatureData(data);
setIsOpen(true);
setIsSearchModalOpen(false);
+ onFeatureSelect?.(data); // 선택된 피쳐 데이터 콜백 호출
}, [mapInstanceRef]);
+ // 지도 생성 및 초기화
useEffect(() => {
if (!mapRef.current) return;
if (mapInstanceRef.current) return; // 이미 생성된 맵이 있다면 중단
@@ -104,7 +133,7 @@ function Gis({ isOpen, gisMaps, isSearchModalOpen, treeData = [], isSearch = fal
]);
register(proj4);
- //맵 생성
+ // OpenLayers 지도 객체 생성
const map = new OlMap({
layers: [gisCore.baseLayers['base'], gisCore.baseLayers['satellite']],
renderer: 'webgl',
@@ -132,7 +161,7 @@ function Gis({ isOpen, gisMaps, isSearchModalOpen, treeData = [], isSearch = fal
gisMaps.current = map
- //미니맵
+ // 미니맵 추가
if (isMiniMap) {
const overviewMap = new OverviewMap({
layers: [GisApp.Module.core.baseMiniMapLayers['base']],
@@ -147,6 +176,7 @@ function Gis({ isOpen, gisMaps, isSearchModalOpen, treeData = [], isSearch = fal
map.addControl(overviewMap);
}
+ // 스케일 컨트롤 및 베이스 레이어 설정
gisCore.__proto__.map = map;
gisCore.scaleControl();
gisCore.changeBaseLayer('base');
@@ -154,16 +184,20 @@ function Gis({ isOpen, gisMaps, isSearchModalOpen, treeData = [], isSearch = fal
mapInstanceRef.current = map; // 생성 완료 플래그 설정
}, []);
+ // treeData 변경 시 레이어 코드 생성
useEffect(() => {
if (!treeData || treeData.length === 0) return;
+ // 기존 오버레이 및 레이어 모두 삭제
gisCore.deleteAllOverlay();
gisCore.deleteAllLayer();
+ // 트리 데이터를 레이어 코드 객체로 변환
const layerCode = gisCore.convertLayerInfosToLayerCode(treeData, 'lyrSn');
- GisApp.layerCode = layerCode;
+ GisApp.layerCode = layerCode; // 전역 레이어 코드 설정
//setTreeData(data);
+ // 편집 가능한 레이어 목록 생성
const layerItems = getLayerItems(layerCode);
//setEditLayerItems(prev => [...layerItems])
}, [treeData]);
@@ -193,33 +227,168 @@ function Gis({ isOpen, gisMaps, isSearchModalOpen, treeData = [], isSearch = fal
}
}
+ // // 데이터 로드 및 팝업 생성 로직
+ // useEffect(() => {
+ // if (!mapInstanceRef.current) return; // 맵이 생성되지 않았으면 실행 안 함
+
+ // // 1. 팝업 데이터 가져오기 (feature.js의 getData와 유사)
+ // const fetchPopupData = async () => {
+ // // 실제 API 호출 로직으로 대체해야 합니다.
+ // // 예시: const response = await yourApiService.getFcltyWtCnt();
+ // // setFacilityInfoPopupData(response.data);
+
+ // // 임시 목업 데이터
+ // const mockData = [
+ // { ftrIdn: 1, ftrCde: 'SA206', bplcNm: '처리장A', fcltyUseYn: 'Y', x: 127.005, y: 37.282, aCnt: 10, cCnt: 5, eCnt: 3, mCnt: 8, iCnt: 2 },
+ // { ftrIdn: 2, ftrCde: 'SA112', bplcNm: '처리장B', fcltyUseYn: 'N', x: 127.010, y: 37.285, aCnt: 8, cCnt: 4, eCnt: 2, mCnt: 6, iCnt: 1 }
+ // ];
+ // setFacilityInfoPopupData(mockData);
+ // };
+
+ // fetchPopupData();
+
+ // }, [mapInstanceRef.current]); // 맵 인스턴스가 생성된 후 한 번만 실행
+
+ // // 데이터가 변경되면 오버레이를 생성/업데이트하는 로직
+ // useEffect(() => {
+ // const map = mapInstanceRef.current;
+ // if (!map || facilityInfoPopupData.length === 0) return;
+
+ // // 기존 오버레이 정리
+ // facilityOverlaysRef.current.forEach(overlay => map.removeOverlay(overlay));
+ // facilityOverlaysRef.current = [];
+
+ // facilityInfoPopupData.forEach(data => {
+ // const popupId = `${data.ftrIdn}_${data.ftrCde}`;
+
+ // // 팝업을 담을 div 엘리먼트 생성
+ // const popupElement = document.createElement('div');
+ // popupElement.id = popupId;
+
+ // // OpenLayers Overlay 생성
+ // const position = olProj.transform([Number(data.x), Number(data.y)], GisApp.FROM_PROJ, GisApp.TO_PROJ);
+ // const overlay = new Overlay({
+ // element: popupElement,
+ // position: position,
+ // offset: [-75, -210], // 팝업 너비의 절반, 높이만큼 오프셋 조정
+ // positioning: 'bottom-center',
+ // className: 'facility-popup-overlay' // 필요시 공통 클래스 추가
+ // });
+
+ // map.addOverlay(overlay);
+ // facilityOverlaysRef.current.push(overlay);
+
+ // // React 컴포넌트를 popupElement에 렌더링
+ // const root = createRoot(popupElement);
+ // root.render();
+ // });
+
+ // // 초기 가시성 업데이트
+ // updatePopupVisibility();
+
+ // }, [facilityInfoPopupData]); // 팝업 데이터가 변경될 때마다 실행
+
+ // // 레이어 가시성에 따라 팝업 가시성을 제어하는 함수
+ // const updatePopupVisibility = useCallback(() => {
+ // const map = mapInstanceRef.current;
+ // if (!map) return;
+
+ // const visibleLayers = new Set();
+ // map.getLayers().forEach(layer => {
+ // if (layer.getVisible()) {
+ // // feature.js의 로직처럼 typeName이나 ftrCde를 사용해 필터링
+ // const typeName = layer.get('typeName');
+ // if (typeName === 'VW_WTL_FCLTY_AS') { // 예시 레이어 이름
+ // visibleLayers.add(layer.get('ftrCde'));
+ // }
+ // }
+ // });
+
+ // facilityOverlaysRef.current.forEach(overlay => {
+ // const element = overlay.getElement();
+ // const ftrCde = element.querySelector('table')?.classList[0]; // FacilityPopup의 클래스에서 ftrCde 가져오기
+
+ // if (visibleLayers.has(ftrCde)) {
+ // element.style.display = '';
+ // } else {
+ // element.style.display = 'none';
+ // }
+ // });
+ // }, []);
+
+ // GisTree에서 체크 상태가 변경될 때 이 함수를 호출하도록 연결해야 합니다.
+ // 예시: GisTree 컴포넌트에 onCheck 콜백을 추가하고, 그 콜백에서 이 함수를 호출
+ // useEffect(() => {
+ // // 레이어 가시성이 변경될 때마다 팝업 가시성 업데이트
+ // const map = mapInstanceRef.current;
+ // if (!map) return;
+
+ // const handleLayerVisibilityChange = () => {
+ // updatePopupVisibility();
+ // };
+
+ // map.getLayers().on('change:length', handleLayerVisibilityChange);
+ // map.getLayers().forEach(layer => {
+ // layer.on('change:visible', handleLayerVisibilityChange);
+ // });
+
+ // return () => {
+ // // 클린업
+ // map.getLayers().un('change:length', handleLayerVisibilityChange);
+ // map.getLayers().forEach(layer => {
+ // layer.un('change:visible', handleLayerVisibilityChange);
+ // });
+ // };
+ // }, [updatePopupVisibility]);
+
+ // 체크된 레이어 정보를 처리하는 함수
+ const handleCheckedLayersChange = useCallback((checkedIds, checkedNodesData) => {
+ setTimeout(() => {
+ setCheckedLayers(checkedIds);
+ setCheckedLayersData(checkedNodesData);
+ onCheckedLayersChange?.(checkedIds, checkedNodesData);
+ }, 0);
+ }, [onCheckedLayersChange]);
+
return (
<>
-
+
{
+ setLayerNm(nodeNm); setIsOpen(true); const dummyFeature = {
+ getProperties: () => ({}), // 최소 구조
+ };
+ onFeatureSelect(dummyFeature);
+ }}
+ />
{/* */}
- {/* isSearchModalOpen이 true면 visible=false로 팝업 숨김 */}
- {isOpen && featureData && !isSearchModalOpen && (
+ {/* DetailView가 존재할 때만 렌더링 isSearchModalOpen이 true면 visible=false로 팝업 숨김 */}
+ {isOpen && !isSearchModalOpen && (
({}) }}
+ seletectedLayerNm={layerNm}
onClose={closePopup} // 닫기 콜백 전달
/>
)}
- {SearchView &&(
-
+ {/* SearchView가 존재할 때만 렌더링 */}
+ {SearchView && (
+
)}
-
+
{isSearch && (
diff --git a/packages/common/business_components/src/ui/map/GisTree.js b/packages/common/business_components/src/ui/map/GisTree.js
index 3c2cb2819..5b30dc7f4 100644
--- a/packages/common/business_components/src/ui/map/GisTree.js
+++ b/packages/common/business_components/src/ui/map/GisTree.js
@@ -6,10 +6,11 @@ import {
import { ChevronLeft, ChevronRight } from 'lucide-react';
import React, { useEffect, useState } from 'react';
import { DndProvider } from "react-dnd";
+
import { useGisContext } from './Gis';
import GisApp from './lib/gis.core';
-export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes = () => { }, visible, setVisible, onCheckedChange }) {
+export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes = () => { }, visible, setVisible, onCheckedChange, onLayerIconClick }) {
const { gisCore, treeData } = useGisContext();
const [checked, setChecked] = useState([]);
const [expanded, setExpanded] = useState([]);
@@ -187,7 +188,10 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes
image.src = canvas.toDataURL('image/png');
return image;
};
-
+ const handleLayerImgClick = (nodeNm) => {
+ console.log('click!', nodeNm)
+ onLayerIconClick(nodeNm)
+ }
if (!treeData || treeData.length === 0) return null;
return (
@@ -220,6 +224,7 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes
handleCheck(node.id)} className="cursor-pointer" />
{node.icon && {node.icon}}
{node.text}
+ {depth > 0 &&
{handleLayerImgClick(node.text)}} alt={node.text}>}
)}
sort={(a, b) => {