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. 96
      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 => {
const [target, setTarget] = useState(null);
// 로딩 상태
const { loading } = useSelector(state => state.loadingReducer);
// 스크롤 끝 감지 데이터 추가
const onIntersect = useCallback(
([entry], observer) => {
if (entry.isIntersecting) {
@ -19,6 +21,7 @@ export const AnalysisSimulationInfo = props => {
[props.handlerPageList]
);
// 무한 스크롤
useEffect(() => {
let observer;
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 AnalysisSimulatorSlider from './AnalysisSimulatorSlider';
export const AnalysisSimulationDetail = props => {
// 슬라이드 진행 방향
const [isRtl, setIsRtl] = useRTL();
return (

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

@ -9,12 +9,15 @@ import { Threebox } from 'threebox-plugin';
import gimPo from '../../map/geojson/gimpoAirportAirArea.json';
export const AnalysisSimulationMap = props => {
// 지도
const mapContainer = useRef(null);
// mabboxgl 지도 초기화
useEffect(() => {
mapBoxMapInit();
}, []);
// mabboxgl 지도 초기화 함수
const mapBoxMapInit = () => {
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';
export const AnalysisSimulationMarker = props => {
// 마커 css
const el = document.createElement('div');
el.className = 'marker';
el.style.width = '30px';
@ -10,6 +11,7 @@ export const AnalysisSimulationMarker = props => {
el.style.textAlign = 'center';
el.style.backgroundImage = `url(${DronIcon})`;
// 마커 경로 담기
useEffect(() => {
if (props.selMarker && props.selMarker.setMap) {
props.selMarker.setMap(null);
@ -30,6 +32,7 @@ export const AnalysisSimulationMarker = props => {
}
}, [props.data]);
// 매 초마다 경로 이동
useEffect(() => {
if (props.isPlay) {
if (props.marker) {
@ -47,6 +50,7 @@ export const AnalysisSimulationMarker = props => {
}
}, [props.info]);
// 지도 드론 표출
const addMarkers = (position, id) => {
// 이미 지정된 마커 제거
if (props.marker) {

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

@ -1,8 +1,10 @@
import { useEffect } from 'react';
export const AnalysisSimulationPolyline = props => {
// 폴리라인 경로 담는 변수
const polylinePath = [];
// 기존 저장된 경로 삭제
useEffect(() => {
// 기존 폴리라인 삭제 처리
if (props.selPolyline && props.selPolyline.setMap) {
@ -19,6 +21,7 @@ export const AnalysisSimulationPolyline = props => {
}
}, [props.data]);
// 경로 그리기
const addPolyline = () => {
if (props.data && props.map) {
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';
export const AnalysisSimulationReport = props => {
// 식별번호
const [filterId, setFilterId] = useState('');
return (

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

@ -24,6 +24,7 @@ const AnalysisSimulatorSlider = ({
return timeString;
}
// 슬라이더 값이 바뀔 때마다 실행되는 함수
const colorOptions = {
start: [playCount ? playCount : 0],
// connect: true,
@ -64,7 +65,6 @@ const AnalysisSimulatorSlider = ({
direction
};
useEffect(() => {}, [playCount]);
return (
<div className='simulation-slider'>
{/* <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초과'
}
};
// 로그인 정보
const { user } = useSelector(state => state.authState);
// 약관 정보
const { termsList } = useSelector(state => state.accountState);
// 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() {
const dispatch = useDispatch();
// 상세보기 모달
const [isAnimation, setIsAnimation] = useState(false);
// Laanc 승인 신청 목록
const { laancSearchData, laancDetail } = useSelector(
state => state.laancState
);
// 로딩 상태
const { loading } = useSelector(state => state.loadingReducer);
// Laanc 승인 신청 목록 조회
@ -169,6 +171,7 @@ export default function LaancGrid() {
}
];
// PDF 다운로드
const handlerPdfDownload = pdf => {
if (pdf) {
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 }) {
const dispatch = useDispatch();
// 날짜 데이터
const [date, setDate] = useState({
createStDate: moment().subtract(0, '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 mapboxgl from 'mapbox-gl';
import threebox from 'threebox-plugin';
import MapboxLanguage from '@mapbox/mapbox-gl-language';
import MapboxDraw from '@mapbox/mapbox-gl-draw';
import {
@ -10,18 +13,6 @@ import {
SimpleSelectMode
} from 'mapbox-gl-draw-circle';
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 {
InitFeature,
handlerCreatePoint,
@ -32,20 +23,26 @@ import {
layerPolyline,
layerWayPoint
} 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 geoJson from '../../map/geojson/airArea.json';
import threebox from 'threebox-plugin';
import { FeatureAirZone } from '../../map/mapbox/feature/FeatureAirZone';
import { WeatherContainer } from '../../../containers/basis/flight/plan/WeatherContainer';
import { initFlightBas } from '../../../modules/laanc/models/laancModels';
import * as LaancAction from '../../../modules/laanc/actions/laancActions';
import LaancAreaMap from './LaancAreaMap';
import LaancDrawModal from './LaancDrawModal';
const initialAddData = {
isAddable: false,
isViewAdd: false,
overAdd: false
};
import { handlerCreateAirSpace } from './LaancComn';
export default function FlightArea({
centeredModal,
@ -61,40 +58,65 @@ export default function FlightArea({
></script>;
const dispatch = useDispatch();
const { areaCoordList } = useSelector(state => state.flightState);
// 비행구역 타입 및 공역 타입
const mapControl = useSelector(state => state.controlMapReducer);
const mapContainer = useRef(null);
// 비행구역 정보 저장
const { areaCoordList } = useSelector(state => state.flightState);
// 지도
const [mapObject, setMapObject] = useState();
const [drawObj, setDrawObj] = useState();
const mapContainer = useRef(null);
// 지도 로드 여부
const [isMapLoad, setIsMapLoad] = useState(false);
// 비행구역 그리기
const [drawObj, setDrawObj] = useState();
// 미니맵 레이어
const [previewLayer, setPreviewLayer] = useState();
// 날씨 모달
const [formModal, setFormModal] = useState(false);
// 비행구역 설정 관련 모달
const [modal, setModal] = useState({
title: '',
desc: '',
isOpen: 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 [saveElev, setSaveElev] = useState();
//날씨 임시 데이터
//날씨 위치 데이터
const [wheather, setWheather] = useState([]);
// 미니맵에 표출되는 비행구역 정보
const previewGeo = {
type: 'FeatureCollection',
features: []
};
// 지도 초기 셋팅
useEffect(() => {
handlerMapInit();
}, []);
// 미니맵에 비행구역 표출 및 날씨 정보 저장
useEffect(() => {
if (areaCoordList) {
const area = areaCoordList[0];
@ -105,78 +127,12 @@ export default function FlightArea({
}
}, [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 = () => {
setModal(!modal);
};
// 비행구역 타입 변경 시 그리기 모드 상태일 때 에러 표출
const handlerDrawType = val => {
if (drawObj.getMode().includes('draw')) {
setModal({
@ -195,6 +151,7 @@ export default function FlightArea({
}
};
// laanc계획서 비행구역 저장버튼 클릭 시 비행구역 정보 저장
const handlerSave = async () => {
if (areaCoordList) {
console.log('save');
@ -208,29 +165,28 @@ export default function FlightArea({
setCenteredModal(false);
dispatch(AREA_DETAIL_LIST_SAVE(resultAreaDetail));
} else {
alert('아무것도 작성 안함');
}
};
// 날씨 handler
// 날씨 모달 표출
const handlerWeather = () => {
setFormModal(!formModal);
};
// 지도 초기 셋팅
const handlerMapInit = () => {
mapboxgl.accessToken = MAPBOX_TOKEN;
const map = new mapboxgl.Map({
container: 'preview', // container ID
style: 'mapbox://styles/mapbox/streets-v12', // style URL
center: [126.612647, 37.519893], // starting position [lng, lat]
// zoom: !areaCoordList ? 14 : bufferZoom.bufferzoom, // starting zoom
container: 'preview',
style: 'mapbox://styles/mapbox/streets-v12',
center: [126.612647, 37.519893],
zoom: 15,
antialias: true,
attributionControl: false
});
// 비행구역 상세맵 draw 정보 셋팅
const draw = new MapboxDraw({
displayControlsDefault: false,
userProperties: true,
@ -243,16 +199,14 @@ export default function FlightArea({
simple_select: SimpleSelectMode
},
styles: [
// line stroke
{
// polyline
id: 'gl-draw-line',
type: 'line',
filter: [
'all',
['==', '$type', 'LineString'],
['!=', 'mode', 'static']
// ['==', 'meta', 'feature'],
// ['==', 'active', 'false']
],
layout: {
'line-cap': 'round',
@ -264,28 +218,8 @@ export default function FlightArea({
'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',
type: 'fill',
filter: ['all', ['==', '$type', 'Polygon'], ['!=', 'mode', 'static']],
@ -295,18 +229,7 @@ export default function FlightArea({
'fill-opacity': 0.1
}
},
// polygon mid points
{
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
// polygon outline
{
id: 'gl-draw-polygon-stroke-active',
type: 'line',
@ -321,8 +244,8 @@ export default function FlightArea({
'line-width': 2
}
},
// vertex point halos
{
// vertex point halos
id: 'gl-draw-polygon-and-line-vertex-halo-active',
type: 'circle',
filter: [
@ -333,11 +256,11 @@ export default function FlightArea({
],
paint: {
'circle-radius': 8,
'circle-color': '#fff'
'circle-color': '#ffffff'
}
},
// vertex points
{
// vertex points
id: 'gl-draw-polygon-and-line-vertex-active',
type: 'circle',
filter: [
@ -359,7 +282,6 @@ export default function FlightArea({
const language = new MapboxLanguage();
map.addControl(language);
// map.addControl(draw);
const tb = (window.tb = new threebox.Threebox(
map,
@ -396,7 +318,7 @@ export default function FlightArea({
}
});
handlerCreateAirSpace(map);
handlerCreateAirSpace(map, mapControl);
// 미니맵 표출
map.addSource('preview', {
@ -417,6 +339,7 @@ export default function FlightArea({
dispatch(mapInitAction(map));
};
// 저장된 비행구역 미니맵에 표출
const handlerPreviewDraw = () => {
if (areaCoordList) {
const areas = areaCoordList[0];
@ -427,7 +350,6 @@ export default function FlightArea({
let fitZoomPaths = [];
// 기존
if (areas.areaType) {
if (areas.areaType === 'CIRCLE') {
const radius = areas.bufferZone;
@ -478,12 +400,8 @@ export default function FlightArea({
//지도 줌 좌표 설정
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);
mapObject.setPaintProperty('waypoint', 'circle-radius', 10);
@ -491,12 +409,13 @@ export default function FlightArea({
}
const coordValue = [];
const coord = paths?.map(coords => {
paths?.map(coords => {
coordValue.push({
lat: coords[1],
lon: coords[0]
});
});
if (page === 1) {
naver.maps.Service.reverseGeocode(
{
@ -527,7 +446,6 @@ export default function FlightArea({
name: 'latlon',
value: coordValue
});
//스텝1에 반경도 글씨가 바뀌어야 함...!!
handleChange({
type: 'area',
name: 'bufferZone',
@ -537,38 +455,33 @@ export default function FlightArea({
}
};
// 비행구역 추가 버튼 클릭 시
const handlerAddClick = () => {
if (!addData.isAddable || !addData.overAdd) {
handlerAddChange('isAddable', true);
const obj = drawObj
.getAll()
.features.filter(obj => obj.properties.id !== 'BUFFER');
// handlerDrawType(obj[0].properties.id);
}
};
// 비행구역 추가 관련 상태 변경
const handlerAddChange = (key, val) => {
// const [addData, setAddData] = useState({
// isAddalbe: false,
// isViewAdd: false,
// overAdd: false
// })
setAddData(prev => ({
...prev,
[key]: val
}));
};
// 비행구역 저장 가능 유무 체크
const handlerSaveCheck = save => {
setIsSaveable(save);
};
// 비행구역 데이터 초기화
const handlerInitCoordinates = () => {
const init = initFlightBas.initDetail.areaList.concat();
dispatch(AREA_COORDINATE_LIST_SAVE(init));
};
// 비행구역 고도 저장
const handlerSaveElev = elev => {
setSaveElev(elev);
};
@ -604,7 +517,6 @@ export default function FlightArea({
</ModalHeader>
<ModalBody>
<LaancAreaMap
centeredModal={centeredModal}
mapContainer={mapContainer}
drawObj={drawObj}
handlerInitCoordinates={handlerInitCoordinates}
@ -684,7 +596,6 @@ export default function FlightArea({
dispatch(LaancAction.LAANC_ALTITUDE.success(saveElev));
}}
>
{/* 닫기 */}
저장
</Button>
</div>

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

@ -3,7 +3,7 @@ import mapboxgl from 'mapbox-gl';
import threebox from 'threebox-plugin';
import MapboxLanguage from '@mapbox/mapbox-gl-language';
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 { Card, CardBody } from 'reactstrap';
import { initFlightBas } from '../../../modules/basis/flight/models/basisFlightModel';
@ -16,22 +16,15 @@ import { mapInitAction } from '../../../modules/control/map/actions/controlMapAc
import {
FormattingCoord,
handlerFitBounds,
handlerGetCircleCoord,
layerBuffer,
layerGuideLine,
layerPolygon,
layerPolyline,
layerWayPoint
handlerGetCircleCoord
} from '../../../utility/DrawUtil';
import flatGimpo from '../../map/geojson/flatGimpoAirportAirArea.json';
import gimpo from '../../map/geojson/gimpoAirportAirArea.json';
import geoJson from '../../map/geojson/airArea.json';
import { FeatureAirZone } from '../../map/mapbox/feature/FeatureAirZone';
import LaancMapSearch from './LaancMapSearch';
import { LaancDrawControl } from './LaancDrawControl';
import { handlerCreateAirSpace } from './LaancComn';
export default function LaancAreaMap({
centeredModal,
mapContainer,
drawObj,
handlerSaveCheck,
@ -43,42 +36,38 @@ export default function LaancAreaMap({
setModal
}) {
const dispatch = useDispatch();
// 비행구역 타입 및 공역 타입
const mapControl = useSelector(state => state.controlMapReducer);
const { areaCoordList } = useSelector(state => state.flightState);
const [mapObject, setMapObject] = useState();
const [isMapLoad, setIsMapLoad] = useState(false);
const [mode, setMode] = useState();
// 비행구역 정보 저장
const { areaCoordList } = useSelector(state => state.flightState);
// 비행구역 초기값 포함 정보 저장
const [mapAreaCoordList, setMapAreaCoordList] = useState(
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 detailGeo = useMemo(() => {
return {
type: 'FeatureCollection',
features: []
};
}, []);
// 좌표 정보 마우스 드래그
const scrollRef = useRef(null);
const [isDrag, setIsDrag] = useState(false);
const [startX, setStartX] = useState();
// 지도 초기 셋팅
useEffect(() => {
handlerMapInit();
}, []);
useEffect(() => {
setMode(mapControl.drawType);
}, [mapControl.drawType]);
// 첫 비행구역 생성 or 저장했던 비행구역 다시 열기 시 비행구역에 화면 맞추어서 zoom
useEffect(() => {
if (areaCoordList && mapObject) {
if (
@ -111,75 +100,7 @@ export default function LaancAreaMap({
}
}, [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 = () => {
mapboxgl.accessToken = MAPBOX_TOKEN;
@ -187,7 +108,6 @@ export default function LaancAreaMap({
container: 'detail', // container ID
style: 'mapbox://styles/mapbox/streets-v12', // style URL
center: [126.612647, 37.519893], // starting position [lng, lat]
// zoom: !areaCoordList ? 14 : bufferZoom.bufferzoom, // starting zoom
zoom: 15,
antialias: true,
attributionControl: false
@ -238,31 +158,15 @@ export default function LaancAreaMap({
}
});
map.addSource('detail', {
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);
handlerCreateAirSpace(map, mapControl);
setIsMapLoad(true);
// const detail = map.getSource('detail');
// if (detail) setDetailLayer(detail);
});
setMapObject(map);
dispatch(mapInitAction(map));
};
// const handlerInitCoordinates = () => {
// const init = initFlightBas.initDetail.areaList.concat();
// dispatch(AREA_COORDINATE_LIST_SAVE(init));
// };
// areaInfo를 areaList 형식으로 반환
const handlerAreaInfoToAreaList = areaInfo => {
const initAreaList = initFlightBas.initDetail.areaList.concat();
const coordList = [];
@ -287,10 +191,10 @@ export default function LaancAreaMap({
return areaList;
};
// 비행관제구역 체크 및 버퍼 생성
const handlerCoordinates = areaInfo => {
const areaList = handlerAreaInfoToAreaList(areaInfo);
// dispatch(LaancAction.LAANC_ALTITUDE.request(areaList));
dispatch(LaancAction.LAANC_VALID_AREA.request(areaList));
if (areaInfo.areaType === 'LINE' || areaInfo.areaType === 'POLYGON') {
dispatch(FLIGHT_PLAN_AREA_BUFFER_LIST.request(areaList));
@ -299,6 +203,7 @@ export default function LaancAreaMap({
}
};
// 비행구역 설정 후 저장
const handlerConfirm = areaList => {
if (areaList === undefined) {
alert('영역을 설정해 주세요.');
@ -308,17 +213,14 @@ export default function LaancAreaMap({
dispatch(AREA_COORDINATE_LIST_SAVE(areaList));
};
// const handlerModal = () => {
// setModal(!modal);
// };
// 좌표 정보 마우스 드래그
// [좌표 정보] 마우스 다운 시 스크롤 준비
const onMouseDown = e => {
e.preventDefault();
setIsDrag(true);
setStartX(e.pageX + scrollRef.current.scrollLeft);
};
// [좌표 정보] 마우스 드래그로 스크롤 이동
const onMouseMove = e => {
if (isDrag) {
const { scrollWidth, clientWidth, scrollLeft } = scrollRef.current;
@ -333,6 +235,7 @@ export default function LaancAreaMap({
}
};
// [좌표 정보] onMouseMove 이벤트 빈도 조절
const throttle = (func, ms) => {
let throttled = false;
return (...args) => {
@ -346,11 +249,13 @@ export default function LaancAreaMap({
};
};
// [좌표 정보] 마우스 업 시 스크롤 멈춤
const onMouseUp = e => {
e.preventDefault();
setIsDrag(false);
};
// [좌표 정보] 마우스 벗어날 시 스크롤 멈춤
const onMouseLeave = () => {
setIsDrag(false);
};
@ -455,21 +360,20 @@ export default function LaancAreaMap({
{isMapLoad && mapObject ? (
<>
<LaancDrawControl
handlerAddChange={handlerAddChange}
addData={addData}
drawObj={drawObj}
setModal={setModal}
mapboxgl={mapboxgl}
mapObject={mapObject}
setViewCoordObj={setViewCoordObj}
areaCoordList={mapAreaCoordList}
setSaveData={setSaveData}
setViewCoordObj={setViewCoordObj}
handlerConfirm={handlerConfirm}
handlerSaveElev={handlerSaveElev}
handlerAddChange={handlerAddChange}
handlerSaveCheck={handlerSaveCheck}
handlerCoordinates={handlerCoordinates}
handlerInitCoordinates={handlerInitCoordinates}
setSaveData={setSaveData}
handlerAreaInfoToAreaList={handlerAreaInfoToAreaList}
handlerSaveElev={handlerSaveElev}
/>
<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 { InfoModal } from '../../modal/InfoModal';
import { ErrorModal } from '../../modal/ErrorModal';
import { useDispatch, useSelector } from 'react-redux';
import {
@ -25,36 +24,42 @@ import Constants from 'mapbox-gl-draw-circle/node_modules/@mapbox/mapbox-gl-draw
export const LaancDrawControl = props => {
const dispatch = useDispatch();
const mapControl = useSelector(state => state.controlMapReducer);
const drawObj = props.drawObj;
// 지도
const mapObject = props.mapObject;
// 비행구역 타입
const { drawType } = useSelector(state => state.controlMapReducer);
// mapbox 기본 onClick 함수 저장
const originClickRef = useRef(null);
// 비행구역이 WayPoint일 때 bufferId
const [bufferId, setBufferId] = useState();
// 그리기모드 상태인지 유무 확인
const [isDrawDone, setIsDrawDone] = useState(false);
const [alertModal, setAlertModal] = useState({
isOpen: false,
title: '',
desc: ''
});
// 지도 렌더 횟수
const [number, setNumber] = useState(0);
// 에러 모달창 정보
const [isErrorModal, setIsErrorModal] = useState({
isOpen: false,
title: '',
desc: ''
});
const [number, setNumber] = useState(0);
// 비행구역 타입 변경에 따른 그리기모드 셋팅
useEffect(() => {
if (mapControl.drawType === 'DONE') {
// 구역 생성 후 바로 directMode
if (drawType === 'DONE') {
if (number !== 0) {
const obj = drawObj
.getAll()
.features.filter(o => o.properties.id !== 'BUFFER');
// 구역 생성 후 바로 directMode
if (obj.length > 0) {
drawObj.changeMode('direct_select', {
featureId: obj[obj.length - 1]?.id
@ -64,10 +69,9 @@ export const LaancDrawControl = props => {
} else {
drawInit();
}
}, [mapControl.drawType]);
const originClickRef = useRef(null);
}, [drawType]);
// mapbox 기본 함수 오버라이드 및 함수 중복 등록 방지
useEffect(() => {
if (mapObject) {
const DrawLineStringMode = MapboxDraw.modes.draw_line_string;
@ -75,6 +79,7 @@ export const LaancDrawControl = props => {
const DrawCircleMode = CircleMode;
const modeArr = [DrawLineStringMode, DrawPolygonMode, DrawCircleMode];
// 등록 함수 제거
const cleanUp = () => {
modeArr.forEach(m => {
m.onStop = null;
@ -154,6 +159,7 @@ export const LaancDrawControl = props => {
setNumber(number + 1);
}
// 컴포넌트 언마운트 시
return () => {
dispatch(drawTypeChangeAction('DONE'));
mapObject.off('draw.update', handlerUpdateSetting);
@ -162,10 +168,12 @@ export const LaancDrawControl = props => {
}
}, [mapObject]);
// 비행구역 데이터 지도에 다시 그려주기
useEffect(() => {
handlerPastDraw();
}, [props.areaCoordList]);
// 비행구역 설정을 올바르게 마쳤을 때 좌표 저장
useEffect(() => {
if (isDrawDone) {
props.handlerConfirm(props.areaCoordList);
@ -173,7 +181,7 @@ export const LaancDrawControl = props => {
}
}, [isDrawDone]);
// 클릭할 때마다 마커 찍어줌
// 클릭할 때마다 마커 표출
const handlerCustomOnClick = (state, e) => {
const mode = handlerReturnMode(drawObj.getMode());
const obj = state[mode?.toLowerCase()];
@ -181,7 +189,7 @@ export const LaancDrawControl = props => {
if (mode && obj) {
const feature = drawObj.get(obj.id);
if (!feature) {
console.log('2222222222');
// console.log('simple_select');
drawObj.changeMode('simple_select');
return;
}
@ -220,7 +228,7 @@ export const LaancDrawControl = props => {
}
};
// 도형 그리기 완료 시
// 비행구역 그리기 완료 시
const handlerFinishDraw = state => {
if (drawObj.getAll().features.length === 0) return;
@ -293,8 +301,7 @@ export const LaancDrawControl = props => {
});
}
} else {
console.log('333333333');
// 좌표가 찍히기도 전에 틀만 생성된 도형들 삭제
// console.log('좌표가 찍히기도 전에 틀만 생성된 도형들 삭제');
// if (mode === 'CIRCLE') {
// const obj = state.polygon;
// drawObj.delete(obj.id);
@ -307,7 +314,7 @@ export const LaancDrawControl = props => {
// 모든 비정상상황 체크
const handlerAbnormalityCheck = async data => {
// radius도 있음
// radius 존재함
const { coords, mode, id } = data;
const areaInfo = handlerSettingAreaInfo(coords, mode);
@ -411,7 +418,7 @@ export const LaancDrawControl = props => {
}
};
// 도형 수정 시
// 비행구역 수정 시
const handlerUpdateSetting = e => {
if (e.features[0]) {
const { geometry, properties, id } = e.features[0];
@ -482,7 +489,7 @@ export const LaancDrawControl = props => {
);
props.setViewCoordObj(viewCoordObj);
// 계속 20개 미만이라 overAdd false처리(임시)
// 계속 20개 미만이라 overAdd false처리
props.handlerAddChange('overAdd', false);
};
@ -565,12 +572,12 @@ export const LaancDrawControl = props => {
return true;
};
// 저장된 비행구역 데이터를 기반으로 지도에 다시 그려주기
const handlerPastDraw = () => {
if (props.areaCoordList) {
const objs = drawObj?.getAll().features;
const areas = props.areaCoordList;
if (areas.length > 0 && objs.length > 0) {
// areas -> 현재는 1개이지만 추후에 데이터가 바뀌면 여러개의 도형도 처리 가능!
areas.map((area, idx) => {
const paths = [];
area.coordList.forEach(coord => paths.push([coord.lon, coord.lat]));
@ -639,7 +646,7 @@ export const LaancDrawControl = props => {
}
objId = polygonId;
}
// 마커를 삭제하고 다시 그려주기
// 마커를 삭제하고 다시 그려
const data = {
coord: area.areaType === 'LINE' ? paths : [paths],
id: objId
@ -754,8 +761,9 @@ export const LaancDrawControl = props => {
return newObjId[0];
};
// 비행구역 기본셋팅
const drawInit = () => {
const mode = mapControl.drawType;
const mode = drawType;
if (mode !== 'DONE') {
if (!mode || mode === 'RESET') {
handlerResetMode();
@ -790,6 +798,7 @@ export const LaancDrawControl = props => {
drawObj.changeMode('simple_select');
};
// 그리기모드 셋팅
const handlerStartMode = mode => {
if (mode === 'LINE') {
drawObj.changeMode('draw_line_string');
@ -802,7 +811,6 @@ export const LaancDrawControl = props => {
return (
<>
<InfoModal modal={alertModal} setModal={setAlertModal} />
<ErrorModal modal={isErrorModal} setModal={setIsErrorModal} />
</>
);

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

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

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

@ -4,8 +4,13 @@ import { useState } from 'react';
import { flightPlanAPI } from '../../../modules/basis/flight/apis/basisFlightApi';
export default function LaancMapSearch({ mapObject }) {
// 검색어
const [query, setQuery] = useState('');
// 검색 결과
const [searchRes, setSearchRes] = useState([]);
// 검색 여부
const [isSearch, setIsSearch] = useState(false);
// 지역 검색
@ -15,6 +20,7 @@ export default function LaancMapSearch({ mapObject }) {
setSearchRes(res.data.items);
};
// 검색어 저장
const handlerSearchChange = e => {
const { name, value } = e.target;
@ -23,12 +29,14 @@ export default function LaancMapSearch({ mapObject }) {
}
};
// 지역 검색 후 엔터 키
const handlerSearchEnter = e => {
if (e.key == 'Enter') {
handlerSearchRes();
}
};
// 해당 좌표로 지도 이동
const handlerSearchCoord = (mapx, mapy) => {
const numberString = [mapx, mapy];
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 [numPages, setNumPages] = useState(null); // total
// total
const [numPages, setNumPages] = useState(null);
// 로그인 정보
const { user } = useSelector(state => state.authState);
// pdf 데이터
const { laancPdf } = useSelector(state => state.laancState);
// PDF 다운로드
@ -241,16 +246,16 @@ export default function LaacnStep3({
<span className='on'></span>
</li>
</ul>
<Button outline onClick={() => setCenteredModal2(!centeredModal2)}>
<Button outline onClick={() => setConfirmModal(!confirmModal)}>
완료
</Button>
<Modal
isOpen={centeredModal2}
toggle={() => setCenteredModal2(!centeredModal2)}
isOpen={confirmModal}
toggle={() => setConfirmModal(!confirmModal)}
className='modal-dialog-centered'
style={{ maxWidth: '650px', margin: '0 auto' }}
>
<ModalHeader toggle={() => setCenteredModal2(!centeredModal2)}>
<ModalHeader toggle={() => setConfirmModal(!confirmModal)}>
비행 승인 완료
</ModalHeader>
<ModalBody>

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

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

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

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

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

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

13
src/containers/laanc/LaancContainer.js

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

314
src/containers/laanc/LaancPlanContainer.js

@ -17,11 +17,20 @@ export default function LaancPlanContainer({
}) {
const dispatch = useDispatch();
// 비행 구역 정보
const { areaCoordList } = useSelector(state => state.flightState);
// 로그인 정보
const { user } = useSelector(state => state.authState);
// 관제권안 정보,고도 정보
const { laancArea, laancElev } = useSelector(state => state.laancState);
// laanc step
const [step, setStep] = useState(1);
// laanc 초기값
const [detailData, setDetailData] = useState(initFlightBas.initDetail);
// 비행 구역 보달
const [centeredModal, setCenteredModal] = useState(false);
// 모달
const [isErrorModal, setIsErrorModal] = useState({
isOpen: false,
title: '',
@ -54,6 +63,309 @@ export default function LaancPlanContainer({
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 승인 요청 취소 버튼 헨들러
const handlerLaancClose = () => {
setStep(1);

34
src/utility/DrawUtil.js

@ -2,6 +2,7 @@ import * as turf from '@turf/turf';
import mapboxgl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
// geojson Feature 형식으로 반환
export const InitFeature = (type, id) => {
return {
type: 'Feature',
@ -43,6 +44,13 @@ export const CalculateDistance = (mouse, center) => {
return distance;
};
// 미터 반환(m붙여서)
export const fromMetersToText = meters => {
meters = meters || 0;
const text = parseFloat(meters.toFixed(1)) + 'm';
return text;
};
// 두 좌표 간의 중간 지점 좌표 반환
export const handlerGetMidPoint = (dis1, dis2) => {
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도 좌표 반환
export const handlerGetCircleCoord = (center, distance) => {
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 => {
return {
id: 'polyline',

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

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

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

@ -1,40 +1,49 @@
import { useEffect, useState } from 'react';
import { X } from 'react-feather';
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 { Badge } from 'reactstrap';
const ControlAlarmList = props => {
const dispatch = useDispatch();
// 비정상상황 상세 히스토리 모달
const [historyModal, setHistoryModal] = useState(false);
const { controlGpList } = useSelector(state => state.controlGpState);
const { controlGpArcrftWarnList } = 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({
totalDroneCnt: 0,
totalWarnCnt: 0,
warnList: []
});
const handleWarnDetail = (cntrlId) => {
// 비정상상황 상세 히스토리 모달 표출
const handleWarnDetail = cntrlId => {
setHistoryModal(prev => !prev);
dispatch(controlGpLogAction.request({ id: cntrlId }));
}
};
// 비행중인 기체 클릭 시 비정상상황 사이드메뉴 닫힘
useEffect(() => {
if (isClickObject) {
props.setOpenAlarmList(false);
}
}, [objectId, isClickObject]);
}, [objectId, isClickObject])
// 비정상상황 기체 개수 계산
useEffect(() => {
if (controlGpArcrftWarnList) {
let totalWarnCnt = 0;
@ -50,10 +59,9 @@ const ControlAlarmList = props => {
totalDroneCnt: controlGpArcrftWarnList.length,
totalWarnCnt: totalWarnCnt,
warnList: controlGpArcrftWarnList
};
});
}
})
}
}, [controlGpArcrftWarnList]);
return (
@ -63,7 +71,6 @@ const ControlAlarmList = props => {
<h4>실시간 비정상 알림 정보</h4>
<button
className='btn-icon'
// outline
color='primary'
onClick={() => props.setOpenAlarmList(false)}
>
@ -100,67 +107,36 @@ const ControlAlarmList = props => {
<h4>알림 목록</h4>
</div>
{total?.warnList.map((warn, i) => {
const warnContent = warn.warnType === 'PLAN' ? '비행 경로 이탈' : '비정상 상황 발생'
const warnContent =
warn.warnType === 'PLAN' ? '비행 경로 이탈' : '비정상 상황 발생';
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'>
<dt>
<div className='list-ti'>
<div className='list-left-txt'>{warn.idntfNum}</div>
<div className='list-right-txt'>{warn.occurDt ? warn.occurDt : '-'}</div>
<div className='list-right-txt'>
{warn.occurDt ? warn.occurDt : '-'}
</div>
<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 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 className='list-left-txt'>
{warnContent ? warnContent : '-'}
</div>
<div className='list-ti'>
<div className='list-left-txt'>비행 경로 이탈</div>
<div className='list-right-txt'>22</div>
</div>
</dt>
</dl>
</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 className='list-right-txt'>
{warn.warnCount ? warn.warnCount : '-'}
</div>
<div className='list-ti'>
<div className='list-left-txt'>비행 경로 이탈</div>
<div className='list-right-txt'>10</div>
</div>
</dt>
</dl>
</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>
<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,
CloudRain,
CloudSnow,
Moon,
Grid
} from 'react-feather';
@ -19,7 +18,6 @@ import { AiOutlinePoweroff } from 'react-icons/ai';
import { IoAlertOutline } from 'react-icons/io5';
import { ReactComponent as DroneMenuIcon } from '../../../assets/images/drone_menu_icon.svg';
import { Card } from 'reactstrap';
import ControlAlarmNotice from '../alarm/ControlAlarmNotice';
import ControlReportList from '../report/ControlReportList';
import ControlReportDetail from '../report/ControlReportDetail';
import ControlAlarmList from '../alarm/ControlAlarmList';
@ -28,71 +26,51 @@ import WebsocketClient from '../../../components/websocket/WebsocketClient';
import { useDispatch, useSelector } from 'react-redux';
import { controlweatherAction } from '../../../modules/control/gp/actions/controlGpAction';
import * as Actions from '../../../modules/account/login/actions/authAction';
import {
ctrlDrawTypeChangeAction,
objectUnClickAction
} from '../../../modules/control/map/actions/controlMapActions';
import { objectUnClickAction } from '../../../modules/control/map/actions/controlMapActions';
const ControlMain = () => {
const dispatch = useDispatch();
const history = useHistory();
// 비행중인 기체 클릭 여부
const { isClickObject } = useSelector(state => state.controlMapReducer);
const { controlGpList, controlGroupAuthInfo } = useSelector(
state => state.controlGpState
);
// 비행중인 기체 정보
const { controlGpList } = useSelector(state => state.controlGpState);
// 기체 상세 정보
const { controlDetail, controlWheather } = useSelector(
state => state.controlGpDtlState
);
// 드론 기체 갯수 (드론 + uam)
const { controlGpCountDrone, controlGpCountFlight } = useSelector(
state => state.controlGpCountState
);
// pav박람회 -> uam, 드론 구별을 위한 임시 코드
// (이 작업으로 고도화 하려면 추후에 서버에서 uam타입을 새로 더 받아 작업해야 함)
// 드론, uam 기체 갯수
const [droneCount, setDroneCount] = useState(0);
const [uamCount, setUamCount] = useState(0);
// 비정상상황 여부
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 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(() => {
if (controlGpList) {
const warnGps = controlGpList.find(gps => {
@ -105,6 +83,7 @@ const ControlMain = () => {
}
}, [controlGpList]);
// 비행중인 기체 클릭 시 열려있는 사이드 메뉴 닫기
useEffect(() => {
if (isClickObject) {
setOpenReportList(false);
@ -112,6 +91,7 @@ const ControlMain = () => {
}
}, [isClickObject]);
// 드론, uam 기체 갯수 계산
useEffect(() => {
if (controlGpCountDrone) {
const uamCnt = controlGpCountDrone.filter(i =>
@ -126,19 +106,43 @@ const ControlMain = () => {
}
}, [controlGpCountDrone]);
const handlerClose = () => {
// 김포공항 날씨 API호출
useEffect(() => {
dispatch(controlweatherAction.request(rq));
}, []);
// 화면 왼쪽 사이드 메뉴 오픈 시 다른 메뉴 닫기
const openMenu = val => {
console.log(val, '--');
if (val === 'reportList') {
setOpenReportList(true);
setOpenAlarmList(false);
} else if (val === 'weatherList') {
setOpenReportList(false);
setOpenAlarmList(false);
} else if (val === 'alarmList') {
dispatch(objectUnClickAction());
setOpenReportList(false);
setOpenAlarmList(true);
setAlarm(false);
}
};
//날씨 API
const rq = {
nx: 37.558522,
ny: 126.793722
// 로그아웃
const handlerLogout = () => {
dispatch(Actions.logout.request());
};
useEffect(() => {
dispatch(controlweatherAction.request(rq));
}, []);
function weathericon() {
// 드론 상세정보 창 닫기
const handlerClose = () => {
setOpenReportList(true);
dispatch(objectUnClickAction());
};
// 김포공항 날씨 아이콘 설정
const weathericon = () => {
if (controlWheather) {
let wheatherDetail = controlWheather.items.item;
let skyDetail = wheatherDetail[6].fcstValue;
@ -150,24 +154,10 @@ const ControlMain = () => {
return <Sun 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 (
<>
<ControlAlarmNotice />
<div className='left-menu'>
<h1 className='logo'>
<img src={logo} width='80' />
@ -283,12 +273,6 @@ const ControlMain = () => {
<div className='data-list-box'>
<div className='data-list'>
<span>드론</span>
{/* <span>{controlGpList ? controlGpList.length : 0}</span> */}
{/* <span>
{controlGpCountDrone?.length > 0
? controlGpCountDrone?.length
: 0}
</span> */}
<span>{droneCount}</span>
</div>
<div className='data-list'>
@ -297,7 +281,6 @@ const ControlMain = () => {
</div>
<div className='data-list'>
<span>항공기</span>
{/* <span>2147대</span> */}
<span>
{controlGpCountFlight?.length > 0
? controlGpCountFlight?.length
@ -307,30 +290,10 @@ const ControlMain = () => {
</div>
</Card>
</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>
{oepnReportList ? (
<ControlReportList
// openReportDetailParam={openReportDetailParam}
setOpenReportList={setOpenReportList}
/>
<ControlReportList setOpenReportList={setOpenReportList} />
) : (
<div />
)}
@ -339,11 +302,6 @@ const ControlMain = () => {
) : (
<div />
)}
{/* {openWeatherList ? (
<WeatherList setOpenWeatherList={setOpenWeatherList} />
) : (
<div />
)} */}
{openAlarmList ? (
<ControlAlarmList setOpenAlarmList={setOpenAlarmList} />

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

@ -1,45 +1,51 @@
import moment from 'moment';
import React, { useMemo } from 'react';
import { useState, useEffect } from 'react';
import { X } from 'react-feather';
import { useDispatch, useSelector } from 'react-redux';
import drone_img from '../../../assets/images/drone.jpg';
import uam_img from '../../../assets/images/uam_img.jpg';
import drone_yellow from '../../../assets/images/drone_yellow.png';
import { IMG_PATH } from '../../../configs/constants';
import { objectUnClickAction } from '../../../modules/control/map/actions/controlMapActions';
import {
GET_ARCTFT_TYPE_CD,
GET_WGHT_TYPE_CD
} from '../../../utility/CondeUtil';
import {
controlGpLogAction,
controlweatherAction
} from '../../../modules/control/gp';
import { controlGpLogAction } from '../../../modules/control/gp';
import ControlAlarmDetail from '../alarm/ControlAlarmDetail';
import axios from '../../../modules/utils/customAxiosUtil';
import {
Navigation2,
Search,
Compass,
Sun,
Cloud,
CloudRain,
CloudSnow
} from 'react-feather';
import { WHEATHER_KEY } from '../../../configs/constants';
import { Table } from 'reactstrap';
const ControlReportDetail = props => {
const dispatch = useDispatch();
// 비정상상황 모달
const [historyModal, setHistoryModal] = useState(false);
// 기체 상세정보
const { controlGpDetail, controlDetail } = useSelector(
state => state.controlGpDtlState
);
//const { controlWheather } = useSelector(state => state.ControlGpWeatherState);
// 비정상상황 히스토리 내역
const { controlGpWarnLog } = useSelector(state => state.controlGpLogState);
function weathericon() {
// 해당 드론의 비정상상황 알림내역 불러옴
useEffect(() => {
if (historyModal) {
if (controlGpDetail) {
dispatch(controlGpLogAction.request({ id: controlGpDetail.controlId }));
}
}
}, [historyModal]);
// 날씨 아이콘 표출
const weathericon = () => {
if (controlDetail) {
let wheatherDetail = controlDetail.items.item;
let skyDetail = wheatherDetail[6].fcstValue;
@ -51,16 +57,9 @@ const ControlReportDetail = props => {
return <Sun />;
} else return <Cloud />;
}
}
useEffect(() => {
if (historyModal) {
if (controlGpDetail) {
dispatch(controlGpLogAction.request({ id: controlGpDetail.controlId }));
}
}
}, [historyModal]);
};
// 상세정보에 내역이 없으면 -로 표출
const nullMessage = val => {
if (val) {
return val;
@ -68,6 +67,7 @@ const ControlReportDetail = props => {
return '-';
}
};
return (
<div className='left-layer'>
<div className='layer-content'>
@ -76,7 +76,6 @@ const ControlReportDetail = props => {
<button
className='btn-icon'
color='primary'
// onClick={() => props.setOpenReportDetail(false)}
onClick={() => props.handlerClose()}
>
<X size={20} />
@ -101,11 +100,6 @@ const ControlReportDetail = props => {
: controlGpDetail?.objectId}
</div>
<div className='drone-img'>
{/* {controlDetail?.imageUrl ? (
<img src={IMG_PATH + controlDetail?.imageUrl} />
) : (
<img src={drone_img} />
)} */}
{controlGpDetail?.objectId.includes('UAM') ? (
<img src={uam_img} />
) : (
@ -138,18 +132,6 @@ const ControlReportDetail = props => {
{GET_ARCTFT_TYPE_CD(controlDetail?.arcrftTypeCd)}
</div>
</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>
</div>
</div>
@ -177,12 +159,6 @@ const ControlReportDetail = props => {
: '-'}
</div>
</dt>
{/* <dt>
<div className='list-left-txt'>현재위치</div>
<div className='list-right-txt'>
인천광역시 부평구 안남로 272
</div>
</dt> */}
<dt>
<div className='list-left-txt'>속도</div>
<div className='list-right-txt'>
@ -207,25 +183,12 @@ const ControlReportDetail = props => {
: '-'}
</div>
</dt>
{/* <dt>
<div className='list-left-txt'>비행거리</div>
<div className='list-right-txt'>
{nullMessage(controlGpDetail?.moveDistance)}{' '}
{controlGpDetail?.moveDistanceType}
</div>
</dt> */}
<dt>
<div className='list-left-txt'>헤딩 방위각</div>
<div className='list-right-txt'>
{nullMessage(controlGpDetail?.heading)}
</div>
</dt>
{/* <dt>
<div className='list-left-txt'>상태</div>
<div className='list-right-txt'>
{nullMessage(controlGpDetail?.dronStatus)}
</div>
</dt> */}
<dt>
<div className='list-left-txt'>위치정보 수신 시간</div>
<div className='list-right-txt'>
@ -248,10 +211,6 @@ const ControlReportDetail = props => {
<div className='layer-content-box'>
<div className='layer-content-info'>
<dl>
{/* <dt>
<div className='list-left-txt'>소속기관</div>
<div className='list-right-txt'>팔네트웍스</div>
</dt> */}
<dt>
<div className='list-left-txt'>담당자 이름</div>
<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 { useDispatch, useSelector } from 'react-redux';
import { Badge, Button, Input, InputGroup } from 'reactstrap';
import {
controlGpDtlAction,
controlGpFlightPlanAction
} from '../../../modules/control/gp';
import { controlGpDtlAction } from '../../../modules/control/gp';
import { objectClickAction } from '../../../modules/control/map/actions/controlMapActions';
import { NavLink } from 'react-router-dom';
const ControlReportList = props => {
const dispatch = useDispatch();
// 비행중인 기체 정보 리스트
const { controlGpList } = useSelector(state => state.controlGpState);
const [filterId, setFilterId] = useState('');
const dispatch = useDispatch();
// 검색한 식별번호
const [filterId, setFilterId] = useState('');
// 기체 상세정보 표출
const handlerDetail = item => {
dispatch(objectClickAction(item.controlId));
dispatch(controlGpDtlAction.request(item.controlId));
// dispatch(controlGpFlightPlanAction.request(item.objectId));
};
// useEffect(() => {}, [filterId]);
return (
<div className='left-layer'>
<div className='layer-content'>
@ -97,34 +94,6 @@ const ControlReportList = props => {
{item.dronStatus ? item.dronStatus : '-'}
</div>
</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>
</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 {
areaClickAction,
mapTypeChangeAction,
sensorClickAction
mapTypeChangeAction
} from '../../../modules/control/map/actions/controlMapActions';
const ControlSetting = props => {
const dispatch = useDispatch();
const history = useHistory();
// 지도, 지도타입, 공역 타입 컨트롤
const mapControl = useSelector(state => state.controlMapReducer);
// 지도 유형 변경
const handlerMapType = val => {
if (val === mapControl.mapType) return;
if (val === 'TERRAIN') {
@ -35,24 +37,16 @@ const ControlSetting = props => {
dispatch(mapTypeChangeAction(val));
};
// 공역 표출
const handlerAreaClick = val => {
dispatch(areaClickAction(val));
};
const handlerSensorClick = (val, isChecked) => {
if (isChecked) {
dispatch(sensorClickAction(val));
} else {
dispatch(sensorClickAction(''));
}
};
return (
<div className=''>
<div className='layer-content'>
<div className='layer-ti'>
<h4>지도유형</h4>
{/* <button className='btn-icon' outline color='primary'><X size={20} /></button> */}
</div>
<div className='map-btn'>
<ButtonGroup>
@ -270,103 +264,8 @@ const ControlSetting = props => {
</a>
</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>
);
};
export default ControlSetting;

Loading…
Cancel
Save