Browse Source

[비행 관제] 비행 구역 표출

feature/auth
노승철 2 years ago
parent
commit
4065868d43
  1. 2
      src/components/basis/flight/plan/FlightPlanAreaMap.js
  2. 5
      src/components/map/naver/NaverMap.js
  3. 42
      src/components/map/naver/dron/DronMarker.js
  4. 180
      src/components/map/naver/dron/DronPlan.js
  5. 16
      src/modules/control/gp/actions/controlGpAction.ts
  6. 25
      src/modules/control/gp/apis/controlGpApi.ts
  7. 67
      src/modules/control/gp/models/controlGpModel.ts
  8. 13
      src/modules/control/gp/reducers/controlGpReducer.ts
  9. 25
      src/modules/control/gp/sagas/controlGpSaga.ts
  10. 6
      src/redux/reducers/rootReducer.ts

2
src/components/basis/flight/plan/FlightPlanAreaMap.js

@ -145,7 +145,7 @@ const FlightPlanAreaMap = (props) => {
<Button.Ripple <Button.Ripple
color='primary' color='primary'
className='area-button' className='area-button'
onClick={e => handlerDrawType('RESET')} onClick={e => handlerDrawType('RESET')}
> >
초기화 초기화
</Button.Ripple> </Button.Ripple>

5
src/components/map/naver/NaverMap.js

@ -10,6 +10,7 @@ import { FeatureAirZone } from './feature/FeatureAirZone';
import geoJson from '../geojson/airArea.json'; import geoJson from '../geojson/airArea.json';
import SensorZone from "./sensor/SensorZone"; import SensorZone from "./sensor/SensorZone";
import { controlGroupAuthAction } from '../../../modules/control/gp'; import { controlGroupAuthAction } from '../../../modules/control/gp';
import DronPlan from './dron/DronPlan';
export const NaverCustomMap = () => { export const NaverCustomMap = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -68,6 +69,10 @@ export const NaverCustomMap = () => {
naver={naver} naver={naver}
test={test} test={test}
/> />
<DronPlan
map={mapObject}
naver={naver}
/>
<NaverMapControl map={mapObject} /> <NaverMapControl map={mapObject} />

42
src/components/map/naver/dron/DronMarker.js

@ -3,11 +3,13 @@ import { useDispatch, useSelector } from 'react-redux';
import '../../../../assets/css/custom.css'; import '../../../../assets/css/custom.css';
import DronIconPulple from '../../../../assets/images/drone-marker-icon-pulple.png'; import DronIconPulple from '../../../../assets/images/drone-marker-icon-pulple.png';
import DronIcon from '../../../../assets/images/drone-marker-icon.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'; import { objectClickAction } from '../../../../modules/control/map/actions/controlMapActions';
export const DronMarker = props => { export const DronMarker = props => {
const { controlGpList } = useSelector(state => state.controlGpState); const { controlGpList } = useSelector(state => state.controlGpState);
const { controlGroupAuthInfo } = useSelector(state => state.controlGroupAuthState);
const { objectId, isClickObject } = useSelector( const { objectId, isClickObject } = useSelector(
state => state.controlMapReducer state => state.controlMapReducer
); );
@ -36,20 +38,20 @@ export const DronMarker = props => {
origin: new naver.maps.Point(0, 0), origin: new naver.maps.Point(0, 0),
anchor: new naver.maps.Point(15, 15), anchor: new naver.maps.Point(15, 15),
}); });
} else { } else {
clickMarker.setIcon({ clickMarker.setIcon({
url: DronIcon, url: DronIcon,
origin: new naver.maps.Point(0, 0), origin: new naver.maps.Point(0, 0),
anchor: new naver.maps.Point(15, 15), anchor: new naver.maps.Point(15, 15),
}); });
} }
}); });
}, [objectId, isClickObject]); }, [objectId, isClickObject]);
useEffect(() => { useEffect(() => {
arrMarkers.map(clickMarker => { arrMarkers.map(clickMarker => {
if (objectId === clickMarker.id) { if (objectId === clickMarker.id) {
props.map.setCenter(clickMarker.getPosition()); props.map.setCenter(clickMarker.getPosition());
props.map.setZoom(13, true); props.map.setZoom(13, true);
} }
}); });
@ -78,18 +80,22 @@ export const DronMarker = props => {
naver.maps.Event.addListener(marker, 'click', function (e) { naver.maps.Event.addListener(marker, 'click', function (e) {
handlerDronClick(marker); handlerDronClick(marker);
}); });
setArrMarkers(m => [...m, marker]); setArrMarkers(m => [...m, marker]);
}; };
const handlerDronClick = marker => { const handlerDronClick = marker => {
// const markerId = marker.id; const idntfNum = marker.id;
const contorlId = marker.controlId; const contorlId = marker.controlId;
// 클릭한 식별번호 정보를 가진 그룹 추출 (1건만 존재해야하는게 맞는 듯)
const group = controlGroupAuthInfo.find(group => group.idntfNum === idntfNum);
//히스토리 불러오기 //히스토리 불러오기
dispatch(objectClickAction(contorlId)); 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); marker.setPosition(position);
}; };
@ -117,33 +123,33 @@ export const DronMarker = props => {
removeArrMarkers(arrData); removeArrMarkers(arrData);
} }
}); });
} }
}; };
//마커를 셋팅 한다. //마커를 셋팅 한다.
const markerInit = () => { const markerInit = () => {
if (controlGpList) { if (controlGpList) {
allRemoveMarkers(); allRemoveMarkers();
controlGpList.map(item => { controlGpList.map(item => {
const position = new naver.maps.LatLng(item.lat, item.lng); const position = new naver.maps.LatLng(item.lat, item.lng);
if (arrMarkers) { if (arrMarkers) {
const isExists = arrMarkers.find( const isExists = arrMarkers.find(
ele => ele.id === item.objectId ele => ele.id === item.objectId
); );
if (isExists) { if (isExists) {
moveMarkers(isExists, position); moveMarkers(isExists, position);
} else { } else {
addMarkers(position, item.objectId, item.controlId); addMarkers(position, item.objectId, item.controlId);
} }
} else { } else {
addMarkers(position, item.objectId, item.controlId); addMarkers(position, item.objectId, item.controlId);
} }
}); });
} }
}; };
const removeArrMarkers = arrData => { const removeArrMarkers = arrData => {
setArrMarkers(arrData); setArrMarkers(arrData);
}; };

180
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;

16
src/modules/control/gp/actions/controlGpAction.ts

@ -5,6 +5,8 @@ import {
ControlDetailData, ControlDetailData,
ControlGpData, ControlGpData,
ControlGpDtlState, ControlGpDtlState,
ControlGpFlightPlanDataList,
ControlGpFlightPlanRQ,
ControlGpHisState, ControlGpHisState,
ControlGpState, ControlGpState,
ControlGroupAuthState 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_SUCCESS = 'control/group/CONTROL_GROUP_AUTH_SUCCESS';
const CONTROL_GROUP_AUTH_FAILURE = 'control/group/CONTROL_GROUP_AUTH_FAILURE'; 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( export const controlGpAction = createAsyncAction(
CONTROL_GP_REQUEST, CONTROL_GP_REQUEST,
CONTROL_GP_SUCCESS, CONTROL_GP_SUCCESS,
@ -61,12 +68,19 @@ export const controlGroupAuthAction = createAsyncAction(
CONTROL_GROUP_AUTH_FAILURE CONTROL_GROUP_AUTH_FAILURE
)<string, ControlGroupAuthState, AxiosError>(); )<string, ControlGroupAuthState, AxiosError>();
export const controlGpFlightPlanAction = createAsyncAction(
CONTROL_FLIGHT_PLAN_REQUEST,
CONTROL_FLIGHT_PLAN_SUCCESS,
CONTROL_FLIGHT_PLAN_FAILURE,
)<ControlGpFlightPlanRQ, ControlGpFlightPlanDataList, AxiosError>();
const actions = { const actions = {
controlGpAction, controlGpAction,
controlGpHisAction, controlGpHisAction,
controlGpDtlAction, controlGpDtlAction,
controlGpRtDtlAction, controlGpRtDtlAction,
controlGroupAuthAction controlGroupAuthAction,
controlGpFlightPlanAction
}; };
export type ControlGpAction = ActionType<typeof actions>; export type ControlGpAction = ActionType<typeof actions>;

25
src/modules/control/gp/apis/controlGpApi.ts

@ -1,5 +1,6 @@
import axios from '../../../utils/customAxiosUtil'; 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 = { export const controlGpApi = {
getHistory: async (id: string) => { getHistory: async (id: string) => {
@ -17,12 +18,28 @@ export const controlGpApi = {
return await axios.get(`api/ctr/cntrl/detail/${id}`); return await axios.get(`api/ctr/cntrl/detail/${id}`);
}, },
getGroupAuth: async (id: number) => { getGroupAuth: async (id: number) => {
if(!id) { if (!id) {
return null; return null;
} }
const { data }:ResponseControlGroupAuth = await axios.get( const { data }: ResponseControlGroupAuth = await axios.get(
`api/ctr/cntrl/group?cstmrSno=${id}` `api/ctr/cntrl/group?cstmrSno=${id}`
); );
return data; 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;
}
}; };

67
src/modules/control/gp/models/controlGpModel.ts

@ -1,5 +1,9 @@
export interface ControlGpState { export interface ControlGpState {
controlGpList: ControlGpData[] | undefined; controlGpList: ControlGpData[] | undefined;
}
export interface ControlGpFlightPlanState {
controlGpFltPlanList: ControlGpFlightPlanDataList | undefined;
} }
export interface ControlGpHisState { export interface ControlGpHisState {
@ -93,6 +97,66 @@ export interface ControlGroupAuthData {
createUserId: string createUserId: string
} }
export interface ControlGpFlightPlanDataList extends Array<ControlGpFlightPlanData> {};
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<FlightPlanAreaData> {};
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<FlightPlanAreaCoordData> {};
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 { export interface ReponseControlGpHistory {
data: ControlGpHistoryData[]; data: ControlGpHistoryData[];
} }
@ -107,4 +171,5 @@ export const initiaResponseControlGpData = {
controlGpHistory: undefined, controlGpHistory: undefined,
controlDetail: undefined, controlDetail: undefined,
controlGroupAuthInfo: undefined, controlGroupAuthInfo: undefined,
controlGpFltPlanList: undefined,
}; };

13
src/modules/control/gp/reducers/controlGpReducer.ts

@ -4,13 +4,15 @@ import {
ControlGpAction, ControlGpAction,
controlGpAction, controlGpAction,
controlGpDtlAction, controlGpDtlAction,
controlGpFlightPlanAction,
controlGpHisAction, controlGpHisAction,
controlGpRtDtlAction, controlGpRtDtlAction,
controlGroupAuthAction controlGroupAuthAction
} from '../actions/controlGpAction'; } from '../actions/controlGpAction';
import { import {
ControlDetailData, ControlDetailData,
ControlGpDtlState, ControlGpDtlState,
ControlGpFlightPlanState,
ControlGpHisState, ControlGpHisState,
ControlGpState, ControlGpState,
ControlGroupAuthState, ControlGroupAuthState,
@ -24,6 +26,15 @@ export const controlGpReducer = createReducer<ControlGpState, ControlGpAction>(
const { controlGpList } = action.payload; const { controlGpList } = action.payload;
draft.controlGpList = controlGpList; draft.controlGpList = controlGpList;
}) })
)
export const controlGpFltPlanReducer = createReducer<ControlGpFlightPlanState, ControlGpAction>(
initiaResponseControlGpData
).handleAction(controlGpFlightPlanAction.success, (state, action) =>
produce(state, draft => {
const list = action.payload;
draft.controlGpFltPlanList = list;
})
); );
export const controlGpHisReducer = createReducer< export const controlGpHisReducer = createReducer<

25
src/modules/control/gp/sagas/controlGpSaga.ts

@ -39,7 +39,6 @@ function* getControlGpSaga(
controlGpList: gpsData controlGpList: gpsData
}) })
); );
if (objectId && isClickObject) { if (objectId && isClickObject) {
@ -118,12 +117,10 @@ function* controlGroupAuthSaga (
const token = cookieStorage.getCookie(COOKIE_ACCESS_TOKEN); const token = cookieStorage.getCookie(COOKIE_ACCESS_TOKEN);
try { try {
if(token) { if(token) {
console.log("group auth token : ", token);
const user = decode<LoginData>(token); const user = decode<LoginData>(token);
const data = yield call(controlGpApi.getGroupAuth, user.cstmrSno); const data = yield call(controlGpApi.getGroupAuth, user.cstmrSno);
console.log("group auth data : ", data);
yield put(Actions.controlGroupAuthAction.success({ yield put(Actions.controlGroupAuthAction.success({
controlGroupAuthInfo: data controlGroupAuthInfo: data
@ -134,10 +131,28 @@ function* controlGroupAuthSaga (
} }
} }
function* controlGpFlightPlanSaga (
action: ActionType<typeof Actions.controlGpFlightPlanAction.request>
) {
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() { export function* controlGpSaga() {
yield takeEvery(Actions.controlGpAction.request, getControlGpSaga); yield takeEvery(Actions.controlGpAction.request, getControlGpSaga);
yield takeEvery(Actions.controlGpHisAction.request, getControlGpHistorySaga); yield takeEvery(Actions.controlGpHisAction.request, getControlGpHistorySaga);
yield takeEvery(Actions.controlGpRtDtlAction.request, controlGpRtDtlSaga); yield takeEvery(Actions.controlGpRtDtlAction.request, controlGpRtDtlSaga);
yield takeEvery(Actions.controlGpDtlAction.request, controlDtlSaga); yield takeEvery(Actions.controlGpDtlAction.request, controlDtlSaga);
yield takeEvery(Actions.controlGroupAuthAction.request, controlGroupAuthSaga); yield takeEvery(Actions.controlGroupAuthAction.request, controlGroupAuthSaga);
yield takeEvery(Actions.controlGpFlightPlanAction.request, controlGpFlightPlanSaga);
} }

6
src/redux/reducers/rootReducer.ts

@ -19,8 +19,9 @@ import {
controlGpHisReducer, controlGpHisReducer,
controlGpReducer, controlGpReducer,
controlGroupAuthReducer, controlGroupAuthReducer,
controlGpFltPlanReducer,
controlGpSaga, controlGpSaga,
ControlGpState, ControlGpState,
} from '../../modules/control/gp'; } from '../../modules/control/gp';
import controlMapReducer from '../../modules/control/map/reducers/controlMapReducer'; import controlMapReducer from '../../modules/control/map/reducers/controlMapReducer';
import { mainDahReducer } from '../../modules/main/dash/reducers/mainDashReducer'; import { mainDahReducer } from '../../modules/main/dash/reducers/mainDashReducer';
@ -63,7 +64,8 @@ const rootReducer = combineReducers({
controlGpState: controlGpReducer, controlGpState: controlGpReducer,
controlGpHisState: controlGpHisReducer, controlGpHisState: controlGpHisReducer,
controlGpDtlState: controlGpDtlReducer, controlGpDtlState: controlGpDtlReducer,
controlGroupAuthState: controlGroupAuthReducer, controlGroupAuthState: controlGroupAuthReducer,
controlGpFltPlanState: controlGpFltPlanReducer,
menuState: menuReducer, menuState: menuReducer,
analysisHistoryState: analysisHistoryReducer, analysisHistoryState: analysisHistoryReducer,
analysisSimulatorState: analysisSimulatorReducer, analysisSimulatorState: analysisSimulatorReducer,

Loading…
Cancel
Save