Browse Source

Merge branch 'master' of http://gitea.palntour.com/pav/pav-fe-kac into LAANC-QR-CODE

pull/2/head
박상현 10 months ago
parent
commit
ff76a81fa7
  1. BIN
      README.md
  2. BIN
      src/assets/images/화면구성도_다이어그램.png
  3. BIN
      src/assets/images/화면구성도다이어그램.png
  4. 3
      src/components/analysis/simulation/AnalysisSimuationInfo.js
  5. 1
      src/components/analysis/simulation/AnalysisSimulationDetail.js
  6. 3
      src/components/analysis/simulation/AnalysisSimulationMap.js
  7. 4
      src/components/analysis/simulation/AnalysisSimulationMarker.js
  8. 3
      src/components/analysis/simulation/AnalysisSimulationPolyline.js
  9. 1
      src/components/analysis/simulation/AnalysisSimulationReport.js
  10. 2
      src/components/analysis/simulation/AnalysisSimulatorSlider.js
  11. 2
      src/components/laanc/list/LaancDetail.js
  12. 5
      src/components/laanc/list/LaancGrid.js
  13. 2
      src/components/laanc/list/LaancSearch.js
  14. 237
      src/components/laanc/map/FlightArea.js
  15. 158
      src/components/laanc/map/LaancAreaMap.js
  16. 73
      src/components/laanc/map/LaancComn.js
  17. 64
      src/components/laanc/map/LaancDrawControl.js
  18. 1
      src/components/laanc/map/LaancDrawModal.js
  19. 8
      src/components/laanc/map/LaancMapSearch.js
  20. 17
      src/components/laanc/step/LaacnStep3.js
  21. 14
      src/components/laanc/step/LaancStep1.js
  22. 6
      src/components/laanc/step/LaancStep2.js
  23. 35
      src/containers/analysis/simulator/AnalysisSimulationContainer.js
  24. 13
      src/containers/laanc/LaancContainer.js
  25. 314
      src/containers/laanc/LaancPlanContainer.js
  26. 34
      src/utility/DrawUtil.js
  27. 28
      src/views/control/alarm/ControlAlarmDetail.js
  28. 114
      src/views/control/alarm/ControlAlarmList.js
  29. 45
      src/views/control/alarm/ControlAlarmNotice.js
  30. 158
      src/views/control/main/ControlMain.js
  31. 83
      src/views/control/report/ControlReportDetail.js
  32. 45
      src/views/control/report/ControlReportList.js
  33. 113
      src/views/control/setting/ControlSetting.js

BIN
README.md

Binary file not shown.

BIN
src/assets/images/화면구성도_다이어그램.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

BIN
src/assets/images/화면구성도다이어그램.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

3
src/components/analysis/simulation/AnalysisSimuationInfo.js

@ -6,8 +6,10 @@ import { Spinner } from 'reactstrap';
export const AnalysisSimulationInfo = props => { export const AnalysisSimulationInfo = props => {
const [target, setTarget] = useState(null); const [target, setTarget] = useState(null);
// 로딩 상태
const { loading } = useSelector(state => state.loadingReducer); const { loading } = useSelector(state => state.loadingReducer);
// 스크롤 끝 감지 데이터 추가
const onIntersect = useCallback( const onIntersect = useCallback(
([entry], observer) => { ([entry], observer) => {
if (entry.isIntersecting) { if (entry.isIntersecting) {
@ -19,6 +21,7 @@ export const AnalysisSimulationInfo = props => {
[props.handlerPageList] [props.handlerPageList]
); );
// 무한 스크롤
useEffect(() => { useEffect(() => {
let observer; let observer;
if (target) { if (target) {

1
src/components/analysis/simulation/AnalysisSimulationDetail.js

@ -11,6 +11,7 @@ import { ReactComponent as Simulation_icon } from '../../../assets/images/simula
import { IMG_PATH } from '../../../configs/constants'; import { IMG_PATH } from '../../../configs/constants';
import AnalysisSimulatorSlider from './AnalysisSimulatorSlider'; import AnalysisSimulatorSlider from './AnalysisSimulatorSlider';
export const AnalysisSimulationDetail = props => { export const AnalysisSimulationDetail = props => {
// 슬라이드 진행 방향
const [isRtl, setIsRtl] = useRTL(); const [isRtl, setIsRtl] = useRTL();
return ( return (

3
src/components/analysis/simulation/AnalysisSimulationMap.js

@ -9,12 +9,15 @@ import { Threebox } from 'threebox-plugin';
import gimPo from '../../map/geojson/gimpoAirportAirArea.json'; import gimPo from '../../map/geojson/gimpoAirportAirArea.json';
export const AnalysisSimulationMap = props => { export const AnalysisSimulationMap = props => {
// 지도
const mapContainer = useRef(null); const mapContainer = useRef(null);
// mabboxgl 지도 초기화
useEffect(() => { useEffect(() => {
mapBoxMapInit(); mapBoxMapInit();
}, []); }, []);
// mabboxgl 지도 초기화 함수
const mapBoxMapInit = () => { const mapBoxMapInit = () => {
mapboxgl.accessToken = MAPBOX_TOKEN; mapboxgl.accessToken = MAPBOX_TOKEN;

4
src/components/analysis/simulation/AnalysisSimulationMarker.js

@ -3,6 +3,7 @@ import DronIcon from '../../../assets/images/drone-marker-icon.png';
import mapboxgl from 'mapbox-gl'; import mapboxgl from 'mapbox-gl';
export const AnalysisSimulationMarker = props => { export const AnalysisSimulationMarker = props => {
// 마커 css
const el = document.createElement('div'); const el = document.createElement('div');
el.className = 'marker'; el.className = 'marker';
el.style.width = '30px'; el.style.width = '30px';
@ -10,6 +11,7 @@ export const AnalysisSimulationMarker = props => {
el.style.textAlign = 'center'; el.style.textAlign = 'center';
el.style.backgroundImage = `url(${DronIcon})`; el.style.backgroundImage = `url(${DronIcon})`;
// 마커 경로 담기
useEffect(() => { useEffect(() => {
if (props.selMarker && props.selMarker.setMap) { if (props.selMarker && props.selMarker.setMap) {
props.selMarker.setMap(null); props.selMarker.setMap(null);
@ -30,6 +32,7 @@ export const AnalysisSimulationMarker = props => {
} }
}, [props.data]); }, [props.data]);
// 매 초마다 경로 이동
useEffect(() => { useEffect(() => {
if (props.isPlay) { if (props.isPlay) {
if (props.marker) { if (props.marker) {
@ -47,6 +50,7 @@ export const AnalysisSimulationMarker = props => {
} }
}, [props.info]); }, [props.info]);
// 지도 드론 표출
const addMarkers = (position, id) => { const addMarkers = (position, id) => {
// 이미 지정된 마커 제거 // 이미 지정된 마커 제거
if (props.marker) { if (props.marker) {

3
src/components/analysis/simulation/AnalysisSimulationPolyline.js

@ -1,8 +1,10 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
export const AnalysisSimulationPolyline = props => { export const AnalysisSimulationPolyline = props => {
// 폴리라인 경로 담는 변수
const polylinePath = []; const polylinePath = [];
// 기존 저장된 경로 삭제
useEffect(() => { useEffect(() => {
// 기존 폴리라인 삭제 처리 // 기존 폴리라인 삭제 처리
if (props.selPolyline && props.selPolyline.setMap) { if (props.selPolyline && props.selPolyline.setMap) {
@ -19,6 +21,7 @@ export const AnalysisSimulationPolyline = props => {
} }
}, [props.data]); }, [props.data]);
// 경로 그리기
const addPolyline = () => { const addPolyline = () => {
if (props.data && props.map) { if (props.data && props.map) {
props.data.forEach(item => { props.data.forEach(item => {

1
src/components/analysis/simulation/AnalysisSimulationReport.js

@ -5,6 +5,7 @@ import Flatpickr from 'react-flatpickr';
import { Button, Input, InputGroup } from 'reactstrap'; import { Button, Input, InputGroup } from 'reactstrap';
export const AnalysisSimulationReport = props => { export const AnalysisSimulationReport = props => {
// 식별번호
const [filterId, setFilterId] = useState(''); const [filterId, setFilterId] = useState('');
return ( return (

2
src/components/analysis/simulation/AnalysisSimulatorSlider.js

@ -24,6 +24,7 @@ const AnalysisSimulatorSlider = ({
return timeString; return timeString;
} }
// 슬라이더 값이 바뀔 때마다 실행되는 함수
const colorOptions = { const colorOptions = {
start: [playCount ? playCount : 0], start: [playCount ? playCount : 0],
// connect: true, // connect: true,
@ -64,7 +65,6 @@ const AnalysisSimulatorSlider = ({
direction direction
}; };
useEffect(() => {}, [playCount]);
return ( return (
<div className='simulation-slider'> <div className='simulation-slider'>
{/* <h5 className='my-2'>Default / Primary Color Slider</h5> */} {/* <h5 className='my-2'>Default / Primary Color Slider</h5> */}

2
src/components/laanc/list/LaancDetail.js

@ -30,7 +30,9 @@ export default function LaancDetail({ data, handlerLaancClose }) {
11: '25kg초과' 11: '25kg초과'
} }
}; };
// 로그인 정보
const { user } = useSelector(state => state.authState); const { user } = useSelector(state => state.authState);
// 약관 정보
const { termsList } = useSelector(state => state.accountState); const { termsList } = useSelector(state => state.accountState);
// Laanc 약관 동의 // Laanc 약관 동의

5
src/components/laanc/list/LaancGrid.js

@ -18,11 +18,13 @@ pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/$
export default function LaancGrid() { export default function LaancGrid() {
const dispatch = useDispatch(); const dispatch = useDispatch();
// 상세보기 모달
const [isAnimation, setIsAnimation] = useState(false); const [isAnimation, setIsAnimation] = useState(false);
// Laanc 승인 신청 목록
const { laancSearchData, laancDetail } = useSelector( const { laancSearchData, laancDetail } = useSelector(
state => state.laancState state => state.laancState
); );
// 로딩 상태
const { loading } = useSelector(state => state.loadingReducer); const { loading } = useSelector(state => state.loadingReducer);
// Laanc 승인 신청 목록 조회 // Laanc 승인 신청 목록 조회
@ -169,6 +171,7 @@ export default function LaancGrid() {
} }
]; ];
// PDF 다운로드
const handlerPdfDownload = pdf => { const handlerPdfDownload = pdf => {
if (pdf) { if (pdf) {
let alink = document.createElement('a'); let alink = document.createElement('a');

2
src/components/laanc/list/LaancSearch.js

@ -8,7 +8,7 @@ import * as LaancAction from '../../../modules/laanc/actions/laancActions';
function LaancSearch({ isSearch }) { function LaancSearch({ isSearch }) {
const dispatch = useDispatch(); const dispatch = useDispatch();
// 날짜 데이터
const [date, setDate] = useState({ const [date, setDate] = useState({
createStDate: moment().subtract(0, 'day').format('YYYY-MM-DD'), createStDate: moment().subtract(0, 'day').format('YYYY-MM-DD'),
createEndDate: moment().subtract(-7, 'day').format('YYYY-MM-DD') createEndDate: moment().subtract(-7, 'day').format('YYYY-MM-DD')

237
src/components/laanc/map/FlightArea.js

@ -1,6 +1,9 @@
import { useEffect, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
// mapbox
import 'mapbox-gl/dist/mapbox-gl.css'; import 'mapbox-gl/dist/mapbox-gl.css';
import mapboxgl from 'mapbox-gl'; import mapboxgl from 'mapbox-gl';
import threebox from 'threebox-plugin';
import MapboxLanguage from '@mapbox/mapbox-gl-language'; import MapboxLanguage from '@mapbox/mapbox-gl-language';
import MapboxDraw from '@mapbox/mapbox-gl-draw'; import MapboxDraw from '@mapbox/mapbox-gl-draw';
import { import {
@ -10,18 +13,6 @@ import {
SimpleSelectMode SimpleSelectMode
} from 'mapbox-gl-draw-circle'; } from 'mapbox-gl-draw-circle';
import { MAPBOX_TOKEN } from '../../../configs/constants'; import { MAPBOX_TOKEN } from '../../../configs/constants';
import { useEffect, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import {
AREA_COORDINATE_LIST_SAVE,
AREA_DETAIL_LIST_SAVE
} from '../../../modules/basis/flight/actions/basisFlightAction';
import {
drawTypeChangeAction,
mapInitAction
} from '../../../modules/control/map/actions/controlMapActions';
import LaancAreaMap from './LaancAreaMap';
import { import {
InitFeature, InitFeature,
handlerCreatePoint, handlerCreatePoint,
@ -32,20 +23,26 @@ import {
layerPolyline, layerPolyline,
layerWayPoint layerWayPoint
} from '../../../utility/DrawUtil'; } from '../../../utility/DrawUtil';
import flatGimpo from '../../map/geojson/flatGimpoAirportAirArea.json'; // actions
import {
AREA_COORDINATE_LIST_SAVE,
AREA_DETAIL_LIST_SAVE
} from '../../../modules/basis/flight/actions/basisFlightAction';
import {
drawTypeChangeAction,
mapInitAction
} from '../../../modules/control/map/actions/controlMapActions';
import * as LaancAction from '../../../modules/laanc/actions/laancActions';
// geojson
import gimpo from '../../map/geojson/gimpoAirportAirArea.json'; import gimpo from '../../map/geojson/gimpoAirportAirArea.json';
import geoJson from '../../map/geojson/airArea.json';
import threebox from 'threebox-plugin';
import { FeatureAirZone } from '../../map/mapbox/feature/FeatureAirZone'; import { FeatureAirZone } from '../../map/mapbox/feature/FeatureAirZone';
import { WeatherContainer } from '../../../containers/basis/flight/plan/WeatherContainer'; import { WeatherContainer } from '../../../containers/basis/flight/plan/WeatherContainer';
import { initFlightBas } from '../../../modules/laanc/models/laancModels'; import { initFlightBas } from '../../../modules/laanc/models/laancModels';
import * as LaancAction from '../../../modules/laanc/actions/laancActions'; import LaancAreaMap from './LaancAreaMap';
import LaancDrawModal from './LaancDrawModal'; import LaancDrawModal from './LaancDrawModal';
import { handlerCreateAirSpace } from './LaancComn';
const initialAddData = {
isAddable: false,
isViewAdd: false,
overAdd: false
};
export default function FlightArea({ export default function FlightArea({
centeredModal, centeredModal,
@ -61,40 +58,65 @@ export default function FlightArea({
></script>; ></script>;
const dispatch = useDispatch(); const dispatch = useDispatch();
const { areaCoordList } = useSelector(state => state.flightState); // 비행구역 타입 및 공역 타입
const mapControl = useSelector(state => state.controlMapReducer); const mapControl = useSelector(state => state.controlMapReducer);
const mapContainer = useRef(null); // 비행구역 정보 저장
const { areaCoordList } = useSelector(state => state.flightState);
// 지도
const [mapObject, setMapObject] = useState(); const [mapObject, setMapObject] = useState();
const [drawObj, setDrawObj] = useState(); const mapContainer = useRef(null);
// 지도 로드 여부
const [isMapLoad, setIsMapLoad] = useState(false); const [isMapLoad, setIsMapLoad] = useState(false);
// 비행구역 그리기
const [drawObj, setDrawObj] = useState();
// 미니맵 레이어
const [previewLayer, setPreviewLayer] = useState(); const [previewLayer, setPreviewLayer] = useState();
// 날씨 모달
const [formModal, setFormModal] = useState(false); const [formModal, setFormModal] = useState(false);
// 비행구역 설정 관련 모달
const [modal, setModal] = useState({ const [modal, setModal] = useState({
title: '', title: '',
desc: '', desc: '',
isOpen: false isOpen: false
}); });
// 비행구역 저장 가능 여부
const [isSaveable, setIsSaveable] = useState(false); const [isSaveable, setIsSaveable] = useState(false);
const [addData, setAddData] = useState(initialAddData);
// 비행구역 추가 가능 여부 판단
const [addData, setAddData] = useState({
isAddable: false,
isViewAdd: false,
overAdd: false
});
// 저장된 비행구역 데이터
const [saveData, setSaveData] = useState(); const [saveData, setSaveData] = useState();
// 비행구역 고도
const [saveElev, setSaveElev] = useState(); const [saveElev, setSaveElev] = useState();
//날씨 임시 데이터 //날씨 위치 데이터
const [wheather, setWheather] = useState([]); const [wheather, setWheather] = useState([]);
// 미니맵에 표출되는 비행구역 정보
const previewGeo = { const previewGeo = {
type: 'FeatureCollection', type: 'FeatureCollection',
features: [] features: []
}; };
// 지도 초기 셋팅
useEffect(() => { useEffect(() => {
handlerMapInit(); handlerMapInit();
}, []); }, []);
// 미니맵에 비행구역 표출 및 날씨 정보 저장
useEffect(() => { useEffect(() => {
if (areaCoordList) { if (areaCoordList) {
const area = areaCoordList[0]; const area = areaCoordList[0];
@ -105,78 +127,12 @@ export default function FlightArea({
} }
}, [areaCoordList, centeredModal, previewLayer]); }, [areaCoordList, centeredModal, previewLayer]);
const handlerCreateAirSpace = ( // 비행구역 설정 관련 모달 표출
map,
useGeoJson = {
...geoJson,
...flatGimpo,
features: [...geoJson.features, ...flatGimpo.features]
}
) => {
if (map.getLayer('maine')) {
map.removeLayer('maine');
map.removeSource('maine');
}
let arrGeoJson = [];
useGeoJson.features.map(item => {
if (item.properties.type === '0001' && mapControl.area0001) {
arrGeoJson.push({
...item,
properties: { ...item.properties, color: '#FF3648' }
});
} else if (item.properties.type === '0002' && mapControl.area0002) {
arrGeoJson.push({
...item,
properties: { ...item.properties, color: '#FFA1AA' }
});
} else if (item.properties.type === '0003' && mapControl.area0003) {
arrGeoJson.push({
...item,
properties: { ...item.properties, color: '#FFA800' }
});
} else if (item.properties.type === '0004' && mapControl.area0004) {
arrGeoJson.push({
...item,
properties: { ...item.properties, color: '#A16B00' }
});
} else if (item.properties.type === '0005' && mapControl.area0005) {
arrGeoJson.push({
...item,
properties: { ...item.properties, color: '#AB40FF' }
});
} else if (item.properties.type === '0006' && mapControl.area0006) {
arrGeoJson.push({
...item,
properties: { ...item.properties, color: '#009cad' }
});
}
});
useGeoJson.features = arrGeoJson.filter(i => i.geometry.type === 'Polygon');
// 공역 생성 start
map.addSource('maine', {
type: 'geojson',
data: {
...useGeoJson
}
});
map.addLayer({
id: 'maine',
type: 'fill',
source: 'maine',
layout: {},
paint: {
'fill-color': ['get', 'color'],
// 'fill-extrusion-height': 3000,
'fill-opacity': 0.5
}
});
};
const handlerModal = () => { const handlerModal = () => {
setModal(!modal); setModal(!modal);
}; };
// 비행구역 타입 변경 시 그리기 모드 상태일 때 에러 표출
const handlerDrawType = val => { const handlerDrawType = val => {
if (drawObj.getMode().includes('draw')) { if (drawObj.getMode().includes('draw')) {
setModal({ setModal({
@ -195,6 +151,7 @@ export default function FlightArea({
} }
}; };
// laanc계획서 비행구역 저장버튼 클릭 시 비행구역 정보 저장
const handlerSave = async () => { const handlerSave = async () => {
if (areaCoordList) { if (areaCoordList) {
console.log('save'); console.log('save');
@ -208,29 +165,28 @@ export default function FlightArea({
setCenteredModal(false); setCenteredModal(false);
dispatch(AREA_DETAIL_LIST_SAVE(resultAreaDetail)); dispatch(AREA_DETAIL_LIST_SAVE(resultAreaDetail));
} else {
alert('아무것도 작성 안함');
} }
}; };
// 날씨 handler // 날씨 모달 표출
const handlerWeather = () => { const handlerWeather = () => {
setFormModal(!formModal); setFormModal(!formModal);
}; };
// 지도 초기 셋팅
const handlerMapInit = () => { const handlerMapInit = () => {
mapboxgl.accessToken = MAPBOX_TOKEN; mapboxgl.accessToken = MAPBOX_TOKEN;
const map = new mapboxgl.Map({ const map = new mapboxgl.Map({
container: 'preview', // container ID container: 'preview',
style: 'mapbox://styles/mapbox/streets-v12', // style URL style: 'mapbox://styles/mapbox/streets-v12',
center: [126.612647, 37.519893], // starting position [lng, lat] center: [126.612647, 37.519893],
// zoom: !areaCoordList ? 14 : bufferZoom.bufferzoom, // starting zoom
zoom: 15, zoom: 15,
antialias: true, antialias: true,
attributionControl: false attributionControl: false
}); });
// 비행구역 상세맵 draw 정보 셋팅
const draw = new MapboxDraw({ const draw = new MapboxDraw({
displayControlsDefault: false, displayControlsDefault: false,
userProperties: true, userProperties: true,
@ -243,16 +199,14 @@ export default function FlightArea({
simple_select: SimpleSelectMode simple_select: SimpleSelectMode
}, },
styles: [ styles: [
// line stroke
{ {
// polyline
id: 'gl-draw-line', id: 'gl-draw-line',
type: 'line', type: 'line',
filter: [ filter: [
'all', 'all',
['==', '$type', 'LineString'], ['==', '$type', 'LineString'],
['!=', 'mode', 'static'] ['!=', 'mode', 'static']
// ['==', 'meta', 'feature'],
// ['==', 'active', 'false']
], ],
layout: { layout: {
'line-cap': 'round', 'line-cap': 'round',
@ -264,28 +218,8 @@ export default function FlightArea({
'line-width': 2 'line-width': 2
} }
}, },
// direct line stroke
// {
// id: 'gl-draw-line-active',
// type: 'line',
// filter: [
// 'all',
// ['==', '$type', 'LineString'],
// ['==', 'meta', 'feature'],
// ['==', 'active', 'true']
// ],
// layout: {
// 'line-cap': 'round',
// 'line-join': 'round'
// },
// paint: {
// 'line-color': '#000000',
// 'line-dasharray': [0.2, 2],
// 'line-width': 2
// }
// },
// polygon fill
{ {
// polygon fill
id: 'gl-draw-polygon-fill', id: 'gl-draw-polygon-fill',
type: 'fill', type: 'fill',
filter: ['all', ['==', '$type', 'Polygon'], ['!=', 'mode', 'static']], filter: ['all', ['==', '$type', 'Polygon'], ['!=', 'mode', 'static']],
@ -295,18 +229,7 @@ export default function FlightArea({
'fill-opacity': 0.1 'fill-opacity': 0.1
} }
}, },
// polygon mid points // polygon outline
{
id: 'gl-draw-polygon-midpoint',
type: 'circle',
filter: ['all', ['==', '$type', 'Point'], ['==', 'meta', 'midpoint']],
paint: {
'circle-radius': 4,
'circle-color': '#8a1c05'
}
},
// polygon outline stroke
// This doesn't style the first edge of the polygon, which uses the line stroke styling instead
{ {
id: 'gl-draw-polygon-stroke-active', id: 'gl-draw-polygon-stroke-active',
type: 'line', type: 'line',
@ -321,8 +244,8 @@ export default function FlightArea({
'line-width': 2 'line-width': 2
} }
}, },
// vertex point halos
{ {
// vertex point halos
id: 'gl-draw-polygon-and-line-vertex-halo-active', id: 'gl-draw-polygon-and-line-vertex-halo-active',
type: 'circle', type: 'circle',
filter: [ filter: [
@ -333,11 +256,11 @@ export default function FlightArea({
], ],
paint: { paint: {
'circle-radius': 8, 'circle-radius': 8,
'circle-color': '#fff' 'circle-color': '#ffffff'
} }
}, },
// vertex points
{ {
// vertex points
id: 'gl-draw-polygon-and-line-vertex-active', id: 'gl-draw-polygon-and-line-vertex-active',
type: 'circle', type: 'circle',
filter: [ filter: [
@ -359,7 +282,6 @@ export default function FlightArea({
const language = new MapboxLanguage(); const language = new MapboxLanguage();
map.addControl(language); map.addControl(language);
// map.addControl(draw);
const tb = (window.tb = new threebox.Threebox( const tb = (window.tb = new threebox.Threebox(
map, map,
@ -396,7 +318,7 @@ export default function FlightArea({
} }
}); });
handlerCreateAirSpace(map); handlerCreateAirSpace(map, mapControl);
// 미니맵 표출 // 미니맵 표출
map.addSource('preview', { map.addSource('preview', {
@ -417,6 +339,7 @@ export default function FlightArea({
dispatch(mapInitAction(map)); dispatch(mapInitAction(map));
}; };
// 저장된 비행구역 미니맵에 표출
const handlerPreviewDraw = () => { const handlerPreviewDraw = () => {
if (areaCoordList) { if (areaCoordList) {
const areas = areaCoordList[0]; const areas = areaCoordList[0];
@ -427,7 +350,6 @@ export default function FlightArea({
let fitZoomPaths = []; let fitZoomPaths = [];
// 기존
if (areas.areaType) { if (areas.areaType) {
if (areas.areaType === 'CIRCLE') { if (areas.areaType === 'CIRCLE') {
const radius = areas.bufferZone; const radius = areas.bufferZone;
@ -478,12 +400,8 @@ export default function FlightArea({
//지도 줌 좌표 설정 //지도 줌 좌표 설정
fitZoomPaths = paths.concat(); fitZoomPaths = paths.concat();
// 마커 삭제
// const ele = document.getElementById('mapboxgl-popup');
// const eleArr = Array.from(ele);
// eleArr?.forEach(marker => marker.remove());
} }
handlerFitBounds(mapObject, fitZoomPaths, 50, areas.areaType); handlerFitBounds(mapObject, fitZoomPaths, 50, areas.areaType);
mapObject.setPaintProperty('waypoint', 'circle-radius', 10); mapObject.setPaintProperty('waypoint', 'circle-radius', 10);
@ -491,12 +409,13 @@ export default function FlightArea({
} }
const coordValue = []; const coordValue = [];
const coord = paths?.map(coords => { paths?.map(coords => {
coordValue.push({ coordValue.push({
lat: coords[1], lat: coords[1],
lon: coords[0] lon: coords[0]
}); });
}); });
if (page === 1) { if (page === 1) {
naver.maps.Service.reverseGeocode( naver.maps.Service.reverseGeocode(
{ {
@ -527,7 +446,6 @@ export default function FlightArea({
name: 'latlon', name: 'latlon',
value: coordValue value: coordValue
}); });
//스텝1에 반경도 글씨가 바뀌어야 함...!!
handleChange({ handleChange({
type: 'area', type: 'area',
name: 'bufferZone', name: 'bufferZone',
@ -537,38 +455,33 @@ export default function FlightArea({
} }
}; };
// 비행구역 추가 버튼 클릭 시
const handlerAddClick = () => { const handlerAddClick = () => {
if (!addData.isAddable || !addData.overAdd) { if (!addData.isAddable || !addData.overAdd) {
handlerAddChange('isAddable', true); handlerAddChange('isAddable', true);
const obj = drawObj
.getAll()
.features.filter(obj => obj.properties.id !== 'BUFFER');
// handlerDrawType(obj[0].properties.id);
} }
}; };
// 비행구역 추가 관련 상태 변경
const handlerAddChange = (key, val) => { const handlerAddChange = (key, val) => {
// const [addData, setAddData] = useState({
// isAddalbe: false,
// isViewAdd: false,
// overAdd: false
// })
setAddData(prev => ({ setAddData(prev => ({
...prev, ...prev,
[key]: val [key]: val
})); }));
}; };
// 비행구역 저장 가능 유무 체크
const handlerSaveCheck = save => { const handlerSaveCheck = save => {
setIsSaveable(save); setIsSaveable(save);
}; };
// 비행구역 데이터 초기화
const handlerInitCoordinates = () => { const handlerInitCoordinates = () => {
const init = initFlightBas.initDetail.areaList.concat(); const init = initFlightBas.initDetail.areaList.concat();
dispatch(AREA_COORDINATE_LIST_SAVE(init)); dispatch(AREA_COORDINATE_LIST_SAVE(init));
}; };
// 비행구역 고도 저장
const handlerSaveElev = elev => { const handlerSaveElev = elev => {
setSaveElev(elev); setSaveElev(elev);
}; };
@ -604,7 +517,6 @@ export default function FlightArea({
</ModalHeader> </ModalHeader>
<ModalBody> <ModalBody>
<LaancAreaMap <LaancAreaMap
centeredModal={centeredModal}
mapContainer={mapContainer} mapContainer={mapContainer}
drawObj={drawObj} drawObj={drawObj}
handlerInitCoordinates={handlerInitCoordinates} handlerInitCoordinates={handlerInitCoordinates}
@ -684,7 +596,6 @@ export default function FlightArea({
dispatch(LaancAction.LAANC_ALTITUDE.success(saveElev)); dispatch(LaancAction.LAANC_ALTITUDE.success(saveElev));
}} }}
> >
{/* 닫기 */}
저장 저장
</Button> </Button>
</div> </div>

158
src/components/laanc/map/LaancAreaMap.js

@ -3,7 +3,7 @@ import mapboxgl from 'mapbox-gl';
import threebox from 'threebox-plugin'; import threebox from 'threebox-plugin';
import MapboxLanguage from '@mapbox/mapbox-gl-language'; import MapboxLanguage from '@mapbox/mapbox-gl-language';
import { MAPBOX_TOKEN } from '../../../configs/constants'; import { MAPBOX_TOKEN } from '../../../configs/constants';
import { useEffect, useMemo, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { Card, CardBody } from 'reactstrap'; import { Card, CardBody } from 'reactstrap';
import { initFlightBas } from '../../../modules/basis/flight/models/basisFlightModel'; import { initFlightBas } from '../../../modules/basis/flight/models/basisFlightModel';
@ -16,22 +16,15 @@ import { mapInitAction } from '../../../modules/control/map/actions/controlMapAc
import { import {
FormattingCoord, FormattingCoord,
handlerFitBounds, handlerFitBounds,
handlerGetCircleCoord, handlerGetCircleCoord
layerBuffer,
layerGuideLine,
layerPolygon,
layerPolyline,
layerWayPoint
} from '../../../utility/DrawUtil'; } from '../../../utility/DrawUtil';
import flatGimpo from '../../map/geojson/flatGimpoAirportAirArea.json';
import gimpo from '../../map/geojson/gimpoAirportAirArea.json'; import gimpo from '../../map/geojson/gimpoAirportAirArea.json';
import geoJson from '../../map/geojson/airArea.json';
import { FeatureAirZone } from '../../map/mapbox/feature/FeatureAirZone'; import { FeatureAirZone } from '../../map/mapbox/feature/FeatureAirZone';
import LaancMapSearch from './LaancMapSearch'; import LaancMapSearch from './LaancMapSearch';
import { LaancDrawControl } from './LaancDrawControl'; import { LaancDrawControl } from './LaancDrawControl';
import { handlerCreateAirSpace } from './LaancComn';
export default function LaancAreaMap({ export default function LaancAreaMap({
centeredModal,
mapContainer, mapContainer,
drawObj, drawObj,
handlerSaveCheck, handlerSaveCheck,
@ -43,42 +36,38 @@ export default function LaancAreaMap({
setModal setModal
}) { }) {
const dispatch = useDispatch(); const dispatch = useDispatch();
// 비행구역 타입 및 공역 타입
const mapControl = useSelector(state => state.controlMapReducer); const mapControl = useSelector(state => state.controlMapReducer);
const { areaCoordList } = useSelector(state => state.flightState);
const [mapObject, setMapObject] = useState(); // 비행구역 정보 저장
const [isMapLoad, setIsMapLoad] = useState(false); const { areaCoordList } = useSelector(state => state.flightState);
const [mode, setMode] = useState(); // 비행구역 초기값 포함 정보 저장
const [mapAreaCoordList, setMapAreaCoordList] = useState( const [mapAreaCoordList, setMapAreaCoordList] = useState(
initFlightBas.initDetail.areaList initFlightBas.initDetail.areaList
); );
const [number, setNumber] = useState(0); // 지도
const [mapObject, setMapObject] = useState();
// 지도 로드 여부
const [isMapLoad, setIsMapLoad] = useState(false);
// const [detailLayer, setDetailLayer] = useState(); // 지도 렌더 횟수
const [number, setNumber] = useState(0);
// 비행구역 좌표 카드에 표출될 좌표 정보
const [viewCoordObj, setViewCoordObj] = useState([]); const [viewCoordObj, setViewCoordObj] = useState([]);
const detailGeo = useMemo(() => {
return {
type: 'FeatureCollection',
features: []
};
}, []);
// 좌표 정보 마우스 드래그 // 좌표 정보 마우스 드래그
const scrollRef = useRef(null); const scrollRef = useRef(null);
const [isDrag, setIsDrag] = useState(false); const [isDrag, setIsDrag] = useState(false);
const [startX, setStartX] = useState(); const [startX, setStartX] = useState();
// 지도 초기 셋팅
useEffect(() => { useEffect(() => {
handlerMapInit(); handlerMapInit();
}, []); }, []);
useEffect(() => { // 첫 비행구역 생성 or 저장했던 비행구역 다시 열기 시 비행구역에 화면 맞추어서 zoom
setMode(mapControl.drawType);
}, [mapControl.drawType]);
useEffect(() => { useEffect(() => {
if (areaCoordList && mapObject) { if (areaCoordList && mapObject) {
if ( if (
@ -111,75 +100,7 @@ export default function LaancAreaMap({
} }
}, [areaCoordList, mapObject, number]); }, [areaCoordList, mapObject, number]);
// 공역 생성 // 맵박스 지도 초기 셋팅
const handlerCreateAirSpace = (
map,
useGeoJson = {
...geoJson,
...flatGimpo,
features: [...geoJson.features, ...flatGimpo.features]
}
) => {
if (map.getLayer('maine')) {
map.removeLayer('maine');
map.removeSource('maine');
}
let arrGeoJson = [];
useGeoJson.features.map(item => {
if (item.properties.type === '0001' && mapControl.area0001) {
arrGeoJson.push({
...item,
properties: { ...item.properties, color: '#FF3648' }
});
} else if (item.properties.type === '0002' && mapControl.area0002) {
arrGeoJson.push({
...item,
properties: { ...item.properties, color: '#FFA1AA' }
});
} else if (item.properties.type === '0003' && mapControl.area0003) {
arrGeoJson.push({
...item,
properties: { ...item.properties, color: '#FFA800' }
});
} else if (item.properties.type === '0004' && mapControl.area0004) {
arrGeoJson.push({
...item,
properties: { ...item.properties, color: '#A16B00' }
});
} else if (item.properties.type === '0005' && mapControl.area0005) {
arrGeoJson.push({
...item,
properties: { ...item.properties, color: '#AB40FF' }
});
} else if (item.properties.type === '0006' && mapControl.area0006) {
arrGeoJson.push({
...item,
properties: { ...item.properties, color: '#009cad' }
});
}
});
useGeoJson.features = arrGeoJson.filter(i => i.geometry.type === 'Polygon');
// 공역 생성 start
map.addSource('maine', {
type: 'geojson',
data: {
...useGeoJson
}
});
map.addLayer({
id: 'maine',
type: 'fill',
source: 'maine',
layout: {},
paint: {
'fill-color': ['get', 'color'],
// 'fill-extrusion-height': 3000,
'fill-opacity': 0.5
}
});
};
const handlerMapInit = () => { const handlerMapInit = () => {
mapboxgl.accessToken = MAPBOX_TOKEN; mapboxgl.accessToken = MAPBOX_TOKEN;
@ -187,7 +108,6 @@ export default function LaancAreaMap({
container: 'detail', // container ID container: 'detail', // container ID
style: 'mapbox://styles/mapbox/streets-v12', // style URL style: 'mapbox://styles/mapbox/streets-v12', // style URL
center: [126.612647, 37.519893], // starting position [lng, lat] center: [126.612647, 37.519893], // starting position [lng, lat]
// zoom: !areaCoordList ? 14 : bufferZoom.bufferzoom, // starting zoom
zoom: 15, zoom: 15,
antialias: true, antialias: true,
attributionControl: false attributionControl: false
@ -238,31 +158,15 @@ export default function LaancAreaMap({
} }
}); });
map.addSource('detail', { handlerCreateAirSpace(map, mapControl);
type: 'geojson',
data: detailGeo
});
map.addLayer(layerWayPoint('detail'));
map.addLayer(layerGuideLine('detail'));
map.addLayer(layerPolyline('detail'));
map.addLayer(layerPolygon('detail'));
map.addLayer(layerBuffer('detail'));
handlerCreateAirSpace(map);
setIsMapLoad(true); setIsMapLoad(true);
// const detail = map.getSource('detail');
// if (detail) setDetailLayer(detail);
}); });
setMapObject(map); setMapObject(map);
dispatch(mapInitAction(map)); dispatch(mapInitAction(map));
}; };
// const handlerInitCoordinates = () => { // areaInfo를 areaList 형식으로 반환
// const init = initFlightBas.initDetail.areaList.concat();
// dispatch(AREA_COORDINATE_LIST_SAVE(init));
// };
const handlerAreaInfoToAreaList = areaInfo => { const handlerAreaInfoToAreaList = areaInfo => {
const initAreaList = initFlightBas.initDetail.areaList.concat(); const initAreaList = initFlightBas.initDetail.areaList.concat();
const coordList = []; const coordList = [];
@ -287,10 +191,10 @@ export default function LaancAreaMap({
return areaList; return areaList;
}; };
// 비행관제구역 체크 및 버퍼 생성
const handlerCoordinates = areaInfo => { const handlerCoordinates = areaInfo => {
const areaList = handlerAreaInfoToAreaList(areaInfo); const areaList = handlerAreaInfoToAreaList(areaInfo);
// dispatch(LaancAction.LAANC_ALTITUDE.request(areaList));
dispatch(LaancAction.LAANC_VALID_AREA.request(areaList)); dispatch(LaancAction.LAANC_VALID_AREA.request(areaList));
if (areaInfo.areaType === 'LINE' || areaInfo.areaType === 'POLYGON') { if (areaInfo.areaType === 'LINE' || areaInfo.areaType === 'POLYGON') {
dispatch(FLIGHT_PLAN_AREA_BUFFER_LIST.request(areaList)); dispatch(FLIGHT_PLAN_AREA_BUFFER_LIST.request(areaList));
@ -299,6 +203,7 @@ export default function LaancAreaMap({
} }
}; };
// 비행구역 설정 후 저장
const handlerConfirm = areaList => { const handlerConfirm = areaList => {
if (areaList === undefined) { if (areaList === undefined) {
alert('영역을 설정해 주세요.'); alert('영역을 설정해 주세요.');
@ -308,17 +213,14 @@ export default function LaancAreaMap({
dispatch(AREA_COORDINATE_LIST_SAVE(areaList)); dispatch(AREA_COORDINATE_LIST_SAVE(areaList));
}; };
// const handlerModal = () => { // [좌표 정보] 마우스 다운 시 스크롤 준비
// setModal(!modal);
// };
// 좌표 정보 마우스 드래그
const onMouseDown = e => { const onMouseDown = e => {
e.preventDefault(); e.preventDefault();
setIsDrag(true); setIsDrag(true);
setStartX(e.pageX + scrollRef.current.scrollLeft); setStartX(e.pageX + scrollRef.current.scrollLeft);
}; };
// [좌표 정보] 마우스 드래그로 스크롤 이동
const onMouseMove = e => { const onMouseMove = e => {
if (isDrag) { if (isDrag) {
const { scrollWidth, clientWidth, scrollLeft } = scrollRef.current; const { scrollWidth, clientWidth, scrollLeft } = scrollRef.current;
@ -333,6 +235,7 @@ export default function LaancAreaMap({
} }
}; };
// [좌표 정보] onMouseMove 이벤트 빈도 조절
const throttle = (func, ms) => { const throttle = (func, ms) => {
let throttled = false; let throttled = false;
return (...args) => { return (...args) => {
@ -346,11 +249,13 @@ export default function LaancAreaMap({
}; };
}; };
// [좌표 정보] 마우스 업 시 스크롤 멈춤
const onMouseUp = e => { const onMouseUp = e => {
e.preventDefault(); e.preventDefault();
setIsDrag(false); setIsDrag(false);
}; };
// [좌표 정보] 마우스 벗어날 시 스크롤 멈춤
const onMouseLeave = () => { const onMouseLeave = () => {
setIsDrag(false); setIsDrag(false);
}; };
@ -455,21 +360,20 @@ export default function LaancAreaMap({
{isMapLoad && mapObject ? ( {isMapLoad && mapObject ? (
<> <>
<LaancDrawControl <LaancDrawControl
handlerAddChange={handlerAddChange}
addData={addData} addData={addData}
drawObj={drawObj} drawObj={drawObj}
setModal={setModal} setModal={setModal}
mapboxgl={mapboxgl}
mapObject={mapObject} mapObject={mapObject}
setViewCoordObj={setViewCoordObj}
areaCoordList={mapAreaCoordList} areaCoordList={mapAreaCoordList}
setSaveData={setSaveData}
setViewCoordObj={setViewCoordObj}
handlerConfirm={handlerConfirm} handlerConfirm={handlerConfirm}
handlerSaveElev={handlerSaveElev}
handlerAddChange={handlerAddChange}
handlerSaveCheck={handlerSaveCheck} handlerSaveCheck={handlerSaveCheck}
handlerCoordinates={handlerCoordinates} handlerCoordinates={handlerCoordinates}
handlerInitCoordinates={handlerInitCoordinates} handlerInitCoordinates={handlerInitCoordinates}
setSaveData={setSaveData}
handlerAreaInfoToAreaList={handlerAreaInfoToAreaList} handlerAreaInfoToAreaList={handlerAreaInfoToAreaList}
handlerSaveElev={handlerSaveElev}
/> />
<FeatureAirZone map={mapObject} mapboxgl={mapboxgl} /> <FeatureAirZone map={mapObject} mapboxgl={mapboxgl} />
</> </>

73
src/components/laanc/map/LaancComn.js

@ -0,0 +1,73 @@
import { useSelector } from 'react-redux';
import geoJson from '../../map/geojson/airArea.json';
import flatGimpo from '../../map/geojson/flatGimpoAirportAirArea.json';
// 공역 생성
export const handlerCreateAirSpace = (
map,
mapControl,
useGeoJson = {
...geoJson,
...flatGimpo,
features: [...geoJson.features, ...flatGimpo.features]
}
) => {
if (map.getLayer('maine')) {
map.removeLayer('maine');
map.removeSource('maine');
}
let arrGeoJson = [];
useGeoJson.features.map(item => {
if (item.properties.type === '0001' && mapControl.area0001) {
arrGeoJson.push({
...item,
properties: { ...item.properties, color: '#FF3648' }
});
} else if (item.properties.type === '0002' && mapControl.area0002) {
arrGeoJson.push({
...item,
properties: { ...item.properties, color: '#FFA1AA' }
});
} else if (item.properties.type === '0003' && mapControl.area0003) {
arrGeoJson.push({
...item,
properties: { ...item.properties, color: '#FFA800' }
});
} else if (item.properties.type === '0004' && mapControl.area0004) {
arrGeoJson.push({
...item,
properties: { ...item.properties, color: '#A16B00' }
});
} else if (item.properties.type === '0005' && mapControl.area0005) {
arrGeoJson.push({
...item,
properties: { ...item.properties, color: '#AB40FF' }
});
} else if (item.properties.type === '0006' && mapControl.area0006) {
arrGeoJson.push({
...item,
properties: { ...item.properties, color: '#009cad' }
});
}
});
useGeoJson.features = arrGeoJson.filter(i => i.geometry.type === 'Polygon');
// 공역 생성 start
map.addSource('maine', {
type: 'geojson',
data: {
...useGeoJson
}
});
map.addLayer({
id: 'maine',
type: 'fill',
source: 'maine',
layout: {},
paint: {
'fill-color': ['get', 'color'],
// 'fill-extrusion-height': 3000,
'fill-opacity': 0.5
}
});
};

64
src/components/laanc/map/LaancDrawControl.js

@ -1,5 +1,4 @@
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { InfoModal } from '../../modal/InfoModal';
import { ErrorModal } from '../../modal/ErrorModal'; import { ErrorModal } from '../../modal/ErrorModal';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { import {
@ -25,36 +24,42 @@ import Constants from 'mapbox-gl-draw-circle/node_modules/@mapbox/mapbox-gl-draw
export const LaancDrawControl = props => { export const LaancDrawControl = props => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const mapControl = useSelector(state => state.controlMapReducer);
const drawObj = props.drawObj; const drawObj = props.drawObj;
// 지도
const mapObject = props.mapObject; const mapObject = props.mapObject;
// 비행구역 타입
const { drawType } = useSelector(state => state.controlMapReducer);
// mapbox 기본 onClick 함수 저장
const originClickRef = useRef(null);
// 비행구역이 WayPoint일 때 bufferId
const [bufferId, setBufferId] = useState(); const [bufferId, setBufferId] = useState();
// 그리기모드 상태인지 유무 확인
const [isDrawDone, setIsDrawDone] = useState(false); const [isDrawDone, setIsDrawDone] = useState(false);
const [alertModal, setAlertModal] = useState({ // 지도 렌더 횟수
isOpen: false, const [number, setNumber] = useState(0);
title: '',
desc: '' // 에러 모달창 정보
});
const [isErrorModal, setIsErrorModal] = useState({ const [isErrorModal, setIsErrorModal] = useState({
isOpen: false, isOpen: false,
title: '', title: '',
desc: '' desc: ''
}); });
const [number, setNumber] = useState(0); // 비행구역 타입 변경에 따른 그리기모드 셋팅
useEffect(() => { useEffect(() => {
if (mapControl.drawType === 'DONE') { if (drawType === 'DONE') {
// 구역 생성 후 바로 directMode
if (number !== 0) { if (number !== 0) {
const obj = drawObj const obj = drawObj
.getAll() .getAll()
.features.filter(o => o.properties.id !== 'BUFFER'); .features.filter(o => o.properties.id !== 'BUFFER');
// 구역 생성 후 바로 directMode
if (obj.length > 0) { if (obj.length > 0) {
drawObj.changeMode('direct_select', { drawObj.changeMode('direct_select', {
featureId: obj[obj.length - 1]?.id featureId: obj[obj.length - 1]?.id
@ -64,10 +69,9 @@ export const LaancDrawControl = props => {
} else { } else {
drawInit(); drawInit();
} }
}, [mapControl.drawType]); }, [drawType]);
const originClickRef = useRef(null);
// mapbox 기본 함수 오버라이드 및 함수 중복 등록 방지
useEffect(() => { useEffect(() => {
if (mapObject) { if (mapObject) {
const DrawLineStringMode = MapboxDraw.modes.draw_line_string; const DrawLineStringMode = MapboxDraw.modes.draw_line_string;
@ -75,6 +79,7 @@ export const LaancDrawControl = props => {
const DrawCircleMode = CircleMode; const DrawCircleMode = CircleMode;
const modeArr = [DrawLineStringMode, DrawPolygonMode, DrawCircleMode]; const modeArr = [DrawLineStringMode, DrawPolygonMode, DrawCircleMode];
// 등록 함수 제거
const cleanUp = () => { const cleanUp = () => {
modeArr.forEach(m => { modeArr.forEach(m => {
m.onStop = null; m.onStop = null;
@ -154,6 +159,7 @@ export const LaancDrawControl = props => {
setNumber(number + 1); setNumber(number + 1);
} }
// 컴포넌트 언마운트 시
return () => { return () => {
dispatch(drawTypeChangeAction('DONE')); dispatch(drawTypeChangeAction('DONE'));
mapObject.off('draw.update', handlerUpdateSetting); mapObject.off('draw.update', handlerUpdateSetting);
@ -162,10 +168,12 @@ export const LaancDrawControl = props => {
} }
}, [mapObject]); }, [mapObject]);
// 비행구역 데이터 지도에 다시 그려주기
useEffect(() => { useEffect(() => {
handlerPastDraw(); handlerPastDraw();
}, [props.areaCoordList]); }, [props.areaCoordList]);
// 비행구역 설정을 올바르게 마쳤을 때 좌표 저장
useEffect(() => { useEffect(() => {
if (isDrawDone) { if (isDrawDone) {
props.handlerConfirm(props.areaCoordList); props.handlerConfirm(props.areaCoordList);
@ -173,7 +181,7 @@ export const LaancDrawControl = props => {
} }
}, [isDrawDone]); }, [isDrawDone]);
// 클릭할 때마다 마커 찍어줌 // 클릭할 때마다 마커 표출
const handlerCustomOnClick = (state, e) => { const handlerCustomOnClick = (state, e) => {
const mode = handlerReturnMode(drawObj.getMode()); const mode = handlerReturnMode(drawObj.getMode());
const obj = state[mode?.toLowerCase()]; const obj = state[mode?.toLowerCase()];
@ -181,7 +189,7 @@ export const LaancDrawControl = props => {
if (mode && obj) { if (mode && obj) {
const feature = drawObj.get(obj.id); const feature = drawObj.get(obj.id);
if (!feature) { if (!feature) {
console.log('2222222222'); // console.log('simple_select');
drawObj.changeMode('simple_select'); drawObj.changeMode('simple_select');
return; return;
} }
@ -220,7 +228,7 @@ export const LaancDrawControl = props => {
} }
}; };
// 도형 그리기 완료 시 // 비행구역 그리기 완료 시
const handlerFinishDraw = state => { const handlerFinishDraw = state => {
if (drawObj.getAll().features.length === 0) return; if (drawObj.getAll().features.length === 0) return;
@ -293,8 +301,7 @@ export const LaancDrawControl = props => {
}); });
} }
} else { } else {
console.log('333333333'); // console.log('좌표가 찍히기도 전에 틀만 생성된 도형들 삭제');
// 좌표가 찍히기도 전에 틀만 생성된 도형들 삭제
// if (mode === 'CIRCLE') { // if (mode === 'CIRCLE') {
// const obj = state.polygon; // const obj = state.polygon;
// drawObj.delete(obj.id); // drawObj.delete(obj.id);
@ -307,7 +314,7 @@ export const LaancDrawControl = props => {
// 모든 비정상상황 체크 // 모든 비정상상황 체크
const handlerAbnormalityCheck = async data => { const handlerAbnormalityCheck = async data => {
// radius도 있음 // radius 존재함
const { coords, mode, id } = data; const { coords, mode, id } = data;
const areaInfo = handlerSettingAreaInfo(coords, mode); const areaInfo = handlerSettingAreaInfo(coords, mode);
@ -411,7 +418,7 @@ export const LaancDrawControl = props => {
} }
}; };
// 도형 수정 시 // 비행구역 수정 시
const handlerUpdateSetting = e => { const handlerUpdateSetting = e => {
if (e.features[0]) { if (e.features[0]) {
const { geometry, properties, id } = e.features[0]; const { geometry, properties, id } = e.features[0];
@ -482,7 +489,7 @@ export const LaancDrawControl = props => {
); );
props.setViewCoordObj(viewCoordObj); props.setViewCoordObj(viewCoordObj);
// 계속 20개 미만이라 overAdd false처리(임시) // 계속 20개 미만이라 overAdd false처리
props.handlerAddChange('overAdd', false); props.handlerAddChange('overAdd', false);
}; };
@ -565,12 +572,12 @@ export const LaancDrawControl = props => {
return true; return true;
}; };
// 저장된 비행구역 데이터를 기반으로 지도에 다시 그려주기
const handlerPastDraw = () => { const handlerPastDraw = () => {
if (props.areaCoordList) { if (props.areaCoordList) {
const objs = drawObj?.getAll().features; const objs = drawObj?.getAll().features;
const areas = props.areaCoordList; const areas = props.areaCoordList;
if (areas.length > 0 && objs.length > 0) { if (areas.length > 0 && objs.length > 0) {
// areas -> 현재는 1개이지만 추후에 데이터가 바뀌면 여러개의 도형도 처리 가능!
areas.map((area, idx) => { areas.map((area, idx) => {
const paths = []; const paths = [];
area.coordList.forEach(coord => paths.push([coord.lon, coord.lat])); area.coordList.forEach(coord => paths.push([coord.lon, coord.lat]));
@ -639,7 +646,7 @@ export const LaancDrawControl = props => {
} }
objId = polygonId; objId = polygonId;
} }
// 마커를 삭제하고 다시 그려주기 // 마커를 삭제하고 다시 그려
const data = { const data = {
coord: area.areaType === 'LINE' ? paths : [paths], coord: area.areaType === 'LINE' ? paths : [paths],
id: objId id: objId
@ -754,8 +761,9 @@ export const LaancDrawControl = props => {
return newObjId[0]; return newObjId[0];
}; };
// 비행구역 기본셋팅
const drawInit = () => { const drawInit = () => {
const mode = mapControl.drawType; const mode = drawType;
if (mode !== 'DONE') { if (mode !== 'DONE') {
if (!mode || mode === 'RESET') { if (!mode || mode === 'RESET') {
handlerResetMode(); handlerResetMode();
@ -790,6 +798,7 @@ export const LaancDrawControl = props => {
drawObj.changeMode('simple_select'); drawObj.changeMode('simple_select');
}; };
// 그리기모드 셋팅
const handlerStartMode = mode => { const handlerStartMode = mode => {
if (mode === 'LINE') { if (mode === 'LINE') {
drawObj.changeMode('draw_line_string'); drawObj.changeMode('draw_line_string');
@ -802,7 +811,6 @@ export const LaancDrawControl = props => {
return ( return (
<> <>
<InfoModal modal={alertModal} setModal={setAlertModal} />
<ErrorModal modal={isErrorModal} setModal={setIsErrorModal} /> <ErrorModal modal={isErrorModal} setModal={setIsErrorModal} />
</> </>
); );

1
src/components/laanc/map/LaancDrawModal.js

@ -1,6 +1,7 @@
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
export default function LaancDrawModal({ modal, handler }) { export default function LaancDrawModal({ modal, handler }) {
// 드론원스탑으로 새창 바로가기
const handlerDroneOneStop = () => { const handlerDroneOneStop = () => {
window.open('https://drone.onestop.go.kr/', '드론원스탑'); window.open('https://drone.onestop.go.kr/', '드론원스탑');
handler(); handler();

8
src/components/laanc/map/LaancMapSearch.js

@ -4,8 +4,13 @@ import { useState } from 'react';
import { flightPlanAPI } from '../../../modules/basis/flight/apis/basisFlightApi'; import { flightPlanAPI } from '../../../modules/basis/flight/apis/basisFlightApi';
export default function LaancMapSearch({ mapObject }) { export default function LaancMapSearch({ mapObject }) {
// 검색어
const [query, setQuery] = useState(''); const [query, setQuery] = useState('');
// 검색 결과
const [searchRes, setSearchRes] = useState([]); const [searchRes, setSearchRes] = useState([]);
// 검색 여부
const [isSearch, setIsSearch] = useState(false); const [isSearch, setIsSearch] = useState(false);
// 지역 검색 // 지역 검색
@ -15,6 +20,7 @@ export default function LaancMapSearch({ mapObject }) {
setSearchRes(res.data.items); setSearchRes(res.data.items);
}; };
// 검색어 저장
const handlerSearchChange = e => { const handlerSearchChange = e => {
const { name, value } = e.target; const { name, value } = e.target;
@ -23,12 +29,14 @@ export default function LaancMapSearch({ mapObject }) {
} }
}; };
// 지역 검색 후 엔터 키
const handlerSearchEnter = e => { const handlerSearchEnter = e => {
if (e.key == 'Enter') { if (e.key == 'Enter') {
handlerSearchRes(); handlerSearchRes();
} }
}; };
// 해당 좌표로 지도 이동
const handlerSearchCoord = (mapx, mapy) => { const handlerSearchCoord = (mapx, mapy) => {
const numberString = [mapx, mapy]; const numberString = [mapx, mapy];
const latlng = []; const latlng = [];

17
src/components/laanc/step/LaacnStep3.js

@ -41,10 +41,15 @@ export default function LaacnStep3({
} }
}; };
const [centeredModal2, setCenteredModal2] = useState(false); // 성공 모달
const [confirmModal, setConfirmModal] = useState(false);
// 공문 모달
const [formModal, setFormModal] = useState(false); const [formModal, setFormModal] = useState(false);
const [numPages, setNumPages] = useState(null); // total // total
const [numPages, setNumPages] = useState(null);
// 로그인 정보
const { user } = useSelector(state => state.authState); const { user } = useSelector(state => state.authState);
// pdf 데이터
const { laancPdf } = useSelector(state => state.laancState); const { laancPdf } = useSelector(state => state.laancState);
// PDF 다운로드 // PDF 다운로드
@ -241,16 +246,16 @@ export default function LaacnStep3({
<span className='on'></span> <span className='on'></span>
</li> </li>
</ul> </ul>
<Button outline onClick={() => setCenteredModal2(!centeredModal2)}> <Button outline onClick={() => setConfirmModal(!confirmModal)}>
완료 완료
</Button> </Button>
<Modal <Modal
isOpen={centeredModal2} isOpen={confirmModal}
toggle={() => setCenteredModal2(!centeredModal2)} toggle={() => setConfirmModal(!confirmModal)}
className='modal-dialog-centered' className='modal-dialog-centered'
style={{ maxWidth: '650px', margin: '0 auto' }} style={{ maxWidth: '650px', margin: '0 auto' }}
> >
<ModalHeader toggle={() => setCenteredModal2(!centeredModal2)}> <ModalHeader toggle={() => setConfirmModal(!confirmModal)}>
비행 승인 완료 비행 승인 완료
</ModalHeader> </ModalHeader>
<ModalBody> <ModalBody>

14
src/components/laanc/step/LaancStep1.js

@ -39,24 +39,36 @@ export default function LaancStep1({
}) { }) {
const dispatch = useDispatch(); const dispatch = useDispatch();
// 로그인 정보
const { user } = useSelector(state => state.authState); const { user } = useSelector(state => state.authState);
// 비행 구역 정보
const { areaCoordList } = useSelector(state => state.flightState); const { areaCoordList } = useSelector(state => state.flightState);
// 일물 일출, 고도 정보, 관제권안 정보
const { laancSun, laancElev, laancArea } = useSelector( const { laancSun, laancElev, laancArea } = useSelector(
state => state.laancState state => state.laancState
); );
// LAANC 폼 제어
const fltElevRef = useRef(null); const fltElevRef = useRef(null);
const bufferZoneRef = useRef(null); const bufferZoneRef = useRef(null);
const schFltStDtRef = useRef(null); const schFltStDtRef = useRef(null);
const schFltEndDtRef = useRef(null); const schFltEndDtRef = useRef(null);
// 마운트 시 지도 표출 여부
const location = useLocation(); const location = useLocation();
const queryParams = new URLSearchParams(location.search); const queryParams = new URLSearchParams(location.search);
const mapParam = queryParams.get('map'); const mapParam = queryParams.get('map');
const [qrData, setQrData] = useState(); // qr 인증 데이터 // qr 인증 데이터
const [qrData, setQrData] = useState();
// qr 팝업
const [isPopUp, setIsPopUp] = useState(false); const [isPopUp, setIsPopUp] = useState(false);
// 아이콘 팝오버
const [popoverCommercial, setPopoverCommercial] = useState(false); const [popoverCommercial, setPopoverCommercial] = useState(false);
const [popoverSchFltStDt, setPopoverSchFltStDt] = useState(false); const [popoverSchFltStDt, setPopoverSchFltStDt] = useState(false);
const [popoverSchFltEndDt, setPopoverSchFltEndDt] = useState(false); const [popoverSchFltEndDt, setPopoverSchFltEndDt] = useState(false);
// 모달
const [isErrorModal, setIsErrorModal] = useState({ const [isErrorModal, setIsErrorModal] = useState({
isOpen: false, isOpen: false,
title: '', title: '',

6
src/components/laanc/step/LaancStep2.js

@ -40,11 +40,17 @@ export default function LaancStep2({
11: '25kg초과' 11: '25kg초과'
} }
}; };
// 약관 동의
const [isterms, setIsterms] = useState(false); const [isterms, setIsterms] = useState(false);
// 약관 팝업
const [isPopUp, setIsPopUp] = useState(false); const [isPopUp, setIsPopUp] = useState(false);
// 비행 승인 요청 데이터
const [flightData, setFlightData] = useState({}); const [flightData, setFlightData] = useState({});
// 로그인 정보
const { user } = useSelector(state => state.authState); const { user } = useSelector(state => state.authState);
// 약관 동의 데이터
const { termsList } = useSelector(state => state.accountState); const { termsList } = useSelector(state => state.accountState);
// pdf 데이터
const { laancPdf } = useSelector(state => state.laancState); const { laancPdf } = useSelector(state => state.laancState);
const dispatch = useDispatch(); const dispatch = useDispatch();

35
src/containers/analysis/simulator/AnalysisSimulationContainer.js

@ -15,33 +15,47 @@ let playCount = 0;
let playCounts = 0; let playCounts = 0;
export const AnalysisSimulationContainer = props => { export const AnalysisSimulationContainer = props => {
// 슬라이드 모든 데이터
const { list, count, detail, searchParams, log, stcsList, stcsCount, page } = const { list, count, detail, searchParams, log, stcsList, stcsCount, page } =
useSelector(state => state.analysisSimulatorState); useSelector(state => state.analysisSimulatorState);
// 비행 시물레이션 데이터
const [oepnReportList, setOpenReportList] = useState(false); const [oepnReportList, setOpenReportList] = useState(false);
// 지도 객체
const [mapObject, setMapObject] = useState(null); const [mapObject, setMapObject] = useState(null);
// 비행 시물레이션 데이터 상세보기
const [openDetail, setOpenDetail] = useState(false); const [openDetail, setOpenDetail] = useState(false);
// 선택 마커
const [selMarker, setSelMarker] = useState(null); const [selMarker, setSelMarker] = useState(null);
// 좌표 정보
const [selPolyline, setSelPolyline] = useState(null); const [selPolyline, setSelPolyline] = useState(null);
// 슬라이드 재생 여부
const [isPlay, setIsPlay] = useState(false); const [isPlay, setIsPlay] = useState(false);
// 드론 정보
const [info, setInfo] = useState(null); const [info, setInfo] = useState(null);
// 슬라이드 시간
const [timeCd, setTimeCd] = useState(null); const [timeCd, setTimeCd] = useState(null);
// 슬라이드 카운터
const [sliderCount, setSliderCount] = useState(0); const [sliderCount, setSliderCount] = useState(0);
// 검색 텍스트
const [searchText, setSearchText] = useState(''); const [searchText, setSearchText] = useState('');
// 비행 pk 값
const [cntrlId, setCntrlId] = useState(''); const [cntrlId, setCntrlId] = useState('');
// 드론 마커
const [marker, setMarker] = useState(null); const [marker, setMarker] = useState(null);
// 카운터 초기값
const [sliderVal, setSliderVal] = useState({ const [sliderVal, setSliderVal] = useState({
maxVal: 0, maxVal: 0,
minVal: 0 minVal: 0
@ -49,16 +63,18 @@ export const AnalysisSimulationContainer = props => {
const dispatch = useDispatch(); const dispatch = useDispatch();
// 드론 갯수
const [dronLength, setDronLength] = useState(0); const [dronLength, setDronLength] = useState(0);
// 비행 시간 카운터
const [countArray, setCountArray] = useState([]); const [countArray, setCountArray] = useState([]);
// 검색 데이터
const [params, setParams] = useState({ const [params, setParams] = useState({
stDate: moment().subtract(1, 'day').format('YYYY-MM-DD'), stDate: moment().subtract(1, 'day').format('YYYY-MM-DD'),
endDate: moment().subtract(0, 'day').format('YYYY-MM-DD'), endDate: moment().subtract(0, 'day').format('YYYY-MM-DD'),
search1: '' search1: ''
}); });
// 시뮬레이션 타이머 // 시뮬레이션 타이머 로직
useEffect(() => { useEffect(() => {
if (isPlay) { if (isPlay) {
const countCheck = log.map(item => const countCheck = log.map(item =>
@ -95,6 +111,7 @@ export const AnalysisSimulationContainer = props => {
} }
}, [isPlay, log]); }, [isPlay, log]);
//
useEffect(() => { useEffect(() => {
if (isPlay) { if (isPlay) {
setInfo({ ...log[playCount], playCount, playCounts }); setInfo({ ...log[playCount], playCount, playCounts });
@ -102,6 +119,7 @@ export const AnalysisSimulationContainer = props => {
} }
}, [stcsList]); }, [stcsList]);
// 검색 변경 헨들러
useEffect(() => { useEffect(() => {
if (oepnReportList) { if (oepnReportList) {
if (searchParams) { if (searchParams) {
@ -113,6 +131,8 @@ export const AnalysisSimulationContainer = props => {
} }
} }
}, [oepnReportList]); }, [oepnReportList]);
// 슬라이드 카운터 로직
useEffect(() => { useEffect(() => {
if (sliderCount && sliderCount > 0) { if (sliderCount && sliderCount > 0) {
let benear = countArray[0]; let benear = countArray[0];
@ -138,6 +158,7 @@ export const AnalysisSimulationContainer = props => {
} }
}, [sliderCount]); }, [sliderCount]);
// 슬라이드 카운터 초기화
useEffect(() => { useEffect(() => {
playCount = 0; playCount = 0;
playCounts = 0; playCounts = 0;
@ -163,6 +184,7 @@ export const AnalysisSimulationContainer = props => {
// let maxDate; // let maxDate;
}, [log]); }, [log]);
// 검색 헨들러
const handlerSearch = search1 => { const handlerSearch = search1 => {
setParams({ ...params, search1 }); setParams({ ...params, search1 });
dispatch( dispatch(
@ -178,10 +200,12 @@ export const AnalysisSimulationContainer = props => {
dispatch(Actions.log.request(id)); dispatch(Actions.log.request(id));
}; };
//
const handlerStcsSearch = id => { const handlerStcsSearch = id => {
dispatch(Actions.stcs.request(id)); dispatch(Actions.stcs.request(id));
}; };
// 검색
const handlerInput = (type, val) => { const handlerInput = (type, val) => {
if (type === 'search1') { if (type === 'search1') {
setParams({ ...params, search1: val }); setParams({ ...params, search1: val });
@ -197,6 +221,7 @@ export const AnalysisSimulationContainer = props => {
} }
}; };
// 상세보기
const handlerDetail = id => { const handlerDetail = id => {
// setOpenReportList(false); // setOpenReportList(false);
handlerDetailSearch(id); handlerDetailSearch(id);
@ -206,19 +231,25 @@ export const AnalysisSimulationContainer = props => {
setCntrlId(id); setCntrlId(id);
}; };
// 로그아웃
const handlerLogout = () => { const handlerLogout = () => {
dispatch(Action.logout.request()); dispatch(Action.logout.request());
}; };
// 비행 시물레이션 데이터 닫기
const handlerDetailClose = () => { const handlerDetailClose = () => {
setOpenDetail(false); setOpenDetail(false);
}; };
// 비행 시물레이션 데이터 호출
const handlerPageList = useCallback(() => { const handlerPageList = useCallback(() => {
dispatch( dispatch(
Actions.list.request({ searchParams: { ...params }, page: page + 1 }) Actions.list.request({ searchParams: { ...params }, page: page + 1 })
); );
}, [params, list, page]); }, [params, list, page]);
// 비행 시물레이션 데이터 모달 헨들러
const handlerOpenReportList = useCallback( const handlerOpenReportList = useCallback(
val => { val => {
setOpenReportList(val); setOpenReportList(val);

13
src/containers/laanc/LaancContainer.js

@ -13,11 +13,17 @@ import LaancGrid from '../../components/laanc/list/LaancGrid';
export default function LaancContainer() { export default function LaancContainer() {
const dispatch = useDispatch(); const dispatch = useDispatch();
const location = useLocation(); // map 컴포넌트 표출 여부
const [currentParm, setCurrentParm] = useState(false); const [currentParm, setCurrentParm] = useState(false);
//LAANC 신청하기 모달
const [disabledAnimation, setDisabledAnimation] = useState(false); const [disabledAnimation, setDisabledAnimation] = useState(false);
<<<<<<< HEAD
const [isSearch, setIsSearch] = useState(false); const [isSearch, setIsSearch] = useState(false);
=======
// 마운트 시 지도 표출 여부
const location = useLocation();
>>>>>>> 1ed9d9e3865eef0d42bac8f02dddb2a221ff8d58
const queryParams = new URLSearchParams(location.search); const queryParams = new URLSearchParams(location.search);
const mapParam = queryParams.get('map'); const mapParam = queryParams.get('map');
@ -30,6 +36,7 @@ export default function LaancContainer() {
setDisabledAnimation(mapParam != 'true' ? false : true); setDisabledAnimation(mapParam != 'true' ? false : true);
}, [location]); }, [location]);
<<<<<<< HEAD
// Laanc 신청 이후 자동 검색 // Laanc 신청 이후 자동 검색
useEffect(() => { useEffect(() => {
if (disabledAnimation) { if (disabledAnimation) {
@ -37,6 +44,8 @@ export default function LaancContainer() {
} else setIsSearch(true); } else setIsSearch(true);
}, [disabledAnimation]); }, [disabledAnimation]);
=======
>>>>>>> 1ed9d9e3865eef0d42bac8f02dddb2a221ff8d58
// LAANC 신청하기 버튼 클릭 헨들러 // LAANC 신청하기 버튼 클릭 헨들러
const handleApply = () => { const handleApply = () => {
dispatch(drawTypeChangeAction('')); dispatch(drawTypeChangeAction(''));

314
src/containers/laanc/LaancPlanContainer.js

@ -17,11 +17,20 @@ export default function LaancPlanContainer({
}) { }) {
const dispatch = useDispatch(); const dispatch = useDispatch();
// 비행 구역 정보
const { areaCoordList } = useSelector(state => state.flightState);
// 로그인 정보
const { user } = useSelector(state => state.authState); const { user } = useSelector(state => state.authState);
// 관제권안 정보,고도 정보
const { laancArea, laancElev } = useSelector(state => state.laancState);
// laanc step
const [step, setStep] = useState(1); const [step, setStep] = useState(1);
// laanc 초기값
const [detailData, setDetailData] = useState(initFlightBas.initDetail); const [detailData, setDetailData] = useState(initFlightBas.initDetail);
// 비행 구역 보달
const [centeredModal, setCenteredModal] = useState(false); const [centeredModal, setCenteredModal] = useState(false);
// 모달
const [isErrorModal, setIsErrorModal] = useState({ const [isErrorModal, setIsErrorModal] = useState({
isOpen: false, isOpen: false,
title: '', title: '',
@ -54,6 +63,309 @@ export default function LaancPlanContainer({
setStep(step); setStep(step);
}; };
// 날씨 핸들러
const handlerWeather = () => {
setFormModal(!formModal);
};
// 비행계획서 작성 핸들러
const handleChange = ({ name, value, type, index, pIndex }) => {
const arrName = `${type}List`;
switch (type) {
case 'coord':
setDetailData(prevState => {
return {
...prevState,
areaList: [
{
...prevState.areaList[0],
coordList: value
}
]
};
});
break;
case 'area':
if (name === 'fltMethod' && value != '직접입력') {
setDetailData(prevState => {
const arr = [...prevState[arrName]];
const updateData = {
...prevState[arrName][0],
[name]: value,
fltMothoeRm: ''
};
arr[0] = updateData;
return {
...prevState,
[arrName]: arr
};
});
} else if (
detailData.areaList[0].areaType === 'LINE' ||
name === 'bufferZone'
) {
setDetailData(prevState => {
const arr = [...prevState[arrName]];
const prevBufferZone = prevState[arrName][0].bufferZone;
const updateData = {
...prevState[arrName][0],
[name]: value,
concatBufferZone: prevBufferZone
};
arr[0] = updateData;
return {
...prevState,
[arrName]: arr
};
});
} else {
setDetailData(prevState => {
const arr = [...prevState[arrName]];
const updateData = {
...prevState[arrName][0],
[name]: value
};
arr[0] = updateData;
return {
...prevState,
[arrName]: arr
};
});
}
break;
case 'pilot':
case 'arcrft':
{
setDetailData(prevState => {
const arr = [...prevState[arrName]];
const updateData = {
...prevState[arrName][0],
[name]: value
};
arr[0] = updateData;
return {
...prevState,
[arrName]: arr
};
});
}
break;
case 'plan':
default:
setDetailData(prevState => ({
...prevState,
[name]: value
}));
break;
}
};
// 스텝 1 다음 버튼 이벤트
const handlerNext = () => {
// 시작일자
const schFltStDt = moment(detailData.schFltStDt, 'YYYY-MM-DD HH:mm:ss');
// 종료일자
const schFltEndDt = moment(detailData.schFltEndDt, 'YYYY-MM-DD HH:mm:ss');
const currentDate = moment(); // 현재 날짜와 시간을 가져옵니다.
const validateAircraftWeightCode =
!detailData.arcrftList[0].arcrftTypeCd &&
(detailData.commercial === 'COMMERCIAL' ||
detailData.arcrftList[0].arcrftWghtCd == '9' ||
detailData.arcrftList[0].arcrftWghtCd == '10' ||
detailData.arcrftList[0].arcrftWghtCd == '11');
const validateidntfNumCode =
!detailData.arcrftList[0].idntfNum &&
(detailData.commercial === 'COMMERCIAL' ||
detailData.arcrftList[0].arcrftWghtCd == '9' ||
detailData.arcrftList[0].arcrftWghtCd == '10' ||
detailData.arcrftList[0].arcrftWghtCd == '11');
if (!detailData.fltType) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '비행 종류(상업/비상업)를 선택해주세요.'
});
return false;
} else if (
!schFltStDt.isAfter(currentDate) ||
!schFltEndDt.isAfter(currentDate)
) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '비행 일자가 이미 지난 일자입니다.'
});
return false;
} else if (schFltStDt.isAfter(schFltEndDt)) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '비행일자를 확인해주세요.'
});
return false;
} else if (schFltStDt.format('A h:mm') === 'PM 11:00') {
setIsErrorModal({
isOpen: true,
title: '특별 비행',
desc: (
<>
야간 비행은 특별 비행에 해당됩니다.
<br />
특별 비행의 경우 드론원스톱을 통해서 신청해주시기 바랍니다.
</>
)
});
return false;
} else if (schFltStDt.format('A h:mm') === 'PM 5:00') {
setIsErrorModal({
isOpen: true,
title: '비행구역 및 비행일자 중복',
desc: (
<>
설정하신 비행구역 비행시간에 이미 승인완료된 신청건이 있습니다.
<br /> 다시 설정 부탁드립니다.
</>
)
});
return false;
} else if (!detailData.fltPurpose) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '비행목적을 선택해 주세요.'
});
return false;
} else if (
!detailData.areaList[0].fltElev ||
detailData.areaList[0].fltElev === 0
) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '고도를 입력해 주세요.'
});
return false;
} else if (!detailData.areaList[0].bufferZone) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '반경을 입력해 주세요.'
});
return false;
} else if (
detailData.areaList[0].concatBufferZone !=
detailData.areaList[0].bufferZone &&
detailData.areaList[0].areaType === 'LINE'
) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: <>적용 버튼을 누르지 않고 값을 변경 없습니다.</>
});
// handleChange({
// type: 'area',
// name: 'bufferZone',
// value: detailData.areaList[0].concatBufferZone
// });
} else if (!detailData.areaList[0].fltMethod) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '비행방식를 입력해 주세요.'
});
return false;
} else if (
detailData.areaList[0].fltMethod === '00' &&
!detailData.areaList[0].fltMothoeRm
) {
// 비행 방식 직접 입력칸 활성화 후 작성 시 조건문
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '비행방식을 입력해 주세요.'
});
return false;
} else if (validateAircraftWeightCode) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '기체 종류를 입력해 주세요.'
});
return false;
} else if (validateidntfNumCode) {
setIsErrorModal({
isOpen: true,
title: '필수값 입력 오류',
desc: '기체 신고 번호를 입력해 주세요.'
});
return false;
} else {
handlerLaanc();
}
};
// 비행 구역 적용 버튼 핸들러
const handlerBufferApply = async () => {
if (areaCoordList) {
if (areaCoordList[0].coordList.length > 0) {
// dispatch(LaancAction.LAANC_ALTITUDE.request(detailData.areaList));
dispatch(LaancAction.LAANC_VALID_AREA.request(detailData.areaList));
const array = [];
const copy = { ...areaCoordList[0] };
copy.bufferZone = detailData.areaList[0].bufferZone;
array.push(copy);
dispatch(FLIGHT_PLAN_AREA_BUFFER_LIST.request(array));
try {
const elev = await axios.post(
`api/bas/laanc/valid/elev`,
detailData.areaList
);
if (elev.data[0] === 0) {
// dispatch(AREA_DETAIL_INIT());
// dispatch(AreaAction.AREA_DETAIL_INIT());
// dispatch(drawTypeChangeAction(''));
// dispatch(LaancAction.LAANC_APPROVAL_INIT());
setIsErrorModal({
title: '비행 불가 지역',
desc: (
<>
설정하신 비행구역 허용고도가 0m인 구역이 있습니다.
<br />
버퍼존을 다시 확인해주시기 바랍니다.
</>
),
isOpen: true
});
}
dispatch(LaancAction.LAANC_ALTITUDE.success(elev.data));
} catch (error) {
{
setIsErrorModal({
isOpen: true,
title: '오류',
desc: '처리중 오류가 발생하였습니다'
});
}
}
}
}
};
// Laanc 승인 요청 취소 버튼 헨들러 // Laanc 승인 요청 취소 버튼 헨들러
const handlerLaancClose = () => { const handlerLaancClose = () => {
setStep(1); setStep(1);

34
src/utility/DrawUtil.js

@ -2,6 +2,7 @@ import * as turf from '@turf/turf';
import mapboxgl from 'mapbox-gl'; import mapboxgl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css'; import 'mapbox-gl/dist/mapbox-gl.css';
// geojson Feature 형식으로 반환
export const InitFeature = (type, id) => { export const InitFeature = (type, id) => {
return { return {
type: 'Feature', type: 'Feature',
@ -43,6 +44,13 @@ export const CalculateDistance = (mouse, center) => {
return distance; return distance;
}; };
// 미터 반환(m붙여서)
export const fromMetersToText = meters => {
meters = meters || 0;
const text = parseFloat(meters.toFixed(1)) + 'm';
return text;
};
// 두 좌표 간의 중간 지점 좌표 반환 // 두 좌표 간의 중간 지점 좌표 반환
export const handlerGetMidPoint = (dis1, dis2) => { export const handlerGetMidPoint = (dis1, dis2) => {
return [(dis1[0] + dis2[0]) / 2, (dis1[1] + dis2[1]) / 2]; return [(dis1[0] + dis2[0]) / 2, (dis1[1] + dis2[1]) / 2];
@ -63,13 +71,6 @@ export const handlerGetHtmlContent = (distance, id) => {
); );
}; };
// 미터 반환(m붙여서)
export const fromMetersToText = meters => {
meters = meters || 0;
const text = parseFloat(meters.toFixed(1)) + 'm';
return text;
};
// circle 360도 좌표 반환 // circle 360도 좌표 반환
export const handlerGetCircleCoord = (center, distance) => { export const handlerGetCircleCoord = (center, distance) => {
const options = { const options = {
@ -183,25 +184,6 @@ export const layerWayPoint = source => {
}; };
}; };
export const layerGuideLine = source => {
return {
id: 'guideline',
type: 'line',
source: source,
layout: {
'line-cap': 'round',
'line-join': 'round'
},
paint: {
'line-color': '#283046',
'line-width': 2,
'line-opacity': 0.5,
'line-dasharray': [5, 5]
},
filter: ['==', ['get', 'id'], 'guideline']
};
};
export const layerPolyline = source => { export const layerPolyline = source => {
return { return {
id: 'polyline', id: 'polyline',

28
src/views/control/alarm/ControlAlarmDetail.js

@ -1,7 +1,10 @@
import { useState, useEffect } from 'react'; import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Card } from 'reactstrap'
const ControlAlarmDetail = ({ historyModal, setHistoryModal, controlGpWarnLog }) => { const ControlAlarmDetail = ({
historyModal,
setHistoryModal,
controlGpWarnLog
}) => {
return ( return (
<Modal <Modal
isOpen={historyModal} isOpen={historyModal}
@ -10,7 +13,7 @@ const ControlAlarmDetail = ({ historyModal, setHistoryModal, controlGpWarnLog })
> >
<ModalHeader toggle={() => setHistoryModal(!historyModal)}> <ModalHeader toggle={() => setHistoryModal(!historyModal)}>
<div className='drone-ti'> <div className='drone-ti'>
<span className="drone-name">{controlGpWarnLog?.idntfNum}</span> <span className='drone-name'>{controlGpWarnLog?.idntfNum}</span>
<span>알림내역</span> <span>알림내역</span>
</div> </div>
</ModalHeader> </ModalHeader>
@ -23,7 +26,7 @@ const ControlAlarmDetail = ({ historyModal, setHistoryModal, controlGpWarnLog })
<th>날짜</th> <th>날짜</th>
<th>내용</th> <th>내용</th>
</tr> </tr>
{controlGpWarnLog ? {controlGpWarnLog ? (
controlGpWarnLog.map((p, i) => { controlGpWarnLog.map((p, i) => {
return ( return (
<tr key={i}> <tr key={i}>
@ -32,25 +35,22 @@ const ControlAlarmDetail = ({ historyModal, setHistoryModal, controlGpWarnLog })
<th>{p.createDt}</th> <th>{p.createDt}</th>
<th>{p.warnType}</th> <th>{p.warnType}</th>
</tr> </tr>
) );
}) })
: ) : (
<tr> <tr>
<th colSpan={3}>데이터가 없습니다.</th> <th colSpan={3}>데이터가 없습니다.</th>
</tr> </tr>
} )}
</table> </table>
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<Button <Button color='info' onClick={() => setHistoryModal(!historyModal)}>
color='info'
onClick={() => setHistoryModal(!historyModal)}
>
확인 확인
</Button> </Button>
</ModalFooter> </ModalFooter>
</Modal> </Modal>
) );
} };
export default ControlAlarmDetail; export default ControlAlarmDetail;

114
src/views/control/alarm/ControlAlarmList.js

@ -1,45 +1,54 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { X } from 'react-feather'; import { X } from 'react-feather';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { controlGpArcrftWarnAction, controlGpLogAction } from '../../../modules/control/gp/actions/controlGpAction'; import { controlGpLogAction } from '../../../modules/control/gp/actions/controlGpAction';
import ControlAlarmDetail from './ControlAlarmDetail'; import ControlAlarmDetail from './ControlAlarmDetail';
import { Badge } from 'reactstrap'; import { Badge } from 'reactstrap';
const ControlAlarmList = props => { const ControlAlarmList = props => {
const dispatch = useDispatch(); const dispatch = useDispatch();
// 비정상상황 상세 히스토리 모달
const [historyModal, setHistoryModal] = useState(false); const [historyModal, setHistoryModal] = useState(false);
const { controlGpList } = useSelector(state => state.controlGpState); // 비정상상황 상세 히스토리
const { controlGpArcrftWarnList } = useSelector(state => state.controlGpLogState);
const { controlGpWarnLog } = useSelector(state => state.controlGpLogState); const { controlGpWarnLog } = useSelector(state => state.controlGpLogState);
const { objectId, isClickObject } = useSelector(state => state.controlMapReducer);
// 비정상상황 기체 목록
const { controlGpArcrftWarnList } = useSelector(
state => state.controlGpLogState
);
// 클릭한 기체 Id, 비행중인 기체 클릭 여부
const { objectId, isClickObject } = useSelector(
state => state.controlMapReducer
);
// 전체 드론, 비정상 드론 개수
const [total, setTotal] = useState({ const [total, setTotal] = useState({
totalDroneCnt: 0, totalDroneCnt: 0,
totalWarnCnt: 0, totalWarnCnt: 0,
warnList: [] warnList: []
}); });
const handleWarnDetail = (cntrlId) => { // 비정상상황 상세 히스토리 모달 표출
const handleWarnDetail = cntrlId => {
setHistoryModal(prev => !prev); setHistoryModal(prev => !prev);
dispatch(controlGpLogAction.request({ id: cntrlId }));
};
dispatch(controlGpLogAction.request({id : cntrlId})); // 비행중인 기체 클릭 시 비정상상황 사이드메뉴 닫힘
}
useEffect(() => { useEffect(() => {
if(isClickObject) { if (isClickObject) {
props.setOpenAlarmList(false); props.setOpenAlarmList(false);
} }
}, [objectId, isClickObject]);
}, [objectId, isClickObject]) // 비정상상황 기체 개수 계산
useEffect(() => { useEffect(() => {
if(controlGpArcrftWarnList) { if (controlGpArcrftWarnList) {
let totalWarnCnt = 0; let totalWarnCnt = 0;
if(controlGpArcrftWarnList.length > 0) { if (controlGpArcrftWarnList.length > 0) {
controlGpArcrftWarnList.forEach(warn => { controlGpArcrftWarnList.forEach(warn => {
totalWarnCnt += warn.warnCount; totalWarnCnt += warn.warnCount;
}); });
@ -47,13 +56,12 @@ const ControlAlarmList = props => {
setTotal(total => { setTotal(total => {
return { return {
totalDroneCnt : controlGpArcrftWarnList.length, totalDroneCnt: controlGpArcrftWarnList.length,
totalWarnCnt : totalWarnCnt, totalWarnCnt: totalWarnCnt,
warnList : controlGpArcrftWarnList warnList: controlGpArcrftWarnList
} };
}) });
} }
}, [controlGpArcrftWarnList]); }, [controlGpArcrftWarnList]);
return ( return (
@ -63,7 +71,6 @@ const ControlAlarmList = props => {
<h4>실시간 비정상 알림 정보</h4> <h4>실시간 비정상 알림 정보</h4>
<button <button
className='btn-icon' className='btn-icon'
// outline
color='primary' color='primary'
onClick={() => props.setOpenAlarmList(false)} onClick={() => props.setOpenAlarmList(false)}
> >
@ -77,7 +84,7 @@ const ControlAlarmList = props => {
<div className='list-left-txt'>드론 현황</div> <div className='list-left-txt'>드론 현황</div>
<div className='list-right-txt'> <div className='list-right-txt'>
<Badge color='light-primary'> <Badge color='light-primary'>
{total? total.totalDroneCnt: 0} 비행 {total ? total.totalDroneCnt : 0} 비행
</Badge> </Badge>
</div> </div>
</div> </div>
@ -87,7 +94,7 @@ const ControlAlarmList = props => {
<div className='list-left-txt'>비정상 알림 전체</div> <div className='list-left-txt'>비정상 알림 전체</div>
<div className='list-right-txt'> <div className='list-right-txt'>
<Badge color='light-primary'> <Badge color='light-primary'>
{total? total.totalWarnCnt : 0} {total ? total.totalWarnCnt : 0}
</Badge> </Badge>
</div> </div>
</div> </div>
@ -100,67 +107,36 @@ const ControlAlarmList = props => {
<h4>알림 목록</h4> <h4>알림 목록</h4>
</div> </div>
{total?.warnList.map((warn, i) => { {total?.warnList.map((warn, i) => {
const warnContent = warn.warnType === 'PLAN' ? '비행 경로 이탈' : '비정상 상황 발생' const warnContent =
warn.warnType === 'PLAN' ? '비행 경로 이탈' : '비정상 상황 발생';
return ( return (
<div className='layer-content-list' key={i} onClick={() => handleWarnDetail(warn.cntrlId)}> <div
className='layer-content-list'
key={i}
onClick={() => handleWarnDetail(warn.cntrlId)}
>
<dl className='notice-list'> <dl className='notice-list'>
<dt> <dt>
<div className='list-ti'> <div className='list-ti'>
<div className='list-left-txt'>{warn.idntfNum}</div> <div className='list-left-txt'>{warn.idntfNum}</div>
<div className='list-right-txt'>{warn.occurDt ? warn.occurDt : '-'}</div> <div className='list-right-txt'>
</div> {warn.occurDt ? warn.occurDt : '-'}
<div className='list-ti'>
<div className='list-left-txt'>{warnContent ? warnContent : '-'}</div>
<div className='list-right-txt'>{warn.warnCount ? warn.warnCount : '-'}</div>
</div>
</dt>
</dl>
</div> </div>
)
})}
{/* <div className='layer-content-list'>
<dl className='notice-list'>
<dt>
<div className='list-ti'>
<div className='list-left-txt'>PAV-001</div>
<div className='list-right-txt'>2022. 09. 02 10:00:30</div>
</div> </div>
<div className='list-ti'> <div className='list-ti'>
<div className='list-left-txt'>비행 경로 이탈</div> <div className='list-left-txt'>
<div className='list-right-txt'>22</div> {warnContent ? warnContent : '-'}
</div> </div>
</dt> <div className='list-right-txt'>
</dl> {warn.warnCount ? warn.warnCount : '-'}
</div>
<div className='layer-content-list'>
<dl className='notice-list'>
<dt>
<div className='list-ti'>
<div className='list-left-txt'>PAV-002</div>
<div className='list-right-txt'>2022. 09. 02 11:23:52</div>
</div> </div>
<div className='list-ti'>
<div className='list-left-txt'>비행 경로 이탈</div>
<div className='list-right-txt'>10</div>
</div> </div>
</dt> </dt>
</dl> </dl>
</div> </div>
<div className='layer-content-list'> );
<dl className='notice-list'> })}
<dt>
<div className='list-ti'>
<div className='list-left-txt'>PAV-003</div>
<div className='list-right-txt'>-</div>
</div>
<div className='list-ti'>
<div className='list-left-txt'>-</div>
<div className='list-right-txt'>0</div>
</div>
</dt>
</dl>
</div> */}
</div> </div>
<ControlAlarmDetail <ControlAlarmDetail

45
src/views/control/alarm/ControlAlarmNotice.js

@ -1,45 +0,0 @@
import { Bell, ChevronDown, ChevronUp } from "react-feather";
import { ReactComponent as DroneMenuIcon } from '../../../assets/images/drone_menu_icon.svg';
const ControlAlarmNotice = () => {
{}
return(
<div>
{/* <div className='notice'>
<div className='notice-icon'>
<Bell size={20} />
</div>
<div className='notice-txt'>
<dl>
<dt>
<span className='time'>2021-06-17 12:00:00</span>AVSF123
지역에 접근하였습니다111.
</dt>
<dt>
<span className='time'>2021-06-30 13:00:00</span>AVSF123
비행금지구역에 접근하였습니다.
</dt>
<dt>
<span className='time'>2021-08-20 14:00:00</span>AVSF123
국립공원구역에 접근하였습니다.
</dt>
</dl>
</div>
<div className='notice-btn'>
<button>
<ChevronUp size={15} />
</button>
<button>
<ChevronDown size={15} />
</button>
</div>
</div> */}
</div>
)
}
export default ControlAlarmNotice;

158
src/views/control/main/ControlMain.js

@ -11,7 +11,6 @@ import {
Cloud, Cloud,
CloudRain, CloudRain,
CloudSnow, CloudSnow,
Moon,
Grid Grid
} from 'react-feather'; } from 'react-feather';
@ -19,7 +18,6 @@ import { AiOutlinePoweroff } from 'react-icons/ai';
import { IoAlertOutline } from 'react-icons/io5'; import { IoAlertOutline } from 'react-icons/io5';
import { ReactComponent as DroneMenuIcon } from '../../../assets/images/drone_menu_icon.svg'; import { ReactComponent as DroneMenuIcon } from '../../../assets/images/drone_menu_icon.svg';
import { Card } from 'reactstrap'; import { Card } from 'reactstrap';
import ControlAlarmNotice from '../alarm/ControlAlarmNotice';
import ControlReportList from '../report/ControlReportList'; import ControlReportList from '../report/ControlReportList';
import ControlReportDetail from '../report/ControlReportDetail'; import ControlReportDetail from '../report/ControlReportDetail';
import ControlAlarmList from '../alarm/ControlAlarmList'; import ControlAlarmList from '../alarm/ControlAlarmList';
@ -28,71 +26,51 @@ import WebsocketClient from '../../../components/websocket/WebsocketClient';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { controlweatherAction } from '../../../modules/control/gp/actions/controlGpAction'; import { controlweatherAction } from '../../../modules/control/gp/actions/controlGpAction';
import * as Actions from '../../../modules/account/login/actions/authAction'; import * as Actions from '../../../modules/account/login/actions/authAction';
import { import { objectUnClickAction } from '../../../modules/control/map/actions/controlMapActions';
ctrlDrawTypeChangeAction,
objectUnClickAction
} from '../../../modules/control/map/actions/controlMapActions';
const ControlMain = () => { const ControlMain = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const history = useHistory();
// 비행중인 기체 클릭 여부
const { isClickObject } = useSelector(state => state.controlMapReducer); const { isClickObject } = useSelector(state => state.controlMapReducer);
const { controlGpList, controlGroupAuthInfo } = useSelector(
state => state.controlGpState // 비행중인 기체 정보
); const { controlGpList } = useSelector(state => state.controlGpState);
// 기체 상세 정보
const { controlDetail, controlWheather } = useSelector( const { controlDetail, controlWheather } = useSelector(
state => state.controlGpDtlState state => state.controlGpDtlState
); );
// 드론 기체 갯수 (드론 + uam)
const { controlGpCountDrone, controlGpCountFlight } = useSelector( const { controlGpCountDrone, controlGpCountFlight } = useSelector(
state => state.controlGpCountState state => state.controlGpCountState
); );
// pav박람회 -> uam, 드론 구별을 위한 임시 코드 // 드론, uam 기체 갯수
// (이 작업으로 고도화 하려면 추후에 서버에서 uam타입을 새로 더 받아 작업해야 함)
const [droneCount, setDroneCount] = useState(0); const [droneCount, setDroneCount] = useState(0);
const [uamCount, setUamCount] = useState(0); const [uamCount, setUamCount] = useState(0);
// 비정상상황 여부
const [alarm, setAlarm] = useState(false); const [alarm, setAlarm] = useState(false);
const { user } = useSelector(state => state.authState);
const [oepnReportList, setOpenReportList] = useState(false);
// const [openReportDetail, setOpenReportDetail] = useState(false);
// const [openWeatherList, setOpenWeatherList] = useState(false);
const [openAlarmList, setOpenAlarmList] = useState(false); // 오른쪽 사이드 메뉴 표출 여부
const [openSetting, setOpenSetting] = useState(true); const [openSetting, setOpenSetting] = useState(true);
const history = useHistory();
const openMenu = val => {
if (val === 'reportList') {
setOpenReportList(true);
// setOpenReportDetail(false);
// setOpenWeatherList(false);
setOpenAlarmList(false);
} else if (val === 'weatherList') {
setOpenReportList(false);
// setOpenReportDetail(false);
// setOpenWeatherList(true);
setOpenAlarmList(false);
} else if (val === 'alarmList') {
dispatch(objectUnClickAction());
setOpenReportList(false);
// setOpenReportDetail(false);
// setOpenWeatherList(false);
setOpenAlarmList(true);
setAlarm(false); // 왼쪽 드론 정보 사이드 메뉴 표출 여부
} const [oepnReportList, setOpenReportList] = useState(false);
};
// const openReportDetailParam = val => { // 왼쪽 드론 비정상상황 알림 사이드 메뉴 표출 여부
// setOpenReportDetail(true); const [openAlarmList, setOpenAlarmList] = useState(false);
// };
const handlerLogout = () => { // 김포공항 좌표
dispatch(Actions.logout.request()); const rq = {
nx: 37.558522,
ny: 126.793722
}; };
// 드론 비정상상황일 시 왼쪽 사이드 메뉴 알람 표시 아이콘 변경
useEffect(() => { useEffect(() => {
if (controlGpList) { if (controlGpList) {
const warnGps = controlGpList.find(gps => { const warnGps = controlGpList.find(gps => {
@ -105,6 +83,7 @@ const ControlMain = () => {
} }
}, [controlGpList]); }, [controlGpList]);
// 비행중인 기체 클릭 시 열려있는 사이드 메뉴 닫기
useEffect(() => { useEffect(() => {
if (isClickObject) { if (isClickObject) {
setOpenReportList(false); setOpenReportList(false);
@ -112,6 +91,7 @@ const ControlMain = () => {
} }
}, [isClickObject]); }, [isClickObject]);
// 드론, uam 기체 갯수 계산
useEffect(() => { useEffect(() => {
if (controlGpCountDrone) { if (controlGpCountDrone) {
const uamCnt = controlGpCountDrone.filter(i => const uamCnt = controlGpCountDrone.filter(i =>
@ -126,19 +106,43 @@ const ControlMain = () => {
} }
}, [controlGpCountDrone]); }, [controlGpCountDrone]);
const handlerClose = () => { // 김포공항 날씨 API호출
useEffect(() => {
dispatch(controlweatherAction.request(rq));
}, []);
// 화면 왼쪽 사이드 메뉴 오픈 시 다른 메뉴 닫기
const openMenu = val => {
console.log(val, '--');
if (val === 'reportList') {
setOpenReportList(true); setOpenReportList(true);
setOpenAlarmList(false);
} else if (val === 'weatherList') {
setOpenReportList(false);
setOpenAlarmList(false);
} else if (val === 'alarmList') {
dispatch(objectUnClickAction()); dispatch(objectUnClickAction());
setOpenReportList(false);
setOpenAlarmList(true);
setAlarm(false);
}
}; };
//날씨 API
const rq = { // 로그아웃
nx: 37.558522, const handlerLogout = () => {
ny: 126.793722 dispatch(Actions.logout.request());
}; };
useEffect(() => {
dispatch(controlweatherAction.request(rq)); // 드론 상세정보 창 닫기
}, []); const handlerClose = () => {
function weathericon() { setOpenReportList(true);
dispatch(objectUnClickAction());
};
// 김포공항 날씨 아이콘 설정
const weathericon = () => {
if (controlWheather) { if (controlWheather) {
let wheatherDetail = controlWheather.items.item; let wheatherDetail = controlWheather.items.item;
let skyDetail = wheatherDetail[6].fcstValue; let skyDetail = wheatherDetail[6].fcstValue;
@ -150,24 +154,10 @@ const ControlMain = () => {
return <Sun size={20} />; return <Sun size={20} />;
} else return <Cloud size={20} />; } else return <Cloud size={20} />;
} }
}
const handlerDrawType = val => {
dispatch(ctrlDrawTypeChangeAction(val));
};
const ThemeToggler = () => {
if (skin === 'dark') {
return <Sun className='ficon' onClick={() => setSkin('light')} />;
} else {
return <Moon className='ficon' onClick={() => setSkin('dark')} />;
}
}; };
return ( return (
<> <>
<ControlAlarmNotice />
<div className='left-menu'> <div className='left-menu'>
<h1 className='logo'> <h1 className='logo'>
<img src={logo} width='80' /> <img src={logo} width='80' />
@ -283,12 +273,6 @@ const ControlMain = () => {
<div className='data-list-box'> <div className='data-list-box'>
<div className='data-list'> <div className='data-list'>
<span>드론</span> <span>드론</span>
{/* <span>{controlGpList ? controlGpList.length : 0}</span> */}
{/* <span>
{controlGpCountDrone?.length > 0
? controlGpCountDrone?.length
: 0}
</span> */}
<span>{droneCount}</span> <span>{droneCount}</span>
</div> </div>
<div className='data-list'> <div className='data-list'>
@ -297,7 +281,6 @@ const ControlMain = () => {
</div> </div>
<div className='data-list'> <div className='data-list'>
<span>항공기</span> <span>항공기</span>
{/* <span>2147대</span> */}
<span> <span>
{controlGpCountFlight?.length > 0 {controlGpCountFlight?.length > 0
? controlGpCountFlight?.length ? controlGpCountFlight?.length
@ -307,30 +290,10 @@ const ControlMain = () => {
</div> </div>
</Card> </Card>
</div> </div>
{/* <div className='main-data-box flight-data'>
<Card>
<div className='data-box-header'>
<span className='box-ti'>화재경보</span>
</div>
<div className='data-list-box'>
<div className='data-list' style={{ cursor: 'pointer' }}>
<span onClick={() => handlerDrawType('CIRCLE')}>
화재구역설정
</span>
</div>
<div className='data-list' style={{ cursor: 'pointer' }}>
<span onClick={() => handlerDrawType('RESET')}>초기화</span>
</div>
</div>
</Card>
</div> */}
</div> </div>
{oepnReportList ? ( {oepnReportList ? (
<ControlReportList <ControlReportList setOpenReportList={setOpenReportList} />
// openReportDetailParam={openReportDetailParam}
setOpenReportList={setOpenReportList}
/>
) : ( ) : (
<div /> <div />
)} )}
@ -339,11 +302,6 @@ const ControlMain = () => {
) : ( ) : (
<div /> <div />
)} )}
{/* {openWeatherList ? (
<WeatherList setOpenWeatherList={setOpenWeatherList} />
) : (
<div />
)} */}
{openAlarmList ? ( {openAlarmList ? (
<ControlAlarmList setOpenAlarmList={setOpenAlarmList} /> <ControlAlarmList setOpenAlarmList={setOpenAlarmList} />

83
src/views/control/report/ControlReportDetail.js

@ -1,45 +1,51 @@
import moment from 'moment'; import moment from 'moment';
import React, { useMemo } from 'react';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { X } from 'react-feather'; import { X } from 'react-feather';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import drone_img from '../../../assets/images/drone.jpg'; import drone_img from '../../../assets/images/drone.jpg';
import uam_img from '../../../assets/images/uam_img.jpg'; import uam_img from '../../../assets/images/uam_img.jpg';
import drone_yellow from '../../../assets/images/drone_yellow.png'; import drone_yellow from '../../../assets/images/drone_yellow.png';
import { IMG_PATH } from '../../../configs/constants';
import { objectUnClickAction } from '../../../modules/control/map/actions/controlMapActions';
import { import {
GET_ARCTFT_TYPE_CD, GET_ARCTFT_TYPE_CD,
GET_WGHT_TYPE_CD GET_WGHT_TYPE_CD
} from '../../../utility/CondeUtil'; } from '../../../utility/CondeUtil';
import { import { controlGpLogAction } from '../../../modules/control/gp';
controlGpLogAction,
controlweatherAction
} from '../../../modules/control/gp';
import ControlAlarmDetail from '../alarm/ControlAlarmDetail'; import ControlAlarmDetail from '../alarm/ControlAlarmDetail';
import axios from '../../../modules/utils/customAxiosUtil';
import { import {
Navigation2, Navigation2,
Search,
Compass, Compass,
Sun, Sun,
Cloud, Cloud,
CloudRain, CloudRain,
CloudSnow CloudSnow
} from 'react-feather'; } from 'react-feather';
import { WHEATHER_KEY } from '../../../configs/constants';
import { Table } from 'reactstrap'; import { Table } from 'reactstrap';
const ControlReportDetail = props => { const ControlReportDetail = props => {
const dispatch = useDispatch(); const dispatch = useDispatch();
// 비정상상황 모달
const [historyModal, setHistoryModal] = useState(false); const [historyModal, setHistoryModal] = useState(false);
// 기체 상세정보
const { controlGpDetail, controlDetail } = useSelector( const { controlGpDetail, controlDetail } = useSelector(
state => state.controlGpDtlState state => state.controlGpDtlState
); );
//const { controlWheather } = useSelector(state => state.ControlGpWeatherState);
// 비정상상황 히스토리 내역
const { controlGpWarnLog } = useSelector(state => state.controlGpLogState); const { controlGpWarnLog } = useSelector(state => state.controlGpLogState);
function weathericon() { // 해당 드론의 비정상상황 알림내역 불러옴
useEffect(() => {
if (historyModal) {
if (controlGpDetail) {
dispatch(controlGpLogAction.request({ id: controlGpDetail.controlId }));
}
}
}, [historyModal]);
// 날씨 아이콘 표출
const weathericon = () => {
if (controlDetail) { if (controlDetail) {
let wheatherDetail = controlDetail.items.item; let wheatherDetail = controlDetail.items.item;
let skyDetail = wheatherDetail[6].fcstValue; let skyDetail = wheatherDetail[6].fcstValue;
@ -51,16 +57,9 @@ const ControlReportDetail = props => {
return <Sun />; return <Sun />;
} else return <Cloud />; } else return <Cloud />;
} }
} };
useEffect(() => {
if (historyModal) {
if (controlGpDetail) {
dispatch(controlGpLogAction.request({ id: controlGpDetail.controlId }));
}
}
}, [historyModal]);
// 상세정보에 내역이 없으면 -로 표출
const nullMessage = val => { const nullMessage = val => {
if (val) { if (val) {
return val; return val;
@ -68,6 +67,7 @@ const ControlReportDetail = props => {
return '-'; return '-';
} }
}; };
return ( return (
<div className='left-layer'> <div className='left-layer'>
<div className='layer-content'> <div className='layer-content'>
@ -76,7 +76,6 @@ const ControlReportDetail = props => {
<button <button
className='btn-icon' className='btn-icon'
color='primary' color='primary'
// onClick={() => props.setOpenReportDetail(false)}
onClick={() => props.handlerClose()} onClick={() => props.handlerClose()}
> >
<X size={20} /> <X size={20} />
@ -101,11 +100,6 @@ const ControlReportDetail = props => {
: controlGpDetail?.objectId} : controlGpDetail?.objectId}
</div> </div>
<div className='drone-img'> <div className='drone-img'>
{/* {controlDetail?.imageUrl ? (
<img src={IMG_PATH + controlDetail?.imageUrl} />
) : (
<img src={drone_img} />
)} */}
{controlGpDetail?.objectId.includes('UAM') ? ( {controlGpDetail?.objectId.includes('UAM') ? (
<img src={uam_img} /> <img src={uam_img} />
) : ( ) : (
@ -138,18 +132,6 @@ const ControlReportDetail = props => {
{GET_ARCTFT_TYPE_CD(controlDetail?.arcrftTypeCd)} {GET_ARCTFT_TYPE_CD(controlDetail?.arcrftTypeCd)}
</div> </div>
</dt> </dt>
{/* <dt>
<div className='list-left-txt'>배터리 잔량</div>
<div className='list-right-txt'>
{controlGpDetail?.betteryLevel} %
</div>
</dt>
<dt>
<div className='list-left-txt'>배터리 전압</div>
<div className='list-right-txt'>
{controlGpDetail?.betteryVoltage} volt
</div>
</dt> */}
</dl> </dl>
</div> </div>
</div> </div>
@ -177,12 +159,6 @@ const ControlReportDetail = props => {
: '-'} : '-'}
</div> </div>
</dt> </dt>
{/* <dt>
<div className='list-left-txt'>현재위치</div>
<div className='list-right-txt'>
인천광역시 부평구 안남로 272
</div>
</dt> */}
<dt> <dt>
<div className='list-left-txt'>속도</div> <div className='list-left-txt'>속도</div>
<div className='list-right-txt'> <div className='list-right-txt'>
@ -207,25 +183,12 @@ const ControlReportDetail = props => {
: '-'} : '-'}
</div> </div>
</dt> </dt>
{/* <dt>
<div className='list-left-txt'>비행거리</div>
<div className='list-right-txt'>
{nullMessage(controlGpDetail?.moveDistance)}{' '}
{controlGpDetail?.moveDistanceType}
</div>
</dt> */}
<dt> <dt>
<div className='list-left-txt'>헤딩 방위각</div> <div className='list-left-txt'>헤딩 방위각</div>
<div className='list-right-txt'> <div className='list-right-txt'>
{nullMessage(controlGpDetail?.heading)} {nullMessage(controlGpDetail?.heading)}
</div> </div>
</dt> </dt>
{/* <dt>
<div className='list-left-txt'>상태</div>
<div className='list-right-txt'>
{nullMessage(controlGpDetail?.dronStatus)}
</div>
</dt> */}
<dt> <dt>
<div className='list-left-txt'>위치정보 수신 시간</div> <div className='list-left-txt'>위치정보 수신 시간</div>
<div className='list-right-txt'> <div className='list-right-txt'>
@ -248,10 +211,6 @@ const ControlReportDetail = props => {
<div className='layer-content-box'> <div className='layer-content-box'>
<div className='layer-content-info'> <div className='layer-content-info'>
<dl> <dl>
{/* <dt>
<div className='list-left-txt'>소속기관</div>
<div className='list-right-txt'>팔네트웍스</div>
</dt> */}
<dt> <dt>
<div className='list-left-txt'>담당자 이름</div> <div className='list-left-txt'>담당자 이름</div>
<div className='list-right-txt'> <div className='list-right-txt'>

45
src/views/control/report/ControlReportList.js

@ -3,27 +3,24 @@ import { useState } from 'react';
import { Search, X } from 'react-feather'; import { Search, X } from 'react-feather';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { Badge, Button, Input, InputGroup } from 'reactstrap'; import { Badge, Button, Input, InputGroup } from 'reactstrap';
import { import { controlGpDtlAction } from '../../../modules/control/gp';
controlGpDtlAction,
controlGpFlightPlanAction
} from '../../../modules/control/gp';
import { objectClickAction } from '../../../modules/control/map/actions/controlMapActions'; import { objectClickAction } from '../../../modules/control/map/actions/controlMapActions';
import { NavLink } from 'react-router-dom';
const ControlReportList = props => { const ControlReportList = props => {
const dispatch = useDispatch();
// 비행중인 기체 정보 리스트
const { controlGpList } = useSelector(state => state.controlGpState); const { controlGpList } = useSelector(state => state.controlGpState);
const [filterId, setFilterId] = useState('');
const dispatch = useDispatch(); // 검색한 식별번호
const [filterId, setFilterId] = useState('');
// 기체 상세정보 표출
const handlerDetail = item => { const handlerDetail = item => {
dispatch(objectClickAction(item.controlId)); dispatch(objectClickAction(item.controlId));
dispatch(controlGpDtlAction.request(item.controlId)); dispatch(controlGpDtlAction.request(item.controlId));
// dispatch(controlGpFlightPlanAction.request(item.objectId));
}; };
// useEffect(() => {}, [filterId]);
return ( return (
<div className='left-layer'> <div className='left-layer'>
<div className='layer-content'> <div className='layer-content'>
@ -97,34 +94,6 @@ const ControlReportList = props => {
{item.dronStatus ? item.dronStatus : '-'} {item.dronStatus ? item.dronStatus : '-'}
</div> </div>
</dt> </dt>
{item.objectId.includes('NAMWON') ? (
<dt>
<div className='data-list'>
<NavLink
to='/system/multi/views'
target='_blank'
style={{ width: '100%' }}
>
<span>실시간 영상보기</span>
</NavLink>
</div>
</dt>
) : (
<></>
)}
{/* <dt>
<div className='data-list'>
<a
href='http://palnet.co.kr/'
target='_blank'
style={{ width: '100%' }}
>
<span>실시간 영상보기</span>
</a>
</div>
</dt> */}
</dl> </dl>
</div> </div>
); );

113
src/views/control/setting/ControlSetting.js

@ -4,15 +4,17 @@ import { Button, ButtonGroup, CustomInput } from 'reactstrap';
import * as Actions from '../../../modules/menu/actions/menuAction'; import * as Actions from '../../../modules/menu/actions/menuAction';
import { import {
areaClickAction, areaClickAction,
mapTypeChangeAction, mapTypeChangeAction
sensorClickAction
} from '../../../modules/control/map/actions/controlMapActions'; } from '../../../modules/control/map/actions/controlMapActions';
const ControlSetting = props => { const ControlSetting = props => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const history = useHistory(); const history = useHistory();
// 지도, 지도타입, 공역 타입 컨트롤
const mapControl = useSelector(state => state.controlMapReducer); const mapControl = useSelector(state => state.controlMapReducer);
// 지도 유형 변경
const handlerMapType = val => { const handlerMapType = val => {
if (val === mapControl.mapType) return; if (val === mapControl.mapType) return;
if (val === 'TERRAIN') { if (val === 'TERRAIN') {
@ -35,24 +37,16 @@ const ControlSetting = props => {
dispatch(mapTypeChangeAction(val)); dispatch(mapTypeChangeAction(val));
}; };
// 공역 표출
const handlerAreaClick = val => { const handlerAreaClick = val => {
dispatch(areaClickAction(val)); dispatch(areaClickAction(val));
}; };
const handlerSensorClick = (val, isChecked) => {
if (isChecked) {
dispatch(sensorClickAction(val));
} else {
dispatch(sensorClickAction(''));
}
};
return ( return (
<div className=''> <div className=''>
<div className='layer-content'> <div className='layer-content'>
<div className='layer-ti'> <div className='layer-ti'>
<h4>지도유형</h4> <h4>지도유형</h4>
{/* <button className='btn-icon' outline color='primary'><X size={20} /></button> */}
</div> </div>
<div className='map-btn'> <div className='map-btn'>
<ButtonGroup> <ButtonGroup>
@ -270,103 +264,8 @@ const ControlSetting = props => {
</a> </a>
</div> </div>
</div> </div>
{/* <div className='layer-content'>
<div className='layer-ti'>
<h4>환경지표</h4>
</div>
<div className='layer-content-box'>
<div className='layer-content-info layer-switch-list'>
<dl>
<dt>
<div className='list-left-txt'>
<span className='dot-icon color-red'></span>(DUST)
</div>
<div className='list-right-txt'>
<CustomInput
onChange={e => handlerSensorClick('dust', e.target.checked)}
className='custom-control-primary'
type='switch'
id='sensorDust'
name='sensorDust'
inline
checked={mapControl.sensor === 'dust'}
// defaultChecked={mapControl.sensor === 'dust'}
/>
</div>
</dt>
<dt>
<div className='list-left-txt'>
<span className='dot-icon color-pink'></span>(O3)
</div>
<div className='list-right-txt'>
<CustomInput
onChange={e => handlerSensorClick('o3', e.target.checked)}
className='custom-control-primary'
type='switch'
id='sensorO3'
name='sensorO3'
inline
checked={mapControl.sensor === 'o3'}
// defaultChecked={mapControl.sensor === 'o3'}
/>
</div>
</dt>
<dt>
<div className='list-left-txt'>
<span className='dot-icon color-orange'></span>(No2)
</div>
<div className='list-right-txt'>
<CustomInput
onChange={e => handlerSensorClick('no2', e.target.checked)}
className='custom-control-primary'
type='switch'
id='sensorNo2'
name='sensorNo2'
inline
checked={mapControl.sensor === 'no2'}
// defaultChecked={mapControl.sensor === 'no2'}
/>
</div>
</dt>
<dt>
<div className='list-left-txt'>
<span className='dot-icon color-brown'></span>(Co)
</div>
<div className='list-right-txt'>
<CustomInput
onChange={e => handlerSensorClick('co', e.target.checked)}
className='custom-control-primary'
type='switch'
id='sensorCo'
name='sensorCo'
inline
checked={mapControl.sensor === 'co'}
// defaultChecked={mapControl.sensor === 'co'}
/>
</div>
</dt>
<dt>
<div className='list-left-txt'>
<span className='dot-icon color-purple'></span>(So2)
</div>
<div className='list-right-txt'>
<CustomInput
onChange={e => handlerSensorClick('so2', e.target.checked)}
className='custom-control-primary'
type='switch'
id='sensorSo2'
name='sensorSo2'
inline
checked={mapControl.sensor === 'so2'}
// defaultChecked={mapControl.sensor === 'so2'}
/>
</div>
</dt>
</dl>
</div>
</div>
</div> */}
</div> </div>
); );
}; };
export default ControlSetting; export default ControlSetting;

Loading…
Cancel
Save