353 lines
12 KiB
TypeScript
353 lines
12 KiB
TypeScript
"use client";
|
|
|
|
import Gis from "@common/business_components/ui/map/Gis";
|
|
import { useEffect, useRef, useState } from "react";
|
|
import { mapService } from "../service";
|
|
import GisApp from "@common/business_components/ui/map/lib/gis.core";
|
|
import { SearchBar } from "@/components/ui/map/searchBar/searchBar";
|
|
import SelectedFcltList from "./selectedFcltList";
|
|
import type { SubsInfo } from "@/app/store/map/type";
|
|
|
|
const Map = () => {
|
|
const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정
|
|
const gisMaps = useRef<any | null>(null);
|
|
const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태
|
|
const [isSearchModalOpen, setIsSearchModalOpen] = useState(true); // SearchModal 열림 상태
|
|
const [featureData, setFeatureData] = useState(null); // 선택된 피쳐 데이터
|
|
const [selectDataPk, setSelectDataPk] = useState<Record<string, any>>({});
|
|
const [selectedFclts, setSelectedFclts] = useState<any[]>([]);
|
|
|
|
//적용 버튼 클릭 이벤트
|
|
const onApply = () => {
|
|
if (selectedFclts.length > 3) {
|
|
alert("재이용시설 후보지는 최대 3개까지만 선택할 수 있습니다.");
|
|
return;
|
|
}
|
|
|
|
const subsInfoList: SubsInfo[] = selectedFclts.map((fclt) => ({
|
|
ruseUtztnFcltNm: fclt.ruseUtztnFcltNm,
|
|
ruseUtztnFcltCd: fclt.pbnstRuseFcltId,
|
|
}));
|
|
|
|
if (!window.opener || typeof window.opener.postMessage !== "function") {
|
|
alert("부모 창을 찾을 수 없습니다. 팝업이 'noopener'로 열렸거나, 보안 정책으로 차단되었을 수 있어요.");
|
|
return;
|
|
}
|
|
|
|
window.opener.postMessage(
|
|
{
|
|
type: "SET_SUBS_INFO",
|
|
payload: subsInfoList, // 배열로 전송 (최대 3개)
|
|
},
|
|
"*"
|
|
);
|
|
|
|
window.close();
|
|
};
|
|
|
|
//트리 레이어 데이터 조회
|
|
const fetchData = async () => {
|
|
try {
|
|
const response = await mapService.getLyrStngs();
|
|
setTreeData(response); // API 응답 저장
|
|
} catch (error) {
|
|
console.error("데이터 불러오기 실패:", error);
|
|
}
|
|
};
|
|
|
|
//하수처리시설코드로 재이용시설 목록 조회
|
|
const reuseData = async (ruseFcltPk: any) => {
|
|
if (!ruseFcltPk) {
|
|
console.warn("[reuseData] ruseFcltPk가 없습니다.");
|
|
return;
|
|
}
|
|
try {
|
|
console.log("[reuseData] API 호출 - ruseFcltPk:", ruseFcltPk);
|
|
const response = await mapService.getPbnstRuseFclts(ruseFcltPk);
|
|
console.log("[reuseData] API 응답 전체:", response);
|
|
// 응답이 배열이면 바로 사용, 객체면 .data 추출
|
|
const data = Array.isArray(response)
|
|
? response
|
|
: Array.isArray(response?.data)
|
|
? response.data
|
|
: response?.data
|
|
? [response.data]
|
|
: [];
|
|
console.log("[reuseData] 파싱된 data:", data);
|
|
setSelectedFclts((prev) => {
|
|
const newItems = data.filter(
|
|
(item: any) =>
|
|
item &&
|
|
Object.keys(item).length > 0 &&
|
|
!prev.some((f) => f.pbnstRuseFcltId === item.pbnstRuseFcltId)
|
|
);
|
|
console.log("[reuseData] 추가될 newItems:", newItems);
|
|
return [...prev, ...newItems];
|
|
});
|
|
} catch (error) {
|
|
console.error("데이터 불러오기 실패:", error);
|
|
}
|
|
};
|
|
|
|
// 컴포넌트 마운트 시 1회 실행
|
|
useEffect(() => {
|
|
fetchData();
|
|
}, []);
|
|
|
|
// 부모창에 초기 선택값 요청 (팝업 열릴 때 기존 선택 목록 받아오기)
|
|
useEffect(() => {
|
|
const origin = window.location.origin;
|
|
if (window.opener) {
|
|
window.opener.postMessage({ type: "REQUEST_INIT_SUBS_INFO" }, origin);
|
|
}
|
|
}, []);
|
|
|
|
// 부모창에서 초기 선택값 수신
|
|
useEffect(() => {
|
|
const origin = window.location.origin;
|
|
|
|
const handler = (event: MessageEvent) => {
|
|
if (event.origin !== origin) return;
|
|
|
|
const data = event.data;
|
|
if (!data || typeof data !== "object") return;
|
|
|
|
if (data.type === "INIT_SUBS_INFO") {
|
|
const list = Array.isArray(data.payload) ? data.payload : [];
|
|
setSelectedFclts(
|
|
list.map((x) => ({
|
|
ruseUtztnFcltNm: x.ruseUtztnFcltNm ?? "",
|
|
pbnstRuseFcltId: x.ruseUtztnFcltCd ?? "",
|
|
}))
|
|
);
|
|
}
|
|
};
|
|
|
|
window.addEventListener("message", handler);
|
|
return () => window.removeEventListener("message", handler);
|
|
}, []);
|
|
|
|
// 레이어 생성 후 초기화 처리
|
|
const onCheckedLayersChange = () => {
|
|
//수요처 ID
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const dmdInfoId = urlParams.get("dmdInfoId");
|
|
console.log("[하이라이트] dmdInfoId:", dmdInfoId);
|
|
|
|
//수요처 위치로 화면 이동
|
|
const maps = gisMaps.current;
|
|
const layer = GisApp.Module.layer as any;
|
|
const layerNm = "tb_wr_ruse_fclt_dmd_co";
|
|
const featureId = `${layerNm}.${dmdInfoId}`;
|
|
let foundFeature: any = null;
|
|
|
|
// 현재 로드된 레이어 목록 출력
|
|
const allLayerNames = maps
|
|
.getLayers()
|
|
.getArray()
|
|
.map((l: any) => l.get("lyrTyNm") || l.get("name") || "(unnamed)");
|
|
console.log("[하이라이트] 전체 레이어 목록:", allLayerNames);
|
|
console.log("[하이라이트] 탐색 대상 레이어:", layerNm);
|
|
console.log("[하이라이트] 탐색 대상 featureId:", featureId);
|
|
|
|
maps
|
|
.getLayers()
|
|
.getArray()
|
|
.forEach((layer: any) => {
|
|
if (layer.get("lyrTyNm") == layerNm) {
|
|
console.log("[하이라이트] 레이어 발견:", layerNm);
|
|
const source = layer.getSource?.();
|
|
if (source?.getFeatureById) {
|
|
const feature = source.getFeatureById(String(featureId).trim());
|
|
console.log("[하이라이트] feature 탐색결과:", feature);
|
|
if (feature) {
|
|
foundFeature = feature;
|
|
return;
|
|
}
|
|
// feature 못 찾을 때 소스 내 전체 feature ID 출력
|
|
if (source?.getFeatures) {
|
|
const allIds = source.getFeatures().map((f: any) => f.getId());
|
|
console.log("[하이라이트] 소스 내 전체 feature ID:", allIds);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
if (!foundFeature) {
|
|
console.warn("[하이라이트] feature를 찾지 못했습니다. 위 레이어 목록/ID를 확인하세요.");
|
|
}
|
|
|
|
if (foundFeature) {
|
|
const geometry = foundFeature.getGeometry();
|
|
|
|
if (geometry) {
|
|
const extent = geometry.getExtent();
|
|
|
|
if (geometry.getType() === "Point") {
|
|
maps.getView().setCenter(geometry.getCoordinates());
|
|
maps.getView().setZoom(11);
|
|
} else {
|
|
maps.getView().fit(extent, {
|
|
duration: 300,
|
|
padding: [30, 30, 30, 30],
|
|
maxZoom: 16,
|
|
});
|
|
}
|
|
}
|
|
|
|
layer.highLightFeature(
|
|
[foundFeature],
|
|
{
|
|
id: GisApp.hlLayerId,
|
|
zIndex: 10,
|
|
opacity: 1,
|
|
selectable: true,
|
|
},
|
|
{ width: 30 }
|
|
);
|
|
} else {
|
|
console.warn("수요처의 좌표정보를 찾을 수 없습니다. dmdInfoId:", dmdInfoId);
|
|
}
|
|
|
|
const formatNumber = (val: any) => {
|
|
const num = Number(val);
|
|
return isNaN(num) ? "-" : num.toLocaleString();
|
|
};
|
|
|
|
// 재이용시설 오버레이
|
|
maps
|
|
.getLayers()
|
|
.getArray()
|
|
.forEach((layer: any) => {
|
|
if (layer.get("lyrTyNm") === "tb_wr_bdst_ruse_fclt") {
|
|
const source = layer.getSource?.();
|
|
if (source?.getFeatures) {
|
|
const features = source.getFeatures();
|
|
|
|
features.forEach((feature: any) => {
|
|
// 필요한 고유 ID 또는 속성 가져오기 (예: ftrIdn 또는 feature.getId())
|
|
const featureId =
|
|
feature.getId() || feature.get("ftrIdn") || Math.random();
|
|
|
|
// HTML 또는 React 오버레이 컴포넌트 구성
|
|
const overlayContent = `
|
|
<div class="rounded-md border border-gray-300 bg-white shadow-md text-sm w-[120px]">
|
|
<!-- 헤더 -->
|
|
<div class="bg-cyan-700 text-white px-3 py-2 text-[11px] font-semibold text-center rounded-t-md">
|
|
${feature.get("swtrmt_fclt_nm") || "-"}
|
|
</div>
|
|
|
|
<!-- 테이블 -->
|
|
<table class="w-full text-sm text-gray-800 border-t border-gray-200">
|
|
<tbody>
|
|
<tr class="border-b border-gray-200">
|
|
<th class="bg-gray-100 text-gray-600 font-semibold text-[10px] text-left">용량(m³/일)</th>
|
|
<td class="text-right text-[10px] font-bold">${formatNumber(feature.get("fclt_cpct"))}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
`;
|
|
feature.set("html", overlayContent);
|
|
feature.set("overLayId", `overLay.${featureId}`);
|
|
});
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
// 검색해서 재이용시설 선택하면 해당 시설로 화면 이동 처리
|
|
useEffect(() => {
|
|
console.warn(selectDataPk);
|
|
if (!selectDataPk || Object.keys(selectDataPk).length === 0) return;
|
|
const maps = gisMaps.current;
|
|
let foundFeature: any = null;
|
|
const layerNm = "tb_wr_bdst_ruse_fclt";
|
|
const featureId = `${layerNm}.${selectDataPk.bscFctCd}`;
|
|
maps
|
|
.getLayers()
|
|
.getArray()
|
|
.forEach((layer: any) => {
|
|
if (layer.get("lyrTyNm") == layerNm) {
|
|
const source = layer.getSource?.();
|
|
if (source?.getFeatureById) {
|
|
const feature = source.getFeatureById(String(featureId).trim());
|
|
if (feature) {
|
|
foundFeature = feature;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
if (foundFeature) {
|
|
const geometry = foundFeature.getGeometry();
|
|
if (geometry) {
|
|
const extent = geometry.getExtent();
|
|
|
|
maps.getView().fit(extent, {
|
|
duration: 300,
|
|
padding: [50, 50, 50, 50],
|
|
maxZoom: 16,
|
|
constrainResolution: true,
|
|
nearest: true,
|
|
});
|
|
}
|
|
} else {
|
|
console.warn("OpenLayers Feature를 찾을 수 없습니다:", selectDataPk);
|
|
}
|
|
}, [selectDataPk]);
|
|
|
|
// 재이용시설 클릭 시 선택 시설목록에 추가
|
|
const onFeatureSelect = (data: any) => {
|
|
// feature의 모든 속성 출력 (어떤 키가 있는지 확인)
|
|
const properties = data.getProperties?.() ?? {};
|
|
console.log("[onFeatureSelect] feature 속성 전체:", properties);
|
|
// 처리시설코드
|
|
const swtrmtFcltCd = data.get("swtrmt_fclt_cd");
|
|
console.log("[onFeatureSelect] swtrmt_fclt_cd:", swtrmtFcltCd);
|
|
reuseData(swtrmtFcltCd);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<div className="bg-container">
|
|
<Gis
|
|
showZoomLevel={true}
|
|
height='h-screen'
|
|
treeData={treeData}
|
|
isSearch={false}
|
|
gisMaps={gisMaps}
|
|
isMiniMap={false}
|
|
searchView={undefined}
|
|
detailView={undefined}
|
|
onFeatureSelect={onFeatureSelect}
|
|
setIsOpen={setIsOpen}
|
|
setFeatureData={setFeatureData}
|
|
featureData={featureData}
|
|
setIsSearchModalOpen={setIsSearchModalOpen}
|
|
isOpen={isOpen}
|
|
isSearchModalOpen={isSearchModalOpen}
|
|
onCheckedLayersChange={onCheckedLayersChange}
|
|
imgVisble={false}
|
|
searchBar={
|
|
<SearchBar
|
|
setHasuPk={(hasuPk: any) => {
|
|
if (hasuPk) setSelectDataPk(hasuPk);
|
|
}}
|
|
onApply={onApply}
|
|
/>
|
|
}
|
|
waterDepth={undefined}
|
|
/>
|
|
</div>
|
|
<SelectedFcltList
|
|
data={selectedFclts}
|
|
onRemove={(updatedList) => setSelectedFclts(updatedList)}
|
|
/>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default Map;
|