diff --git a/src/components/basis/flight/plan/FlightPlanAreaMap.js b/src/components/basis/flight/plan/FlightPlanAreaMap.js index 30c14d5..52681a8 100644 --- a/src/components/basis/flight/plan/FlightPlanAreaMap.js +++ b/src/components/basis/flight/plan/FlightPlanAreaMap.js @@ -145,7 +145,7 @@ const FlightPlanAreaMap = (props) => { handlerDrawType('RESET')} + onClick={e => handlerDrawType('RESET')} > 초기화 diff --git a/src/components/map/naver/NaverMap.js b/src/components/map/naver/NaverMap.js index 7c95e6b..df7ea63 100644 --- a/src/components/map/naver/NaverMap.js +++ b/src/components/map/naver/NaverMap.js @@ -10,6 +10,7 @@ import { FeatureAirZone } from './feature/FeatureAirZone'; import geoJson from '../geojson/airArea.json'; import SensorZone from "./sensor/SensorZone"; import { controlGroupAuthAction } from '../../../modules/control/gp'; +import DronPlan from './dron/DronPlan'; export const NaverCustomMap = () => { const dispatch = useDispatch(); @@ -68,6 +69,10 @@ export const NaverCustomMap = () => { naver={naver} test={test} /> + diff --git a/src/components/map/naver/dron/DronMarker.js b/src/components/map/naver/dron/DronMarker.js index 2681e28..d4ab443 100644 --- a/src/components/map/naver/dron/DronMarker.js +++ b/src/components/map/naver/dron/DronMarker.js @@ -3,11 +3,13 @@ import { useDispatch, useSelector } from 'react-redux'; import '../../../../assets/css/custom.css'; import DronIconPulple from '../../../../assets/images/drone-marker-icon-pulple.png'; import DronIcon from '../../../../assets/images/drone-marker-icon.png'; -import { controlGpDtlAction, controlGpHisAction, controlDbHisAction } from '../../../../modules/control/gp'; +import { controlGpDtlAction, controlGpHisAction, controlDbHisAction, controlGpFlightPlanAction } from '../../../../modules/control/gp'; import { objectClickAction } from '../../../../modules/control/map/actions/controlMapActions'; export const DronMarker = props => { const { controlGpList } = useSelector(state => state.controlGpState); + const { controlGroupAuthInfo } = useSelector(state => state.controlGroupAuthState); + const { objectId, isClickObject } = useSelector( state => state.controlMapReducer ); @@ -36,20 +38,20 @@ export const DronMarker = props => { origin: new naver.maps.Point(0, 0), anchor: new naver.maps.Point(15, 15), }); - } else { + } else { clickMarker.setIcon({ url: DronIcon, origin: new naver.maps.Point(0, 0), anchor: new naver.maps.Point(15, 15), }); } - }); + }); }, [objectId, isClickObject]); useEffect(() => { arrMarkers.map(clickMarker => { - if (objectId === clickMarker.id) { - props.map.setCenter(clickMarker.getPosition()); + if (objectId === clickMarker.id) { + props.map.setCenter(clickMarker.getPosition()); props.map.setZoom(13, true); } }); @@ -78,18 +80,22 @@ export const DronMarker = props => { naver.maps.Event.addListener(marker, 'click', function (e) { handlerDronClick(marker); - }); - + }); + setArrMarkers(m => [...m, marker]); }; - const handlerDronClick = marker => { - // const markerId = marker.id; - const contorlId = marker.controlId; + const handlerDronClick = marker => { + const idntfNum = marker.id; + const contorlId = marker.controlId; + + // 클릭한 식별번호 정보를 가진 그룹 추출 (1건만 존재해야하는게 맞는 듯) + const group = controlGroupAuthInfo.find(group => group.idntfNum === idntfNum); //히스토리 불러오기 dispatch(objectClickAction(contorlId)); - dispatch(controlGpDtlAction.request(contorlId)); + dispatch(controlGpDtlAction.request(contorlId)); + dispatch(controlGpFlightPlanAction.request(group)); }; //마커를 삭제 한다. @@ -98,7 +104,7 @@ export const DronMarker = props => { }; //마커에 위치를 이동한다. - const moveMarkers = (marker, position) => { + const moveMarkers = (marker, position) => { marker.setPosition(position); }; @@ -117,33 +123,33 @@ export const DronMarker = props => { removeArrMarkers(arrData); } }); - } + } }; //마커를 셋팅 한다. const markerInit = () => { if (controlGpList) { - allRemoveMarkers(); + allRemoveMarkers(); controlGpList.map(item => { const position = new naver.maps.LatLng(item.lat, item.lng); - if (arrMarkers) { + if (arrMarkers) { const isExists = arrMarkers.find( ele => ele.id === item.objectId ); - if (isExists) { + if (isExists) { moveMarkers(isExists, position); } else { addMarkers(position, item.objectId, item.controlId); } - } else { + } else { addMarkers(position, item.objectId, item.controlId); } }); } }; - const removeArrMarkers = arrData => { + const removeArrMarkers = arrData => { setArrMarkers(arrData); }; diff --git a/src/components/map/naver/dron/DronPlan.js b/src/components/map/naver/dron/DronPlan.js new file mode 100644 index 0000000..1d26d4e --- /dev/null +++ b/src/components/map/naver/dron/DronPlan.js @@ -0,0 +1,180 @@ +import { useEffect, useState } from 'react'; +import { useSelector } from 'react-redux'; + + +const DronPlan = ({ naver, map }) => { + const { controlGpList } = useSelector(state => state.controlGpState); + const { controlGpFltPlanList } = useSelector(state => state.controlGpFltPlanState); + const { objectId, isClickObject } = useSelector(state => state.controlMapReducer); + + const [area, setArea] = useState(); // 비행 구역 관리 + const [buffer, setBuffer] = useState(); // 버퍼 구역 관리 + const [controlId, setControlId] = useState(); // 식별번호 저장 + + useEffect(() => { + if (controlGpFltPlanList) { + init(); + } + }, [controlGpFltPlanList]) + + useEffect(() => { + if (!isClickObject) { + + // 관제 종료시 영역 초기화. + clear(); + + } else { + setControlId(objectId); + } + + }, [objectId ,isClickObject]); + + useEffect(() => { + // 구역 대상의 드론 데이터가 지도 상에 존재하고 있는지 체크해야 한다. (없을 경우 영역 제거) + if(controlId && isClickObject) { + if (controlGpList) { + const isExist = controlGpList.find(data => data.controlId === controlId) + + if(!isExist) { + clear(); + } + } + } + }, [controlGpList]) + + + /* 비행 구역 그리기. */ + const init = () => { + if(area) { + area.setMap(null); + } + + /* 좌표 추출 */ + const planList = controlGpFltPlanList; + planList.forEach(plan => { + + const areaList = plan.areaList; + + // 구역 정보는 계획서당 1개만 존재 + areaList.forEach(area => { + const coordList = area.coordList; // 기초 좌표 + const bufferList = area.bufferCoordList; // 기초 좌표의 버퍼 좌표 + const bufferZone = area.bufferZone; // 반경 값 + const areaType = area.areaType; // 도형 타입 + + const paths = []; + coordList.forEach((coord) => { + const path = new naver.maps.LatLng(coord.lat, coord.lon) + + paths.push(path); + }); + + clear(); + + if (areaType === 'LINE') { + polyline(paths, bufferList); + } + + if (areaType === 'POLYGON') { + polygon(paths); + } + + if (areaType === 'CIRCLE') { + circle(paths, bufferZone); + } + + }); + }) + } + + /* Polyline Create */ + const polyline = (paths, bufferList) => { + if (paths && paths.length > 1) { + const line = new naver.maps.Polyline({ + strokeLineCap: 'round', + strokeLineJoin: 'round', + strokeColor: '#283046', + strokeWeight: 1, + strokeOpacity: 0.5, + path: paths, + map: map + }); + + setArea(line); + } + + if (bufferList && bufferList.length > 1) { + const paths = []; + bufferList.forEach((buffer) => { + const path = new naver.maps.LatLng(buffer.lat, buffer.lon) + + paths.push(path); + }); + + const lineBuffer = new naver.maps.Polygon({ + strokeColor: '#283046', + strokeOpacity: 1, + fillColor: '#7367F0', + fillOpacity: 0.1, + paths: paths, + map: map + }); + + setBuffer(lineBuffer); + } + } + + /* Polygon Create */ + const polygon = (paths) => { + if (paths && paths.length > 1) { + const poly = new naver.maps.Polygon({ + strokeColor: '#283046', + strokeOpacity: 1, + fillColor: '#7367F0', + fillOpacity: 0.1, + paths: paths, + map: map + }); + + setArea(poly); + } + } + + /* Circle Create */ + const circle = (paths, bufferZone) => { + + if (paths[0].lat !== 0 && paths[0].lon !== 0) { + const circle = new naver.maps.Circle({ + strokeColor: '#283046', + strokeOpacity: 1, + fillColor: '#7367F0', + fillOpacity: 0.1, + center: paths[0], + radius: bufferZone, + map: map, + clickable: true + }); + + setArea(circle); + } + } + + /* 구역 초기화 */ + const clear = () => { + + if(area) { + area.setMap(null); + + setArea(); + if(buffer) { + buffer.setMap(null); + setBuffer(); + } + } + } + + + return null; +} + +export default DronPlan; \ No newline at end of file diff --git a/src/modules/control/gp/actions/controlGpAction.ts b/src/modules/control/gp/actions/controlGpAction.ts index 825619c..404c500 100644 --- a/src/modules/control/gp/actions/controlGpAction.ts +++ b/src/modules/control/gp/actions/controlGpAction.ts @@ -5,6 +5,8 @@ import { ControlDetailData, ControlGpData, ControlGpDtlState, + ControlGpFlightPlanDataList, + ControlGpFlightPlanRQ, ControlGpHisState, ControlGpState, ControlGroupAuthState @@ -31,6 +33,11 @@ const CONTROL_GROUP_AUTH_REQUEST = 'control/group/CONTROL_GROUP_AUTH_REQUEST'; const CONTROL_GROUP_AUTH_SUCCESS = 'control/group/CONTROL_GROUP_AUTH_SUCCESS'; const CONTROL_GROUP_AUTH_FAILURE = 'control/group/CONTROL_GROUP_AUTH_FAILURE'; +// [관제] +const CONTROL_FLIGHT_PLAN_REQUEST = 'control/gp/flight/plan/CONTROL_FLIGHT_PLAN_REQUEST'; +const CONTROL_FLIGHT_PLAN_SUCCESS = 'control/gp/flight/plan/CONTROL_FLIGHT_PLAN_SUCCESS'; +const CONTROL_FLIGHT_PLAN_FAILURE = 'control/gp/flight/plan/CONTROL_FLIGHT_PLAN_FAILURE'; + export const controlGpAction = createAsyncAction( CONTROL_GP_REQUEST, CONTROL_GP_SUCCESS, @@ -61,12 +68,19 @@ export const controlGroupAuthAction = createAsyncAction( CONTROL_GROUP_AUTH_FAILURE )(); +export const controlGpFlightPlanAction = createAsyncAction( + CONTROL_FLIGHT_PLAN_REQUEST, + CONTROL_FLIGHT_PLAN_SUCCESS, + CONTROL_FLIGHT_PLAN_FAILURE, +)(); + const actions = { controlGpAction, controlGpHisAction, controlGpDtlAction, controlGpRtDtlAction, - controlGroupAuthAction + controlGroupAuthAction, + controlGpFlightPlanAction }; export type ControlGpAction = ActionType; diff --git a/src/modules/control/gp/apis/controlGpApi.ts b/src/modules/control/gp/apis/controlGpApi.ts index 218666f..3bfaa83 100644 --- a/src/modules/control/gp/apis/controlGpApi.ts +++ b/src/modules/control/gp/apis/controlGpApi.ts @@ -1,5 +1,6 @@ import axios from '../../../utils/customAxiosUtil'; -import { ReponseControlGpHistory, ControlGroupAuthData, ResponseControlGroupAuth} from '../models/controlGpModel'; +import { ReponseControlGpHistory, ControlGroupAuthData, ResponseControlGroupAuth, ControlGpFlightPlanRQ } from '../models/controlGpModel'; +import qs from 'qs'; export const controlGpApi = { getHistory: async (id: string) => { @@ -17,12 +18,28 @@ export const controlGpApi = { return await axios.get(`api/ctr/cntrl/detail/${id}`); }, getGroupAuth: async (id: number) => { - if(!id) { + if (!id) { return null; } - const { data }:ResponseControlGroupAuth = await axios.get( + const { data }: ResponseControlGroupAuth = await axios.get( `api/ctr/cntrl/group?cstmrSno=${id}` - ); + ); return data; }, + getFlightPlan: async (rq: ControlGpFlightPlanRQ) => { + if (!rq.idntfNum) { + return null; + } + + const queryString = qs.stringify(rq, { + addQueryPrefix: true, + arrayFormat: 'repeat' + }); + + const { data } = await axios.get( + `api/ctr/cntrl/flight_plan${queryString}` + ); + + return data; + } }; diff --git a/src/modules/control/gp/models/controlGpModel.ts b/src/modules/control/gp/models/controlGpModel.ts index 5870172..2ec8f97 100644 --- a/src/modules/control/gp/models/controlGpModel.ts +++ b/src/modules/control/gp/models/controlGpModel.ts @@ -1,5 +1,9 @@ export interface ControlGpState { - controlGpList: ControlGpData[] | undefined; + controlGpList: ControlGpData[] | undefined; +} + +export interface ControlGpFlightPlanState { + controlGpFltPlanList: ControlGpFlightPlanDataList | undefined; } export interface ControlGpHisState { @@ -93,6 +97,66 @@ export interface ControlGroupAuthData { createUserId: string } +export interface ControlGpFlightPlanDataList extends Array {}; + +export interface ControlGpFlightPlanData { + planSno?: number, + groupId: string, + cstmrSno: number, + memberName: string, + email: string, + hpno: string, + clncd: string, + addr: string, + addrDtlCn: string, + zip: string, + schFltStDt: string, + schFltEndDt: string, + fltPurpose: string, + aprvlYn: string, + delYn: string, + createUserId: string, + createDt: string, + updateUserId: string, + updateDt: string, + areaList?: FlightPlanAreaDataList | undefined +} + +export interface FlightPlanAreaDataList extends Array {}; + +export interface FlightPlanAreaData { + planAreaSno?: number, + planSno: number, + areaType: string, + fltMethod: string, + bufferZone: number, + fltElev: string, + createUserId?: string, + createDt?: string, + updateUserId?: string, + updateDt?: string, + coordList?: FlightPlanAreaCoordDataList | undefined + bufferCoordList?: FlightPlanAreaCoordDataList | undefined +} + +export interface FlightPlanAreaCoordDataList extends Array {}; + +export interface FlightPlanAreaCoordData { + planAreaCoordSno?: number, + planAreaSno?: number, + lat: number, + lon: number, + createUserId?: string, + createDt?: string + // docState: string +} + +export interface ControlGpFlightPlanRQ { + idntfNum: string, + groupId: string, + cstmrSno: number, // 흠 +} + export interface ReponseControlGpHistory { data: ControlGpHistoryData[]; } @@ -107,4 +171,5 @@ export const initiaResponseControlGpData = { controlGpHistory: undefined, controlDetail: undefined, controlGroupAuthInfo: undefined, + controlGpFltPlanList: undefined, }; diff --git a/src/modules/control/gp/reducers/controlGpReducer.ts b/src/modules/control/gp/reducers/controlGpReducer.ts index 32aef1b..98a981f 100644 --- a/src/modules/control/gp/reducers/controlGpReducer.ts +++ b/src/modules/control/gp/reducers/controlGpReducer.ts @@ -4,13 +4,15 @@ import { ControlGpAction, controlGpAction, controlGpDtlAction, + controlGpFlightPlanAction, controlGpHisAction, controlGpRtDtlAction, controlGroupAuthAction } from '../actions/controlGpAction'; import { ControlDetailData, - ControlGpDtlState, + ControlGpDtlState, + ControlGpFlightPlanState, ControlGpHisState, ControlGpState, ControlGroupAuthState, @@ -24,6 +26,15 @@ export const controlGpReducer = createReducer( const { controlGpList } = action.payload; draft.controlGpList = controlGpList; }) +) + +export const controlGpFltPlanReducer = createReducer( + initiaResponseControlGpData +).handleAction(controlGpFlightPlanAction.success, (state, action) => + produce(state, draft => { + const list = action.payload; + draft.controlGpFltPlanList = list; + }) ); export const controlGpHisReducer = createReducer< diff --git a/src/modules/control/gp/sagas/controlGpSaga.ts b/src/modules/control/gp/sagas/controlGpSaga.ts index 853592b..14e45ee 100644 --- a/src/modules/control/gp/sagas/controlGpSaga.ts +++ b/src/modules/control/gp/sagas/controlGpSaga.ts @@ -39,7 +39,6 @@ function* getControlGpSaga( controlGpList: gpsData }) ); - if (objectId && isClickObject) { @@ -118,12 +117,10 @@ function* controlGroupAuthSaga ( const token = cookieStorage.getCookie(COOKIE_ACCESS_TOKEN); try { - if(token) { - console.log("group auth token : ", token); + if(token) { const user = decode(token); - const data = yield call(controlGpApi.getGroupAuth, user.cstmrSno); - console.log("group auth data : ", data); + yield put(Actions.controlGroupAuthAction.success({ controlGroupAuthInfo: data @@ -134,10 +131,28 @@ function* controlGroupAuthSaga ( } } +function* controlGpFlightPlanSaga ( + action: ActionType +) { + try { + const rq = action.payload; + + const list = yield call(controlGpApi.getFlightPlan, rq); + + console.log("flight plan list : ", list); + + yield put(Actions.controlGpFlightPlanAction.success(list)); + + } catch (error) { + yield put(Actions.controlGpFlightPlanAction.failure(error)); + } +} + export function* controlGpSaga() { yield takeEvery(Actions.controlGpAction.request, getControlGpSaga); yield takeEvery(Actions.controlGpHisAction.request, getControlGpHistorySaga); yield takeEvery(Actions.controlGpRtDtlAction.request, controlGpRtDtlSaga); yield takeEvery(Actions.controlGpDtlAction.request, controlDtlSaga); yield takeEvery(Actions.controlGroupAuthAction.request, controlGroupAuthSaga); + yield takeEvery(Actions.controlGpFlightPlanAction.request, controlGpFlightPlanSaga); } diff --git a/src/redux/reducers/rootReducer.ts b/src/redux/reducers/rootReducer.ts index ae07618..35f4130 100644 --- a/src/redux/reducers/rootReducer.ts +++ b/src/redux/reducers/rootReducer.ts @@ -19,8 +19,9 @@ import { controlGpHisReducer, controlGpReducer, controlGroupAuthReducer, + controlGpFltPlanReducer, controlGpSaga, - ControlGpState, + ControlGpState, } from '../../modules/control/gp'; import controlMapReducer from '../../modules/control/map/reducers/controlMapReducer'; import { mainDahReducer } from '../../modules/main/dash/reducers/mainDashReducer'; @@ -63,7 +64,8 @@ const rootReducer = combineReducers({ controlGpState: controlGpReducer, controlGpHisState: controlGpHisReducer, controlGpDtlState: controlGpDtlReducer, - controlGroupAuthState: controlGroupAuthReducer, + controlGroupAuthState: controlGroupAuthReducer, + controlGpFltPlanState: controlGpFltPlanReducer, menuState: menuReducer, analysisHistoryState: analysisHistoryReducer, analysisSimulatorState: analysisSimulatorReducer,