diff --git a/apps/wr/app/(map-layout)/map/mainMap/page.tsx b/apps/wr/app/(map-layout)/map/mainMap/page.tsx index 362d45cb8..927703ad9 100644 --- a/apps/wr/app/(map-layout)/map/mainMap/page.tsx +++ b/apps/wr/app/(map-layout)/map/mainMap/page.tsx @@ -21,7 +21,7 @@ import WrOverLay from "@/components/ui/map/wrOverLay/wrOverLay"; const Map = () => { - const {code, comMap ,rainMapDetile ,rswtMapDetile,getRuseMaplListReturn,pbnMapDetaile,ldrmntMapDetile,htdfMapDetile,coSttnMapDetail} = useWrStore((state) => state); + const { code, comMap, rainMapDetile, rswtMapDetile, getRuseMaplListReturn, pbnMapDetaile, ldrmntMapDetile, htdfMapDetile, coSttnMapDetail } = useWrStore((state) => state); const { isFeatured } = comMap; const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정 const gisMaps = useRef(null); @@ -93,7 +93,6 @@ const Map = () => { return; } - // 항상 같은 select 객체 사용 const select = GisApp.Module.select as any; if (!select) return; @@ -109,7 +108,6 @@ const Map = () => { const layerName = getWrLayerNm(data); const featurePk = getWrFeaturePk(layerName ?? ""); - // 재이용시설 if (featurePk) { const lastPk = @@ -254,139 +252,139 @@ const Map = () => { const OVERLAY_MIN_ZOOM = 16; -const OVERLAY_COORD_PRECISION = 5; -const OVERLAY_MAX_PER_VIEW = 800; + const OVERLAY_COORD_PRECISION = 5; + const OVERLAY_MAX_PER_VIEW = 800; -const overlayManagersRef = useRef([]); -const overlayActiveRef = useRef(false); -const moveEndDebounceRef = useRef(null); + const overlayManagersRef = useRef([]); + const overlayActiveRef = useRef(false); + const moveEndDebounceRef = useRef(null); -// 반환: Promise -const overlayFormatter = async (feature: any, props: any) => { - const p = props || {}; - const featureId = - feature.getId?.() ?? p?.id ?? p?.fid ?? p?.ftrIdn ?? ''; - if (!featureId) return null; + // 반환: Promise + const overlayFormatter = async (feature: any, props: any) => { + const p = props || {}; + const featureId = + feature.getId?.() ?? p?.id ?? p?.fid ?? p?.ftrIdn ?? ''; + if (!featureId) return null; - const tableId = String(featureId).split('.')[0]??''; - const fcltCd = getFeatureCd(props, tableId); + const tableId = String(featureId).split('.')[0] ?? ''; + const fcltCd = getFeatureCd(props, tableId); - if (!fcltCd) return null; + if (!fcltCd) return null; - let data: any = null; - let title = ''; + let data: any = null; + let title = ''; - if (tableId === 'tb_il_swage_prcs_fclt') { data = await pbnMapDetaile(fcltCd); title = '하수처리시설'; } - else if (tableId === 'tb_wr_bdst_ruse_fclt') { data = await getRuseMaplListReturn(fcltCd); title = '하수재이용시설'; } - else if (tableId === 'tb_wr_rnwtr_utztn_fclt'){ data = await rainMapDetile(fcltCd); title = '빗물이용시설'; } - else if (tableId === 'tb_wr_rswtr_fclt') { data = await rswtMapDetile(fcltCd); title = '중수도시설'; } - else if (tableId === 'tb_wr_htdfl_fclt') { data = await htdfMapDetile(fcltCd); title = '온배수시설'; } - else if (tableId === 'tb_wr_ruse_fclt_dmd_co'){ data = await coSttnMapDetail(fcltCd); title = '수요처'; } - else if (tableId === 'tb_wr_tmpr_dmd_co') { data = await ldrmntMapDetile(fcltCd); title = '잠재수요처'; } + if (tableId === 'tb_il_swage_prcs_fclt') { data = await pbnMapDetaile(fcltCd); title = '하수처리시설'; } + else if (tableId === 'tb_wr_bdst_ruse_fclt') { data = await getRuseMaplListReturn(fcltCd); title = '하수재이용시설'; } + else if (tableId === 'tb_wr_rnwtr_utztn_fclt') { data = await rainMapDetile(fcltCd); title = '빗물이용시설'; } + else if (tableId === 'tb_wr_rswtr_fclt') { data = await rswtMapDetile(fcltCd); title = '중수도시설'; } + else if (tableId === 'tb_wr_htdfl_fclt') { data = await htdfMapDetile(fcltCd); title = '온배수시설'; } + else if (tableId === 'tb_wr_ruse_fclt_dmd_co') { data = await coSttnMapDetail(fcltCd); title = '수요처'; } + else if (tableId === 'tb_wr_tmpr_dmd_co') { data = await ldrmntMapDetile(fcltCd); title = '잠재수요처'; } - const empty = - data == null || - (Array.isArray(data) && data.length === 0) || - (typeof data === 'object' && !Array.isArray(data) && Object.keys(data).length === 0); + const empty = + data == null || + (Array.isArray(data) && data.length === 0) || + (typeof data === 'object' && !Array.isArray(data) && Object.keys(data).length === 0); - if (empty) return null; + if (empty) return null; - const div = document.createElement('div'); - const root = createRoot(div); - (div as any).__dispose = () => { try { root.unmount(); } catch {} }; - root.render(); - return div; // ← 데이터 있을 때만 HTMLElement 반환 -}; -const destroyAllManagers = () => { - overlayManagersRef.current.forEach(m => { try { m.destroy(); } catch {} }); - overlayManagersRef.current = []; - overlayActiveRef.current = false; -}; + const div = document.createElement('div'); + const root = createRoot(div); + (div as any).__dispose = () => { try { root.unmount(); } catch { } }; + root.render(); + return div; // ← 데이터 있을 때만 HTMLElement 반환 + }; + const destroyAllManagers = () => { + overlayManagersRef.current.forEach(m => { try { m.destroy(); } catch { } }); + overlayManagersRef.current = []; + overlayActiveRef.current = false; + }; -const mountOverlayManagers = () => { - const map = gisMaps.current; - if (!map) return; + const mountOverlayManagers = () => { + const map = gisMaps.current; + if (!map) return; - // 이전 것 정리 - destroyAllManagers(); + // 이전 것 정리 + destroyAllManagers(); - // 체크된 벡터 레이어만 골라 매니저 생성 - const checkedSet = new Set(checkedLayers.map(String)); - const layers: any[] = map.getLayers().getArray(); - const targets = layers.filter((layer: any) => { - const src = layer.getSource?.(); - const isVector = src && typeof src.getFeatures === "function"; - const id = String(layer.get?.("lyrSn") ?? layer.get?.("id") ?? ""); - return isVector && layer.getVisible?.() && checkedSet.has(id); - }); - - targets.forEach((layer: any) => { - const mgr = createOverlayManager(map, layer, overlayFormatter, { - coordPrecision: OVERLAY_COORD_PRECISION, - maxOverlaysPerViewport: OVERLAY_MAX_PER_VIEW, + // 체크된 벡터 레이어만 골라 매니저 생성 + const checkedSet = new Set(checkedLayers.map(String)); + const layers: any[] = map.getLayers().getArray(); + const targets = layers.filter((layer: any) => { + const src = layer.getSource?.(); + const isVector = src && typeof src.getFeatures === "function"; + const id = String(layer.get?.("lyrSn") ?? layer.get?.("id") ?? ""); + return isVector && layer.getVisible?.() && checkedSet.has(id); }); - mgr.mount(); // 리스너 1회 등록 - mgr.enable(); // 표시 + 1회 sync - overlayManagersRef.current.push(mgr); - }); - overlayActiveRef.current = overlayManagersRef.current.length > 0; -}; + targets.forEach((layer: any) => { + const mgr = createOverlayManager(map, layer, overlayFormatter, { + coordPrecision: OVERLAY_COORD_PRECISION, + maxOverlaysPerViewport: OVERLAY_MAX_PER_VIEW, + }); + mgr.mount(); // 리스너 1회 등록 + mgr.enable(); // 표시 + 1회 sync + overlayManagersRef.current.push(mgr); + }); -useEffect(() => { - const map = gisMaps.current; - if (!map) return; - - const z = map.getView()?.getZoom?.() ?? 0; - if (z < OVERLAY_MIN_ZOOM) { - // 저줌에선 그냥 안 보이게만 - overlayManagersRef.current.forEach(m => { try { m.disable({ clear:true, delayed:true }); } catch {} }); - return; - } - mountOverlayManagers(); - - return () => { destroyAllManagers(); }; -}, [checkedLayers]); - - -useEffect(() => { - const map = gisMaps.current; - if (!map) return; - - const onMoveStart = () => { - // 숨기기만 (리스너/DOM 대량 제거 X) - overlayManagersRef.current.forEach(m => { try { m.disable({ clear:false }); } catch {} }); - if (moveEndDebounceRef.current) { clearTimeout(moveEndDebounceRef.current); moveEndDebounceRef.current = null; } + overlayActiveRef.current = overlayManagersRef.current.length > 0; }; - const onMoveEnd = () => { + useEffect(() => { + const map = gisMaps.current; + if (!map) return; + const z = map.getView()?.getZoom?.() ?? 0; - const active = overlayActiveRef.current; + if (z < OVERLAY_MIN_ZOOM) { + // 저줌에선 그냥 안 보이게만 + overlayManagersRef.current.forEach(m => { try { m.disable({ clear: true, delayed: true }); } catch { } }); + return; + } + mountOverlayManagers(); - if (moveEndDebounceRef.current) clearTimeout(moveEndDebounceRef.current); - moveEndDebounceRef.current = window.setTimeout(() => { - if (z < OVERLAY_MIN_ZOOM) { - if (active) overlayManagersRef.current.forEach(m => { try { m.disable({ clear:true, delayed:true }); } catch {} }); - return; - } - if (!active) { - mountOverlayManagers(); - } else { - // 다시 보이고 1회만 동기화 - overlayManagersRef.current.forEach(m => { try { m.enable(); m.syncOverlays(); } catch {} }); - } - }, 180) as unknown as number; - }; + return () => { destroyAllManagers(); }; + }, [checkedLayers]); - map.on("movestart", onMoveStart); - map.on("moveend", onMoveEnd); - return () => { - map.un("movestart", onMoveStart); - map.un("moveend", onMoveEnd); - if (moveEndDebounceRef.current) { clearTimeout(moveEndDebounceRef.current); moveEndDebounceRef.current = null; } - }; -}, [checkedLayers]); + + useEffect(() => { + const map = gisMaps.current; + if (!map) return; + + const onMoveStart = () => { + // 숨기기만 (리스너/DOM 대량 제거 X) + overlayManagersRef.current.forEach(m => { try { m.disable({ clear: false }); } catch { } }); + if (moveEndDebounceRef.current) { clearTimeout(moveEndDebounceRef.current); moveEndDebounceRef.current = null; } + }; + + const onMoveEnd = () => { + const z = map.getView()?.getZoom?.() ?? 0; + const active = overlayActiveRef.current; + + if (moveEndDebounceRef.current) clearTimeout(moveEndDebounceRef.current); + moveEndDebounceRef.current = window.setTimeout(() => { + if (z < OVERLAY_MIN_ZOOM) { + if (active) overlayManagersRef.current.forEach(m => { try { m.disable({ clear: true, delayed: true }); } catch { } }); + return; + } + if (!active) { + mountOverlayManagers(); + } else { + // 다시 보이고 1회만 동기화 + overlayManagersRef.current.forEach(m => { try { m.enable(); m.syncOverlays(); } catch { } }); + } + }, 180) as unknown as number; + }; + + map.on("movestart", onMoveStart); + map.on("moveend", onMoveEnd); + return () => { + map.un("movestart", onMoveStart); + map.un("moveend", onMoveEnd); + if (moveEndDebounceRef.current) { clearTimeout(moveEndDebounceRef.current); moveEndDebounceRef.current = null; } + }; + }, [checkedLayers]); return (
{ const featureCd = - layerNm === "tb_wr_bdst_ruse_fclt" + layerNm === "tb_wr_bdst_ruse_fclt" //하수재이용시설 ? featurePropertise?.swtrmt_fclt_cd : [ - "tb_wr_rswtr_fclt", - "tb_wr_htdfl_fclt", - "tb_wr_rnwtr_utztn_fclt", - "tb_wr_tmpr_dmd_co", - ].includes(layerNm) + "tb_wr_rswtr_fclt", // 중수도시설 + "tb_wr_htdfl_fclt", // 온배수시설 + "tb_wr_rnwtr_utztn_fclt", // 빗물이용시설 + "tb_wr_tmpr_dmd_co", // 잠재수요처 + ].includes(layerNm) ? featurePropertise?.bdst_nm - : layerNm === "tb_il_swage_prcs_fclt" + : layerNm === "tb_il_swage_prcs_fclt" //하수처리시설 ? featurePropertise?.swtrmt_fclt_cd - : layerNm === "tb_wr_ruse_fclt_dmd_co" + : layerNm === "tb_wr_ruse_fclt_dmd_co" //수요처 ? featurePropertise?.bdst_info_sn : null; @@ -71,9 +71,9 @@ export const getSelectedLayers = (isFeature: any, gisMaps: any) => { let result = null; const layers = map ? map - .getLayers() - .getArray() - .filter((l: any) => l.getVisible()) + .getLayers() + .getArray() + .filter((l: any) => l.getVisible()) : []; // gisMap이 null인지 확인 후 레이어 가져오기 let featureLayer: { get: (key: string) => any } = { get: () => null }; @@ -119,12 +119,12 @@ export const getClickFeatureId = (data: any) => { } else if (layerName == "수요처") { //수요처 featureId = `${layerName}.${data.dmdInfoId.replaceAll("-", "")}`; - }else if (layerName == "잠재수요처") { + } else if (layerName == "잠재수요처") { //잠재수요처 featureId = data.id_ - } else { - + } else { + featureId = data.id_ } @@ -160,7 +160,7 @@ export function featurePopUpOpen( ); } -export const getOverLayObj = (fclType: string, data: any,code?:any) => { +export const getOverLayObj = (fclType: string, data: any, code?: any) => { let newObj = {}; let title = ""; @@ -169,11 +169,11 @@ export const getOverLayObj = (fclType: string, data: any,code?:any) => { if (fclType === "하수재이용시설") { newObj = { - "시설용량(톤/일)": formatCommaNumber(data?.fcltCpct) ??'확인불가', - "재이용률(%)": (data?.fcltCpct / data?.bscFctCpc)||0, + "시설용량(톤/일)": formatCommaNumber(data?.fcltCpct) ?? '확인불가', + "재이용률(%)": (data?.fcltCpct / data?.bscFctCpc) || 0, "장내용수(m³/년)": formatCommaNumber(data?.inUsage) ?? '확인불가', - "장외용수(m3/년)":formatCommaNumber(data?.outUsage) ?? '확인불가', - // 수요처: data?.reuseBuilding[0]?.rainyObj.pntfColArea ?? '확인불가', + "장외용수(m3/년)": formatCommaNumber(data?.outUsage) ?? '확인불가', + // 수요처: data?.reuseBuilding[0]?.rainyObj.pntfColArea ?? '확인불가', }; title = data?.bdstNm ?? '확인불가'; } else if (fclType === "하수처리시설") { @@ -184,9 +184,9 @@ export const getOverLayObj = (fclType: string, data: any,code?:any) => { "재이용시설 등록여부": data?.ruse ?? '확인불가', }; title = data?.bscFctNm ?? '확인불가'; - } else if (fclType === "빗물이용시설" ) { + } else if (fclType === "빗물이용시설") { newObj = { - 건물용도: code?.bdstUsgCd.find((item) => item.val=== String(data?.reuseBuilding[0].bdstUsgCd) )?.text ?? '확인불가', + 건물용도: code?.bdstUsgCd.find((item) => item.val === String(data?.reuseBuilding[0].bdstUsgCd))?.text ?? '확인불가', 설치완료일: data?.reuseBuilding[0]?.instlCmptnDt ?? '확인불가', 처리시설유무: data?.reuseBuilding[0]?.rainyObj.filtrTrtmfctYn ?? '확인불가', "처리시설용량(m³/일)": formatCommaNumber(data?.reuseBuilding[0]?.rainyObj.filtrTrtmfctCpct) ?? '확인불가', @@ -195,25 +195,25 @@ export const getOverLayObj = (fclType: string, data: any,code?:any) => { title = data?.bdstNm ?? '확인불가'; } else if (fclType === "중수도시설") { newObj = { - 건물용도: code?.bdstUsgCd.find((item) => item.val===String(data?.reuseBuilding[0].bdstUsgCd) )?.text ?? '확인불가', + 건물용도: code?.bdstUsgCd.find((item) => item.val === String(data?.reuseBuilding[0].bdstUsgCd))?.text ?? '확인불가', 설치완료일: data?.reuseBuilding[0]?.instlCmptnDt ?? '확인불가', - "주요 처리공정": data?.reuseBuilding[0]?.rswtrObj.filtrTrtmfctProcs ?? '확인불가', - 원수종류: code?.orgwtKndCd.find((item) => item.val === data?.reuseBuilding[0]?.rswtrObj.orgwtKndCd)?.text ?? '확인불가', + "주요 처리공정": data?.reuseBuilding[0]?.rswtrObj.filtrTrtmfctProcs ?? '확인불가', + 원수종류: code?.orgwtKndCd.find((item) => item.val === data?.reuseBuilding[0]?.rswtrObj.orgwtKndCd)?.text ?? '확인불가', //이용용도: isCode.filtrTrtmfctUsgCd data?.reuseBuilding[0]?.rswtrObj.altUswtrKndCd ?? '확인불가', }; title = data?.bdstNm ?? '확인불가'; } else if (fclType === "온배수시설") { newObj = { - '발전유형': data?.reuseBuilding[0]?.htdflObj.etgtType??'', - '설비용량': data?.reuseBuilding[0]?.htdflObj.fclCpct??'', - '담수화 여부': data?.reuseBuilding[0]?.htdflObj.plwtYn??'', + '발전유형': data?.reuseBuilding[0]?.htdflObj.etgtType ?? '', + '설비용량': data?.reuseBuilding[0]?.htdflObj.fclCpct ?? '', + '담수화 여부': data?.reuseBuilding[0]?.htdflObj.plwtYn ?? '', }; title = data?.bdstNm ?? '확인불가'; } else if (fclType === "수요처") { newObj = { - '사업자등록번호': data?.dmdInfoId??'', - '주소': data?.daddr??'', - '전화번호': formatPhoneNumber(data?.telno)??'', + '사업자등록번호': data?.dmdInfoId ?? '', + '주소': data?.daddr ?? '', + '전화번호': formatPhoneNumber(data?.telno) ?? '', }; title = data?.dmdCoNm ?? '확인불가';