"use client"; import Gis from "@common/business_components/ui/map/Gis"; import { useEffect, useRef, useState, useCallback } from "react"; import { mapService } from "../../service"; import GisApp from "@common/business_components/ui/map/lib/gis.core"; import { SearchBar } from "@common/business_components/ui/map/searchBar/searchBar"; import { useWrStore } from "@/app/store"; import { getFeatureCd } from "@/components/lib/func/mapUtil"; import toast, { Toaster } from "react-hot-toast"; const Map = () => { const { comMap } = useWrStore((state) => state); const { isFeatured } = comMap; const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정 const gisMaps = useRef(null); const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태 const [isSearchModalOpen, setIsSearchModalOpen] = useState(true); // SearchModal 열림 상태 const [featureData, setFeatureData] = useState(null); // 선택된 피쳐 데이터 const [checkedLayers, setCheckedLayers] = useState([]); const [checkedLayersData, setCheckedLayersData] = useState([]); const popupRef = useRef(null); const popupDetailRef = useRef(null); const fetchData = async () => { try { const response = await mapService.getLyrStngs(); setTreeData(response); // API 응답 저장 } catch (error) { console.error("데이터 불러오기 실패:", error); } }; // 팝업 닫기 핸들러를 useCallback으로 메모이제이션 // const handlePopupClose = useCallback(() => { // setIsOpen(false); // setFeatureData(null); // }, []); // 컴포넌트 마운트 시 1회 실행 useEffect(() => { fetchData(); }, []); useEffect(() => { const bc = new BroadcastChannel("mapDetailPopUp"); bc.onmessage = (event) => { if (event.data.type === "SELECT_FEATURE") { onFeatureSelect(event.data.feature); closePopup(); } }; }, []); useEffect(() => { const messageHandler = (event: MessageEvent) => { if (event.data.type === "POPUP_CLOSED") { if (event.data.target === "detail") { console.log("디테일 팝업 닫힘 감지됨"); setFeatureData(null); setIsOpen(false); setIsSearchModalOpen(true); popupDetailRef.current = null; } else { console.log("기타 팝업 닫힘 감지됨"); popupRef.current = null; } } }; window.addEventListener("message", messageHandler); return () => { window.removeEventListener("message", messageHandler); }; }, []); useEffect(() => { console.log("isFeatured ", isFeatured); if (isFeatured) { console.log("isFeatured ", isFeatured); onFeatureSelect(isFeatured); } }, [isFeatured]); // 체크된 레이어 정보를 받는 핸들러 const handleCheckedLayersChange = ( checkedIds: string[], checkedNodesData: any[] ) => { setCheckedLayers(checkedIds); setCheckedLayersData(checkedNodesData); }; // SearchModal, GisContent 등에서 호출 const onFeatureFocus = (data: any) => { console.log(data); }; const onFeatureSelect = (data: any) => { const map = gisMaps.current; if (!map) { console.warn("지도가 초기화되지 않았습니다."); return; } // 항상 같은 select 객체 사용 const select = GisApp.Module.select as any; if (!select) return; // 1. 기존 선택 초기화 select.resetSelectedFeature(); // 2. data가 OpenLayers Feature 인스턴스인지 확인 let foundFeature: any = null; let featureId = data.fid; const layerName = data.layerNm; console.log("선택된 레이어 이름:", data, layerName); if (layerName === "하수재이용시설") { // 재이용시설 featureId = `${layerName}.${data.searchPk}`; } else if ( //빗물이용시설, 중수도시설 layerName === "빗물이용시설" || layerName == "중수도시설" ) { featureId = `${layerName}.${data.bdstInfoSn}`; } else if (layerName === "하수처리시설") { //하수처리시설 featureId = `${layerName}.${data.bscFctCd}`; } else if (layerName === "온배수시설") { //온배수시설 featureId = `${layerName}.${data.bdstRuseFcltSn}`; } else if (layerName == "수요처") { //수요처 featureId = `${layerName}.${data.dmdInfoId.replaceAll("-", "")}`; } else { console.warn("해당 레이어에 대한 featureId 매핑 없음", layerName); featureId = `${layerName}.${data.fid || data.id || data.ftrIdn}`; } map .getLayers() .getArray() .forEach((layer: any) => { const source = layer.getSource?.(); if (source?.getFeatureById) { const feature = source.getFeatureById(String(featureId).trim()); if (feature) foundFeature = feature; } }); // 3. 피처 선택 if (foundFeature) { const geometry = foundFeature.getGeometry(); if (geometry) { const extent = geometry.getExtent(); map.getView().fit(extent, { duration: 300, padding: [50, 50, 50, 50], maxZoom: 16, constrainResolution: true, nearest: true, }); } select.selectFeature(foundFeature, true); } else { console.warn("OpenLayers Feature를 찾을 수 없습니다:", data); } const currentId = data?.fid || data?.id || data?.ftrIdn; const lastId = featureData?.fid || featureData?.id || featureData?.ftrIdn; if (featureData && currentId === lastId) return; // 이전과 같으면 무시 setFeatureData(data); setIsOpen(true); setIsSearchModalOpen(false); }; const getSelectedLayers = (isFeature: any) => { const map = gisMaps.current; if (!map) { console.warn("지도가 초기화되지 않았습니다."); return; } let result = null; const layers = map ? map .getLayers() .getArray() .filter((l: any) => l.getVisible()) : []; // gisMap이 null인지 확인 후 레이어 가져오기 let featureLayer: { get: (key: string) => any } = { get: () => null }; layers.forEach((layer: any) => { const source = layer.getSource(); if ( source && source.hasFeature && isFeature && typeof isFeature.getId === "function" && source.hasFeature(isFeature) ) { featureLayer = layer; // 피처가 속한 레이어 찾기) } }); result = featureLayer.get("lyrNm"); return result; }; const renderDetailView = useCallback( (props: any) => { const map = gisMaps.current; if (!map) { console.warn("지도가 초기화되지 않았습니다."); return; } const isFeature = props?.featureData; console.log('isFeature 1', isFeature) if( typeof isFeature.getProperties === "function" ){ if ( Object.keys(isFeature?.getProperties()).length === 0 && isFeature.constructor === Object ) { const url = `/mapDetailPopUp?layerNm=${encodeURIComponent(props.seletectedLayerNm)}`; const width = window.screen.availWidth; const height = window.screen.availHeight; const left = 0; const top = 0; const popupOptions = `width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=no`; popupRef.current = window.open(url, "지도 레이어 팝업", popupOptions); } else { const layerNm = getSelectedLayers(isFeature); const safeProps = isFeature.getProperties(); const featureCd = getFeatureCd(safeProps, String(layerNm)); const url = `/mapFeaturePopup?layerNm=${encodeURIComponent(String(layerNm))}&featureCd=${encodeURIComponent(featureCd)} `; const width = String(layerNm) === "하수재이용시설" ? Math.floor(window.innerWidth * 0.8) : Math.floor(window.innerWidth * 0.4); // 화면 폭의 40% const height = String(layerNm) === "하수재이용시설" ? Math.floor(window.innerHeight - 150) : Math.floor(window.innerHeight - 450); // 150px 여백 확보; // calc(100% - 70px) const left = (window.screen.availWidth - width) / 2; const top = (window.screen.availHeight - height) / 2; const popupOptions = `width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes`; popupDetailRef.current = window.open( url, "지도 시설 상세정보 팝업", popupOptions ); } }else{ console.log(' data확인 필요') } }, [checkedLayers, checkedLayersData, featureData] // 필요 시 의존성 넣기 ); const closePopup = () => { if (popupRef.current && !popupRef.current.closed) { popupRef.current.close(); popupRef.current = null; setIsOpen(false); setFeatureData(null); } }; const onOpenPopup = (data:any) => { if(data){ const url = `/mapDetailPopUp?layerNm=${encodeURIComponent(data)}`; const width = window.screen.availWidth; const height = window.screen.availHeight; const left = 0; const top = 0; const popupOptions = `width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=no`; popupRef.current = window.open(url, "지도 레이어 팝업", popupOptions); }else{ toast.error('시설 및 수요처가 선택되지 않았습니다.') } } return (
} waterDepth={undefined} onOpenPopup={(data:any) =>{ onOpenPopup(data) }} />
); }; export default Map;