245 lines
7.3 KiB
JavaScript
245 lines
7.3 KiB
JavaScript
import { Search } from 'lucide-react';
|
|
import OlMap from 'ol/Map';
|
|
import View from 'ol/View';
|
|
import { defaults as defaultControls, OverviewMap } from 'ol/control';
|
|
import { defaults } from 'ol/interaction';
|
|
import * as olProj from 'ol/proj';
|
|
import { register } from 'ol/proj/proj4';
|
|
import proj4 from 'proj4';
|
|
import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
import GisLonLat from './GisLonLat';
|
|
import GisMapToggle from './GisMapToggle';
|
|
import GisSelect from './GisSelect';
|
|
import GisToolBar from './GisToolBar';
|
|
import GisTree from './GisTree';
|
|
import './lib/css/gis.common.css';
|
|
import GisApp from './lib/gis.core';
|
|
|
|
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 }) {
|
|
const mapRef = useRef();
|
|
const [mapObject, setMapObject] = useState({});
|
|
const [tabVisible, setTabVisible] = useState(true);
|
|
// 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([]);
|
|
let gisMap = null;
|
|
const closePopup = () => {
|
|
setIsOpen(false); // 팝업 닫기
|
|
setFeatureData(null)
|
|
};
|
|
|
|
// SearchModal, GisContent 등에서 호출
|
|
const handleFeatureSelect = useCallback((data) => {
|
|
gisMap = mapInstanceRef.current;
|
|
|
|
if (!gisMap) {
|
|
console.warn('지도가 초기화되지 않았습니다.');
|
|
return;
|
|
}
|
|
|
|
// 항상 같은 select 객체 사용
|
|
const select = GisApp.Module.select;
|
|
if (!select) return;
|
|
|
|
// 1. 기존 선택 초기화
|
|
select.resetSelectedFeature();
|
|
|
|
// 2. data가 OpenLayers Feature 인스턴스인지 확인
|
|
let foundFeature = null;
|
|
if (data && typeof data.getGeometry === 'function' && typeof data.setStyle === 'function') {
|
|
// 이미 Feature 인스턴스라면 그대로 사용
|
|
foundFeature = data;
|
|
} else {
|
|
// 아니면 featureId로 맵에서 Feature 인스턴스를 찾아서 사용
|
|
const featureId = data.fid || data.ftrIdn || data.id;
|
|
gisMap.getLayers().getArray().forEach(layer => {
|
|
const source = layer.getSource?.();
|
|
if (source?.getFeatureById) {
|
|
const feature = source.getFeatureById(String(featureId).trim());
|
|
if (feature) foundFeature = feature;
|
|
}
|
|
});
|
|
}
|
|
|
|
// 3. 피처 선택
|
|
if (foundFeature) {
|
|
let geometry = foundFeature.getGeometry();
|
|
if (geometry) {
|
|
let extent = geometry.getExtent();
|
|
|
|
gisMap.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);
|
|
}
|
|
|
|
setFeatureData(data);
|
|
setIsOpen(true);
|
|
setIsSearchModalOpen(false);
|
|
}, [mapInstanceRef]);
|
|
|
|
useEffect(() => {
|
|
if (!mapRef.current) return;
|
|
if (mapInstanceRef.current) return; // 이미 생성된 맵이 있다면 중단
|
|
|
|
// EPSG:5179 등록
|
|
proj4.defs([
|
|
[
|
|
'EPSG:5179',
|
|
'+proj=tmerc +lat_0=38 +lon_0=127.5 +k=0.9996 +x_0=1000000 +y_0=2000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs',
|
|
],
|
|
]);
|
|
register(proj4);
|
|
|
|
//맵 생성
|
|
const map = new OlMap({
|
|
layers: [gisCore.baseLayers['base'], gisCore.baseLayers['satellite']],
|
|
renderer: 'webgl',
|
|
target: mapRef.current,
|
|
interactions: defaults({ doubleClickZoom: false }),
|
|
controls: defaultControls({
|
|
zoom: false,
|
|
rotate: false,
|
|
attributionOptions: {
|
|
collapsible: false,
|
|
},
|
|
}).extend([]),
|
|
view: new View({
|
|
projection: GisApp.TO_PROJ,
|
|
zoom: gisCore.defaults.zoom,
|
|
center: olProj.transform(
|
|
[126.6757, 37.5585],
|
|
GisApp.FROM_PROJ,
|
|
GisApp.TO_PROJ
|
|
),
|
|
minZoom: gisCore.defaults.minZoom,
|
|
maxZoom: gisCore.defaults.maxZoom,
|
|
}),
|
|
});
|
|
|
|
gisMaps.current = map
|
|
|
|
//미니맵
|
|
if(isMiniMap){
|
|
const overviewMap = new OverviewMap({
|
|
layers: [GisApp.Module.core.baseMiniMapLayers['base']],
|
|
view: new View({
|
|
projection: GisApp.TO_PROJ,
|
|
minZoom: 9,
|
|
}),
|
|
collapsed: false,
|
|
collapseLabel: '\u00BB',
|
|
label: '\u00AB',
|
|
});
|
|
map.addControl(overviewMap);
|
|
}
|
|
|
|
gisCore.__proto__.map = map;
|
|
gisCore.scaleControl();
|
|
gisCore.changeBaseLayer('base');
|
|
setMapObject({ map: map });
|
|
mapInstanceRef.current = map; // 생성 완료 플래그 설정
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!treeData || treeData.length === 0) return;
|
|
|
|
gisCore.deleteAllOverlay();
|
|
gisCore.deleteAllLayer();
|
|
|
|
const layerCode = gisCore.convertLayerInfosToLayerCode(treeData, 'lyrSn');
|
|
GisApp.layerCode = layerCode;
|
|
|
|
//setTreeData(data);
|
|
const layerItems = getLayerItems(layerCode);
|
|
//setEditLayerItems(prev => [...layerItems])
|
|
}, [treeData]);
|
|
|
|
const getLayerItems = (layerCode) => {
|
|
const layerItems = Object.entries(layerCode)
|
|
.filter(([key, obj], idx) => obj?.updtPsblYn === 'Y')
|
|
.map(([key, obj]) => ({
|
|
...obj,
|
|
name: obj.lyrNm,
|
|
label: obj.lyrNm,
|
|
value: obj.lyrSn + '',
|
|
}))
|
|
.sort((a, b) => {
|
|
if (a.sortOrdr > b.sortOrdr) return 1;
|
|
if (a.sortOrdr < b.sortOrdr) return -1;
|
|
return 0;
|
|
});
|
|
return layerItems;
|
|
};
|
|
|
|
if (mapObject.map) {
|
|
try {
|
|
gisMap = mapObject.map;
|
|
} catch (e) {
|
|
console.error('오류 발생:', e); // 시큐어코딩 관련 수정 : 오류 정보를 로그로 출력
|
|
}
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<GisContext.Provider value={{ gisCore, gisMap: mapInstanceRef.current, treeData, checkedLayerIds }}>
|
|
<div className="h-[calc(100vh-10rem)] flex relative">
|
|
<div ref={mapRef} className="gis-map flex-1 relative" style={{ height: '100%', padding: 0 }} />
|
|
<GisTree visible={tabVisible} setVisible={setTabVisible} />
|
|
|
|
{/* <GisContent onFeatureSelect={handleFeatureSelect} isSearch={isSearch} isSearchModalOpen={isSearchModalOpen} setIsSearchModalOpen={setIsSearchModalOpen} /> */}
|
|
|
|
{/* isSearchModalOpen이 true면 visible=false로 팝업 숨김 */}
|
|
{isOpen && featureData && !isSearchModalOpen && (
|
|
<DetailView
|
|
featureData={featureData}
|
|
onClose={closePopup} // 닫기 콜백 전달
|
|
/>
|
|
)}
|
|
<SearchView
|
|
open={isSearchModalOpen}
|
|
onOpenChange={setIsSearchModalOpen}
|
|
onFeatureSelect={onFeatureSelect}
|
|
/>
|
|
<GisLonLat/>
|
|
<GisToolBar/>
|
|
<GisMapToggle/>
|
|
<GisSelect onFeatureSelect={onFeatureSelect} />
|
|
</div>
|
|
|
|
{isSearch && (
|
|
<div className="absolute right-8 top-[305px] z-20">
|
|
<button
|
|
onClick={() => setIsSearchModalOpen(true)}
|
|
className="overflow-hidden w-[40px] h-[40px] bg-white
|
|
border border-[#9aa7b8]
|
|
rounded
|
|
shadow-[0_0_5px_#999]
|
|
flex items-center justify-center
|
|
p-0
|
|
"
|
|
>
|
|
<Search size={24} className="text-gray-700" />
|
|
</button>
|
|
</div>
|
|
)}
|
|
</GisContext.Provider>
|
|
</>
|
|
);
|
|
}
|
|
|
|
export default React.memo(Gis);
|