diff --git a/src/components/map/mapbox/draw/LanncDraw.js b/src/components/map/mapbox/draw/LanncDraw.js
new file mode 100644
index 0000000..5c81fc0
--- /dev/null
+++ b/src/components/map/mapbox/draw/LanncDraw.js
@@ -0,0 +1,679 @@
+import { useDispatch, useSelector } from 'react-redux';
+import { InfoModal } from '../../../modal/InfoModal';
+import * as turf from '@turf/turf';
+import { useEffect, useState } from 'react';
+
+export const InitFeature = (type, id) => {
+ return {
+ type: 'Feature',
+ geometry: {
+ type: type,
+ coordinates: []
+ },
+ properties: { id: id, center: '' }
+ };
+};
+
+//소수점 7자리까지만 자름 / coord = [lng, lat]
+export const FormattingCoord = coord => {
+ const resultArr = [];
+
+ coord.forEach(co => {
+ const split = String(co).split('.');
+ const result = split[0] + '.' + split[1].substring(0, 7);
+
+ resultArr.push(Number(result));
+ });
+
+ return resultArr;
+};
+
+//거리 계산(meters로 리턴)
+// 사용 1. 원의 센터로부터 e.coord까지
+// 사용 2. distance 마커
+export const CalculateDistance = (mouse, center) => {
+ const centerCoord = turf.point(center);
+ const mouseCoord = turf.point(mouse);
+
+ const options = { units: 'kilometers' };
+
+ const distance = Math.round(
+ turf.distance(centerCoord, mouseCoord, options) * 1000
+ );
+
+ return distance;
+};
+
+export const LanncDraw = props => {
+ const dispatch = useDispatch();
+ const mapControl = useSelector(state => state.controlMapReducer);
+ const mapObject = props.mapObject;
+ const canvas = mapObject.getCanvasContainer();
+
+ //비행계획서 작성 완료 후에는 수정 불가일거라서 이거는 임시 보류
+ const isDone = props.isDone;
+ const isDisabled = props.isDisabled;
+
+ const [alertModal, setAlertModal] = useState({
+ isOpen: false,
+ title: '',
+ desc: ''
+ });
+
+ //도형들이 온전히 그려진 후 변경 될 때 마다 감지
+ const [isDrawDone, setIsDrawDone] = useState(false);
+
+ const [mouseDownEve, setMouseDownEve] = useState(false);
+
+ const areaInfo = {
+ coordinates: [],
+ bufferZone: 0,
+ areaType: ''
+ };
+
+ const geojson = props.geojson;
+ let mode = props.mode;
+
+ let guideLine = InitFeature('LineString', 'guideline');
+ let lineString = InitFeature('LineString', 'polyline');
+ let bufferPolyline = InitFeature('LineString', 'buffer');
+
+ let polygon = InitFeature('Polygon', 'polygon');
+
+ let circle = InitFeature('Polygon', 'circle');
+
+ let point = [];
+
+ let dragCircleIdx;
+
+ useEffect(() => {
+ if (mapControl.drawType) drawInit();
+ }, [mapControl.drawType]);
+
+ useEffect(() => {
+ if (isDrawDone) {
+ props.handlerConfirm(props.areaCoordList);
+ setIsDrawDone(false);
+ }
+ }, [isDrawDone]);
+
+ useEffect(() => {
+ const area = props.areaCoordList[0];
+ console.log(area, '>>>>>draw area');
+ if (area.areaType && area.areaType !== '') {
+ handlerPastDraw();
+ }
+ }, [props.areaCoordList]);
+
+ const drawInit = () => {
+ handlerButtonClick(mapControl.drawType);
+ };
+
+ //그리기 타입 선택
+ const handlerButtonClick = newMode => {
+ handlerClearMode(mode);
+
+ if (mode === newMode) {
+ mode = null;
+ return;
+ }
+
+ handlerStartMode(newMode);
+ };
+
+ const handlerClearMode = () => {
+ console.log('clearMode');
+ finishDraw();
+
+ removeGeoJson();
+ props.handlerInitCoordinates();
+ };
+
+ const handlerStartMode = mode => {
+ if (!mode) return;
+
+ mapObject.on('click', clickEve);
+ };
+
+ const removeListener = () => {
+ console.log('removeListener');
+
+ mapObject.off('click', clickEve);
+ mapObject.off('click', onClickCircle);
+ mapObject.off('mouseup', onMouseUp);
+ mapObject.off('mousedown', 'waypoint', onMouseDown);
+ mapObject.off('mousedown', 'polygon', onMouseDown);
+ mapObject.off('mousemove', onMouseMove);
+ mapObject.off('mousemove', onMouseMovePolyline);
+ mapObject.off('mousemove', onMouseMovePolygon);
+ mapObject.off('contextmenu', finishDraw);
+
+ // 이거 있나 없나 뭔 차이지?
+ setMouseDownEve(false);
+ };
+
+ const removeGeoJson = () => {
+ console.log('removeGeoJson');
+
+ handlerRemoveMarker();
+
+ guideLine.geometry.coordinates = [];
+ lineString.geometry.coordinates = [];
+ bufferPolyline.geometry.coordinates = [];
+
+ polygon.geometry.coordinates = [];
+
+ circle.geometry.coordinates = [];
+
+ point = [];
+
+ geojson.features = [];
+ mapObject.getSource('geojson').setData(geojson);
+ };
+
+ const finishDraw = () => {
+ removeListener();
+ console.log('finish');
+
+ const drawType = mapControl.drawType;
+ const path = handlerGetGeoJsonCoord('point');
+ if (path.length > 0) {
+ if (drawType === 'LINE') {
+ if (path.length > 1) {
+ handlerReplaceDuplicate('guideline', '');
+ handlerSaveAreaInfo(lineString.geometry.coordinates);
+ } else {
+ setAlertModal({
+ isOpen: true,
+ title: '좌표 최소 개수',
+ desc: '좌표를 두 개 점으로 이어주세요.'
+ });
+ removeGeoJson();
+ props.handlerDrawType('RESET');
+ // mapObject.on('click', clickEve);
+ }
+ mapObject.getSource('geojson').setData(geojson);
+ } else if (drawType === 'POLYGON') {
+ if (path.length > 2) {
+ polygon.geometry.coordinates[0] = path;
+
+ handlerReplaceDuplicate('polygon', polygon);
+ handlerSaveAreaInfo(polygon.geometry.coordinates[0]);
+ } else {
+ setAlertModal({
+ isOpen: true,
+ title: '좌표 최소 개수',
+ desc: '좌표를 세 개 점으로 이어주세요.'
+ });
+ removeGeoJson();
+ props.handlerDrawType('RESET');
+ // mapObject.on('click', clickEve);
+ }
+ }
+ mapObject.getSource('geojson').setData(geojson);
+ }
+ };
+
+ const clickEve = e => {
+ const drawType = mapControl.drawType;
+ if (drawType === 'LINE') onClickFeature(e, lineString);
+ if (drawType === 'POLYGON') onClickFeature(e, polygon);
+ if (drawType === 'CIRCLE') onClickCircle(e);
+ };
+
+ // polyline, polygon 생성
+ const onClickFeature = (e, obj) => {
+ const formatCoord = FormattingCoord([e.lngLat.lng, e.lngLat.lat]);
+ //현재 내 좌표가 waypoint레이어의 geojson도형 안에 속해있는지 안해있는지?
+ //geojson을 반환해주는 듯?
+ const features = mapObject.queryRenderedFeatures(e.point, {
+ layers: ['waypoint']
+ });
+
+ const id = obj.properties.id;
+
+ if (geojson.features.length > 1) {
+ handlerReplaceDuplicate(id, '');
+ }
+
+ if (features.length) {
+ const featuresId = features[0].properties.id;
+ handlerReplaceDuplicate(featuresId, '');
+ } else {
+ const index = geojson.features.filter(
+ geo => geo.properties?.id === 'point'
+ ).length;
+
+ const wayPoint = handlerCreatePoint(
+ formatCoord,
+ index,
+ mapControl.drawType
+ );
+ handlerReplaceDuplicate('Point', wayPoint);
+ }
+
+ guideLine.geometry.coordinates = [formatCoord];
+
+ if (geojson.features.length > 1) {
+ //나머지 좌표
+ const coordinates = handlerGetGeoJsonCoord('point');
+
+ obj.geometry.coordinates =
+ id === 'polyline' ? coordinates : [coordinates];
+ geojson.features.push(obj);
+
+ addMileStone(coordinates, '');
+ } else {
+ //첫 좌표
+ mapObject.on('contextmenu', finishDraw);
+ mapObject.on(
+ 'mousemove',
+ id === 'polyline' ? onMouseMovePolyline : onMouseMovePolygon
+ );
+ addMileStone(formatCoord, '');
+ }
+ mapObject.getSource('geojson').setData(geojson);
+ };
+
+ // polyline 가이드 생성
+ const onMouseMovePolyline = e => {
+ const formatCoord = FormattingCoord([e.lngLat.lng, e.lngLat.lat]);
+ if (guideLine.geometry.coordinates.length > 1) {
+ guideLine.geometry.coordinates.pop();
+ handlerReplaceDuplicate('guideline', guideLine);
+ }
+ guideLine.geometry.coordinates.push(formatCoord);
+
+ mapObject.getSource('geojson').setData(geojson);
+ };
+
+ // polygon 가이드 생성
+ const onMouseMovePolygon = e => {
+ const formatCoord = FormattingCoord([e.lngLat.lng, e.lngLat.lat]);
+
+ if (polygon.geometry.coordinates.length > 0) {
+ if (polygon.geometry.coordinates[0].length > 1) {
+ if (guideLine.geometry.coordinates.length > 1) {
+ guideLine.geometry.coordinates.pop();
+ polygon.geometry.coordinates[0].pop();
+ }
+ guideLine.geometry.coordinates.push(formatCoord);
+ polygon.geometry.coordinates[0].push(formatCoord);
+ }
+ }
+
+ // 이거.. 왜 안해도 잘 되지....?
+ // handlerReplaceDuplicate('polygon', polygon);
+ mapObject.getSource('geojson').setData(geojson);
+ };
+
+ // circle 생성
+ const onClickCircle = e => {
+ console.log('circleClick');
+ const formatCoord = FormattingCoord([e.lngLat.lng, e.lngLat.lat]);
+
+ if (circle.geometry.coordinates.length === 0) {
+ mapObject.on('mousedown', 'polygon', onMouseDown);
+ }
+
+ const circleCoords = handlerGetCircleCoord(formatCoord, 100);
+ circle.properties.center = formatCoord;
+ circle.geometry.coordinates = circleCoords;
+
+ handlerReplaceDuplicate('circle', circle);
+ handlerSaveAreaInfo('');
+
+ addMileStone(formatCoord, 100);
+ mapObject.getSource('geojson').setData(geojson);
+ };
+
+ const onMouseDown = e => {
+ e.preventDefault();
+ console.log('down');
+ //타입 교체만 하면 왜 처음엔 down이 두번 잡힐까...
+ // console.log(e.features[0].properties.type, '>>down e');
+
+ canvas.style.cursor = 'grab';
+
+ if (circle.geometry.coordinates.length > 0) {
+ removeListener();
+ mapObject.on('mousedown', 'polygon', onMouseDown);
+ } else {
+ dragCircleIdx = e.features[0].properties.index;
+ }
+
+ mapObject.on('mousemove', onMouseMove);
+ mapObject.on('mouseup', onMouseUp);
+
+ mapObject.off('click', clickEve);
+ };
+
+ const onMouseMove = e => {
+ const formatCoord = FormattingCoord([e.lngLat.lng, e.lngLat.lat]);
+ canvas.style.cursor = 'grabbing';
+
+ if (circle.geometry.coordinates.length > 0) {
+ const distance = CalculateDistance(formatCoord, circle.properties.center);
+ const center = circle.properties.center;
+ const circleCoords = handlerGetCircleCoord(center, distance);
+ circle.geometry.coordinates = circleCoords;
+ } else {
+ geojson.features = geojson.features.map(geo => {
+ const coord = formatCoord;
+
+ if (geo.properties?.index === dragCircleIdx) {
+ geo.geometry.coordinates = coord;
+ }
+
+ if (geo.properties?.id === 'polyline') {
+ geo.geometry.coordinates[dragCircleIdx] = coord;
+ lineString = geo;
+ }
+
+ if (geo.properties?.id === 'buffer') {
+ geo.geometry.coordinates = [];
+ }
+
+ if (geo.properties?.id === 'polygon') {
+ geo.geometry.coordinates[0][dragCircleIdx] = coord;
+ polygon = geo;
+ }
+
+ return geo;
+ });
+ }
+
+ mapObject.getSource('geojson').setData(geojson);
+ };
+
+ const onMouseUp = () => {
+ canvas.style.cursor = '';
+ console.log('up');
+
+ mapObject.off('mousedown', 'waypoint', onMouseDown);
+ mapObject.off('mousemove', onMouseMove);
+ mapObject.off('mouseup', onMouseUp);
+ mapObject.off('click', clickEve);
+ setMouseDownEve(false);
+
+ const type = mapControl.drawType;
+ const obj =
+ type === 'LINE'
+ ? lineString
+ : type === 'POLYGON'
+ ? polygon
+ : type === 'CIRCLE'
+ ? circle
+ : undefined;
+
+ if (obj) {
+ const id = obj.properties?.id;
+ const coord =
+ id === 'polyline'
+ ? obj.geometry.coordinates
+ : obj.geometry.coordinates[0];
+
+ if (id === 'circle') {
+ mapObject.on('click', clickEve);
+ handlerSaveAreaInfo('');
+ } else {
+ // mapObject.on('mousedown', 'waypoint', onMouseDown);
+ handlerSaveAreaInfo(coord);
+ }
+ } else {
+ // 저장된 좌표 불러왔을 때
+ const areas = props.areaCoordList[0];
+ const type = areas.areaType;
+
+ const paths = [];
+ areas.coordList.forEach(coord => paths.push([coord.lon, coord.lat]));
+
+ if (type === 'LINE') {
+ handlerSaveAreaInfo(lineString.geometry.coordinates);
+ } else if (type === 'POLYGON') {
+ handlerSaveAreaInfo(polygon.geometry.coordinates[0]);
+ } else if (type === 'CIRCLE') {
+ handlerSaveAreaInfo('');
+ }
+ }
+ };
+
+ // 도형 정보 변경되면 저장
+ const handlerSaveAreaInfo = coord => {
+ console.log('areaInfo');
+ const bufferZone = polygon.geometry.coordinates.length > 0 ? 0 : 100;
+ const prePath = [];
+
+ if (lineString.geometry.coordinates.length > 0) {
+ areaInfo.areaType = 'LINE';
+ } else if (polygon.geometry.coordinates.length > 0) {
+ areaInfo.areaType = 'POLYGON';
+ } else if (circle.geometry.coordinates.length > 0) {
+ areaInfo.areaType = 'CIRCLE';
+ }
+
+ if (areaInfo.areaType !== 'CIRCLE') {
+ coord.forEach(item => {
+ const p = {
+ lat: item[1],
+ lon: item[0]
+ };
+ prePath.push(p);
+ });
+ }
+
+ areaInfo.coordinates = prePath;
+ areaInfo.bufferZone = bufferZone;
+ if (areaInfo.areaType === 'CIRCLE') {
+ const point = {
+ lat: circle.properties.center[1],
+ lon: circle.properties.center[0]
+ };
+ areaInfo.coordinates = [point];
+ areaInfo.bufferZone = CalculateDistance(
+ circle.geometry.coordinates[0][0],
+ circle.properties.center
+ );
+ }
+
+ props.handlerCoordinates(areaInfo);
+ setIsDrawDone(true);
+ };
+
+ // 확정된 도형 재 생성(let이라서 지워지기 때문에)
+ const handlerPastDraw = () => {
+ if (props.areaCoordList) {
+ console.log('pastDraw');
+
+ const areas = props.areaCoordList[0];
+ const paths = [];
+ areas.coordList.forEach(coord => paths.push([coord.lon, coord.lat]));
+
+ if (areas.areaType) {
+ if (areas.areaType === 'CIRCLE') {
+ const radius = areas.bufferZone;
+ const circleCoords = handlerGetCircleCoord(paths[0], radius);
+
+ circle.properties.center = paths[0];
+ circle.geometry.coordinates = circleCoords;
+
+ geojson.features.push(circle);
+ } else {
+ if (areas.areaType === 'LINE') {
+ lineString.geometry.coordinates = paths;
+ geojson.features.push(lineString);
+
+ // 버퍼 생성
+ if (areas.bufferCoordList) {
+ const bufferPaths = [];
+
+ areas.bufferCoordList.forEach(bfCoord =>
+ bufferPaths.push([bfCoord.lon, bfCoord.lat])
+ );
+
+ bufferPolyline.geometry.coordinates = bufferPaths;
+
+ handlerReplaceDuplicate('buffer', bufferPolyline);
+ }
+ } else if (areas.areaType === 'POLYGON') {
+ polygon.geometry.coordinates = [paths];
+ geojson.features.push(polygon);
+ }
+
+ // 포인트 생성
+ paths.forEach((p, i) => handlerCreatePoint(p, i, areas.areaType));
+ handlerReplaceDuplicate('point', '');
+ point.forEach(p => geojson.features.push(p));
+
+ //once로 하면 꼬이는건 해결되는데 지도를 움직이면 이벤트가 사라짐 -> 왜? onMouseDown이 실행된게 아니잖아?
+ //on으로 하면 그 반대 현상 -> 이벤트 다 지워줬는데 왜 down이 두번 잡혀??????
+ //얘만 해결하면 끝인데...
+ if (!mouseDownEve) {
+ mapObject.on('mousedown', 'waypoint', onMouseDown);
+ setMouseDownEve(true);
+ }
+ }
+
+ // 기존 마커 제거 후 재 생성
+ handlerRemoveMarker();
+ handlerCreateAllMarker(paths);
+
+ mapObject.setPaintProperty('waypoint', 'circle-radius', 8);
+ mapObject.getSource('geojson').setData(geojson);
+ }
+ }
+ };
+
+ // 새로운 popup 한 개 추가 (coord의 뒤에서 두개의 좌표 이용)
+ const addMileStone = (coord, radius) => {
+ const len = coord.length;
+ let lngLat = coord;
+ let anchor;
+ let distance;
+
+ if (coord[0].length) {
+ if (
+ mapControl.drawType !== 'CIRCLE' ||
+ props.areaCoordList[0].areaType !== 'CIRCLE'
+ ) {
+ lngLat = handlerGetMidPoint(coord[len - 2], coord[len - 1]);
+ anchor = [0, 0];
+ distance = CalculateDistance(coord[len - 2], coord[len - 1]);
+ }
+ } else {
+ if (
+ mapControl.drawType === 'CIRCLE' ||
+ props.areaCoordList[0].areaType === 'CIRCLE'
+ ) {
+ anchor = [20, 35];
+ distance = radius;
+ } else {
+ anchor = [0, -10];
+ distance = 'Start';
+ }
+ }
+
+ const popup = new props.mapboxgl.Popup({
+ offset: anchor,
+ closeButton: false,
+ closeOnClick: false
+ })
+ .setLngLat(lngLat)
+ .setHTML(handlerGetHtmlContent(distance))
+ .addTo(mapObject);
+ };
+
+ // 좌표 기반으로 모든 마커 재 생성
+ const handlerCreateAllMarker = coord => {
+ console.log('allCreateMarker');
+ const areas = props.areaCoordList[0];
+
+ if (areas.areaType !== 'CIRCLE') {
+ for (let i = 0; i < coord.length; i++) {
+ if (i == 0) {
+ addMileStone(coord[i], '');
+ } else {
+ addMileStone([coord[i], coord[i - 1]], '');
+ }
+ }
+
+ if (areas.areaType === 'POLYGON') {
+ addMileStone([coord[0], coord[coord.length - 1]], '');
+ }
+ } else {
+ addMileStone(coord[0], areas.bufferZone);
+ }
+ };
+
+ // 모든 마커 삭제
+ const handlerRemoveMarker = () => {
+ const ele = document.getElementsByClassName('mapboxgl-popup');
+ const eleArr = Array.from(ele);
+ eleArr?.forEach(marker => marker.remove());
+ };
+
+ // 두 좌표 간의 중간 지점 좌표 반환
+ const handlerGetMidPoint = (dis1, dis2) => {
+ return [(dis1[0] + dis2[0]) / 2, (dis1[1] + dis2[1]) / 2];
+ };
+
+ // html Content 반환
+ const handlerGetHtmlContent = distance => {
+ const text =
+ typeof distance === 'number' ? fromMetersToText(distance) : distance;
+
+ return (
+ '
' +
+ text +
+ '
'
+ );
+ };
+
+ // 미터 반환(m붙여서)
+ const fromMetersToText = meters => {
+ meters = meters || 0;
+ const text = parseFloat(meters.toFixed(1)) + 'm';
+ return text;
+ };
+
+ // geojson에서 중복되는 obj 제거 or 제거 후 새로 생성
+ const handlerReplaceDuplicate = (id, obj) => {
+ geojson.features = geojson.features.filter(
+ geo => geo.properties?.id !== id
+ );
+ if (obj !== '') geojson.features.push(obj);
+ };
+
+ // geojson에서 원하는 Id의 coord만 추출
+ const handlerGetGeoJsonCoord = id => {
+ return geojson.features
+ .filter(geo => geo.properties?.id === id)
+ .map(geo => geo.geometry.coordinates);
+ };
+
+ // circle 360도 좌표 반환
+ const handlerGetCircleCoord = (center, distance) => {
+ const options = {
+ steps: 360,
+ units: 'kilometers'
+ };
+ return turf.circle(center, distance / 1000, options).geometry.coordinates;
+ };
+
+ // 포인트 생성
+ const handlerCreatePoint = (coord, index, type) => {
+ const wayPoint = {
+ type: 'Feature',
+ geometry: {
+ type: 'Point',
+ coordinates: coord
+ },
+ properties: { id: 'point', index: index, type: type }
+ };
+ point.push(wayPoint);
+
+ return wayPoint;
+ };
+
+ return ;
+};
diff --git a/src/containers/basis/flight/laanc/LaancPlanContainer.js b/src/containers/basis/flight/laanc/LaancPlanContainer.js
index ad7a914..5d2b309 100644
--- a/src/containers/basis/flight/laanc/LaancPlanContainer.js
+++ b/src/containers/basis/flight/laanc/LaancPlanContainer.js
@@ -109,7 +109,6 @@ const LaancPlanContainer = () => {
}
};
- console.log('>>>>', detailData);
// 스텝 1 다음 버튼 이벤트
const handlerNext = () => {
const reg_email =
diff --git a/src/containers/basis/flight/plan/FlightPlanContainer.js b/src/containers/basis/flight/plan/FlightPlanContainer.js
index b9c244f..091241a 100644
--- a/src/containers/basis/flight/plan/FlightPlanContainer.js
+++ b/src/containers/basis/flight/plan/FlightPlanContainer.js
@@ -25,7 +25,12 @@ import {
GROUP_LIST
} from '../../../../modules/basis/group/actions/basisGroupAction';
import FlightPlanGroupGrid from '../../../../components/basis/flight/plan/FlightPlanGroupGrid';
-import { AlertCircle, CheckCircle, XCircle, AlertTriangle } from 'react-feather';
+import {
+ AlertCircle,
+ CheckCircle,
+ XCircle,
+ AlertTriangle
+} from 'react-feather';
const initSearchData = {
schFltStDt: moment()
@@ -546,7 +551,10 @@ const FlightPlanContainer = () => {
{/* validation은 on 클래스로 제어 */}
- 기체보험가입이 되어있지 않거나 유효기간이 만료되었습니다. 기체보험가입이 되어있지 않거나 유효기간이 만료되었습니다. 기체보험가입이 되어있지 않거나 유효기간이 만료되었습니다.
+
+ 기체보험가입이 되어있지 않거나 유효기간이 만료되었습니다.
+ 기체보험가입이 되어있지 않거나 유효기간이 만료되었습니다.
+ 기체보험가입이 되어있지 않거나 유효기간이 만료되었습니다.
@@ -559,7 +567,10 @@ const FlightPlanContainer = () => {
{' '}
사전 결과 미 승인 대상입니다.
-
관제권 및 비행금지 공역을 제외한 지역에서는 주간에 150m미만의 고도에서는 비행승인없이 비행가능합니다.
+
+ 관제권 및 비행금지 공역을 제외한 지역에서는 주간에
+ 150m미만의 고도에서는 비행승인없이 비행가능합니다.{' '}
+
@@ -813,7 +824,7 @@ const FlightPlanContainer = () => {
- {/*
+
{user ? (
<>
{
) : (
<>>
)}
- */}
+
+
{/* {selectGroup.cstmrSno !== 0 ? ( */}
{listSelect.cstmrSno !== 0 ? (
diff --git a/src/views/laanc/FlightArea.js b/src/views/laanc/FlightArea.js
index 5a1c2f3..cbce9d0 100644
--- a/src/views/laanc/FlightArea.js
+++ b/src/views/laanc/FlightArea.js
@@ -1,4 +1,12 @@
+import 'mapbox-gl/dist/mapbox-gl.css';
+import mapboxgl from 'mapbox-gl';
+import MapboxLanguage from '@mapbox/mapbox-gl-language';
+import { MAPBOX_TOKEN } from '../../configs/constants';
+import { useEffect, useRef, useState } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
import {
+ Card,
+ CardBody,
Row,
Col,
Button,
@@ -11,10 +19,266 @@ import {
Label,
Input
} from 'reactstrap';
+import { initFlightBas } from '../../modules/basis/flight/models/basisFlightModel';
+import {
+ AREA_DETAIL_INIT,
+ AREA_DETAIL_LIST_SAVE
+} from '../../modules/basis/flight/actions/basisFlightAction';
+import { drawTypeChangeAction } from '../../modules/control/map/actions/controlMapActions';
+import LaancAreaMap from './LaancAreaMap';
+
+export default function FlightArea({ centeredModal, setCenteredModal }) {
+ const dispatch = useDispatch();
+ const mapControl = useSelector(state => state.controlMapReducer);
+ const { areaCoordList } = useSelector(state => state.flightState);
+
+ const mapContainer = useRef(null);
+ const [mapObject, setMapObject] = useState([null, null]);
+ const [isMapLoad, setIsMapLoad] = useState(false);
+ const [mode, setMode] = useState();
+ const [mapAreaCoordList, setMapAreaCoordList] = useState(
+ initFlightBas.initDetail.areaList
+ );
+
+ const [query, setQuery] = useState('');
+ const [searchRes, setSearchRes] = useState([]);
+ const [isSearch, setIsSearch] = useState(false);
+
+ const [number, setNumber] = useState(0);
+
+ const buildingLayer = {
+ id: 'add-3d-buildings',
+ source: 'composite',
+ 'source-layer': 'building',
+ filter: ['==', 'extrude', 'true'],
+ type: 'fill-extrusion',
+ minzoom: 15,
+ paint: {
+ 'fill-extrusion-color': '#aaa',
+
+ // Use an 'interpolate' expression to
+ // add a smooth transition effect to
+ // the buildings as the user zooms in.
+ 'fill-extrusion-height': [
+ 'interpolate',
+ ['linear'],
+ ['zoom'],
+ 15,
+ 0,
+ 15.05,
+ ['get', 'height']
+ ],
+ 'fill-extrusion-base': [
+ 'interpolate',
+ ['linear'],
+ ['zoom'],
+ 15,
+ 0,
+ 15.05,
+ ['get', 'min_height']
+ ],
+ 'fill-extrusion-opacity': 0.6
+ }
+ };
+ const terrainLayer = {
+ type: 'raster-dem',
+ url: 'mapbox://mapbox.mapbox-terrain-dem-v1'
+ // url: 'mapbox://mapbox.mapbox-street-v1',
+ // tileSize: 512,
+ // maxZoom: 16,
+ };
+ const geojson = {
+ type: 'FeatureCollection',
+ features: []
+ };
+
+ useEffect(() => {
+ // mapBoxMapInit();
+ handlerMapInit('preview');
+
+ return () => {
+ dispatch(AREA_DETAIL_INIT());
+ };
+ }, []);
+
+ const handlerDrawType = val => {
+ dispatch(drawTypeChangeAction(val));
+ };
+
+ const handlerSave = async () => {
+ const areaDetail = areaCoordList;
+ const resultAreaDetail = areaDetail.map(area => {
+ return {
+ ...area,
+ coordList: areaDetail[0].coordList
+ };
+ });
+
+ // const { data } = await axios.post(
+ // `api/bas/flight/airspace/contains`,
+ // resultAreaDetail
+ // );
+
+ // if (data.result) {
+ // setAlertModal({
+ // isOpen: true,
+ // title: '우회 여부 확인',
+ // desc: '경로상에 비행 금지된 구역이 있습니다.\n우회하여 경로 설정해주시기 바랍니다.'
+ // });
+
+ // return false;
+ // }
+
+ dispatch(AREA_DETAIL_LIST_SAVE(resultAreaDetail));
+ setCenteredModal(false);
+ // handleModal({ type: 'area', isOpne: false });
+ };
+
+ const handlerMapInit = mapType => {
+ mapboxgl.accessToken = MAPBOX_TOKEN;
+
+ const map = new mapboxgl.Map({
+ container: mapType, // 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
+ });
+
+ const language = new MapboxLanguage();
+ map.addControl(language);
+
+ map.on('style.load', () => {
+ const layers = map.getStyle().layers;
+
+ const labelLayerId = layers.find(
+ layer => layer.type === 'symbol' && layer.layout['text-field']
+ ).id;
+
+ // 지형 3d start
+ map.addSource('mapbox-dem', terrainLayer);
+ map.setTerrain({ source: 'mapbox-dem', exaggeration: 1 });
+ map.addLayer(buildingLayer, labelLayerId);
+
+ //김포 3d 공역
+ // map.addLayer({
+ // id: 'route',
+ // type: 'custom',
+ // renderingMode: '3d',
+ // onAdd: function () {
+ // for (let i = 0; i < gimPo.features.length; i++) {
+ // let line;
+ // const options = {
+ // path: gimPo.features[i].geometry.coordinates
+ // };
+ // let lineGeometry = options.path;
+ // line = tb.line({
+ // geometry: lineGeometry,
+ // width: gimPo.features[i].properties['stroke-width'],
+ // color: gimPo.features[i].properties.stroke
+ // });
+ // tb.add(line);
+ // }
+ // },
+ // render: function () {
+ // tb.update();
+ // }
+ // });
+
+ //mapDraw layer
+ map.addSource('geojson', {
+ type: 'geojson',
+ data: geojson
+ });
+ map.addLayer({
+ id: 'waypoint',
+ type: 'circle',
+ source: 'geojson',
+ paint: {
+ 'circle-radius': 5,
+ 'circle-color': '#ffffff',
+ 'circle-stroke-color': '#000000',
+ 'circle-stroke-width': 1
+ },
+ filter: ['in', '$type', 'Point']
+ });
+ map.addLayer({
+ id: 'guideline',
+ type: 'line',
+ source: 'geojson',
+ 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']
+ });
+ map.addLayer({
+ id: 'polyline',
+ type: 'line',
+ source: 'geojson',
+ layout: {
+ 'line-cap': 'round',
+ 'line-join': 'round'
+ },
+ paint: {
+ 'line-color': '#283046',
+ 'line-width': 2
+ },
+ filter: ['in', ['get', 'id'], ['literal', ['polyline', 'outline']]]
+ });
+ map.addLayer({
+ id: 'polygon',
+ type: 'fill',
+ source: 'geojson',
+ layout: {},
+ paint: {
+ 'fill-color': '#8a1c05',
+ 'fill-opacity': 0.5,
+ 'fill-outline-color': '#000000'
+ },
+ //polygon, circle에 사용
+ filter: ['in', '$type', 'Polygon']
+ });
+ map.addLayer({
+ id: 'buffer',
+ type: 'line',
+ source: 'geojson',
+ layout: {
+ 'line-cap': 'round',
+ 'line-join': 'round'
+ },
+ paint: {
+ 'line-color': '#283046',
+ 'line-width': 1,
+ 'line-dasharray': [5, 5]
+ },
+ filter: ['==', ['get', 'id'], 'buffer']
+ });
+ // setMapObject(map);
+ mapType === 'preview'
+ ? setMapObject(prev => [map, prev[1]])
+ : setMapObject(prev => [prev[0], map]);
+ setIsMapLoad(true);
+ });
+ };
-const FlightArea = ({ centeredModal, setCenteredModal }) => {
return (
+
setCenteredModal(!centeredModal)}
@@ -23,21 +287,40 @@ const FlightArea = ({ centeredModal, setCenteredModal }) => {
setCenteredModal(!centeredModal)}>
비행 구역 설정
- 지도영역 지도 height 정해주기
+
+
+
- 버튼1
- 버튼2
- 버튼3
- 버튼4
+ handlerDrawType('LINE')}>
+ WayPoint
+
+ handlerDrawType('CIRCLE')}>
+ Circle
+
+ handlerDrawType('POLYGON')}
+ >
+ Polygon
+
+ handlerDrawType('RESET')}>
+ 초기화
+
setCenteredModal(!centeredModal)}
+ // onClick={() => setCenteredModal(!centeredModal)}
+ onClick={handlerSave}
>
- 닫기
+ {/* 닫기 */}
+ 저장
@@ -45,5 +328,4 @@ const FlightArea = ({ centeredModal, setCenteredModal }) => {
);
-};
-export default FlightArea;
+}
diff --git a/src/views/laanc/FlightPlan.js b/src/views/laanc/FlightPlan.js
index c40830b..d6ac496 100644
--- a/src/views/laanc/FlightPlan.js
+++ b/src/views/laanc/FlightPlan.js
@@ -40,6 +40,7 @@ import {
} from '../../modules/control/map/actions/controlMapActions';
import { useHistory } from 'react-router-dom';
import FlightArea from './FlightArea';
+
const FlightPlan = ({
handleChange,
handlerNext,
@@ -82,7 +83,7 @@ const FlightPlan = ({
type='text'
id='memberName'
name='memberName'
- size='sm'
+ bssize='sm'
placeholder=''
value={user.memberName}
disabled
@@ -95,7 +96,7 @@ const FlightPlan = ({
* 종류
-
+
= 선택 =
상업
비상업
@@ -172,7 +173,7 @@ const FlightPlan = ({
id='fltPurpose'
name='fltPurpose'
value={data.fltPurpose}
- bsSize='sm'
+ bssize='sm'
onChange={e => {
const { name, value } = e.target;
handleChange({
@@ -201,7 +202,7 @@ const FlightPlan = ({
- 비행 구역 정보적용
+ 비행 구역 정보적용
@@ -215,7 +216,7 @@ const FlightPlan = ({
name='fltElev'
// defaultValue={data.email || ''}
value={data.areaList[0].fltElev}
- bsSize='sm'
+ bssize='sm'
onChange={e => {
const { name, value } = e.target;
handleChange({
@@ -240,7 +241,7 @@ const FlightPlan = ({
name='bufferZone'
// defaultValue={data.email || ''}
value={data.areaList[0].bufferZone}
- bsSize='sm'
+ bssize='sm'
onChange={e => {
const { name, value } = e.target;
handleChange({
@@ -265,7 +266,7 @@ const FlightPlan = ({
name='fltMethod'
// defaultValue={data.email || ''}
value={data.areaList[0].fltMethod}
- bsSize='sm'
+ bssize='sm'
onChange={e => {
const { name, value } = e.target;
handleChange({
@@ -297,7 +298,7 @@ const FlightPlan = ({
@@ -313,7 +314,7 @@ const FlightPlan = ({
* 기체 중량
-
+
@@ -326,7 +327,7 @@ const FlightPlan = ({
id='arcrftTypeCd'
name='arcrftTypeCd'
value={data.arcrftList[0].arcrftTypeCd}
- size='sm'
+ bssize='sm'
onChange={e => {
const { name, value } = e.target;
handleChange({
@@ -355,7 +356,7 @@ const FlightPlan = ({
name='groupNm'
// defaultValue={data.email || ''}
value={data.arcrftList[0].groupNm}
- bsSize='sm'
+ bssize='sm'
onChange={e => {
const { name, value } = e.target;
handleChange({
@@ -375,8 +376,10 @@ const FlightPlan = ({
- 비행 구역 설정
- setCenteredModal(!centeredModal)}>
+ setCenteredModal(!centeredModal)}
+ >
자세히보기
diff --git a/src/views/laanc/LaancAreaMap.js b/src/views/laanc/LaancAreaMap.js
new file mode 100644
index 0000000..7440052
--- /dev/null
+++ b/src/views/laanc/LaancAreaMap.js
@@ -0,0 +1,255 @@
+import 'mapbox-gl/dist/mapbox-gl.css';
+import mapboxgl from 'mapbox-gl';
+import MapboxLanguage from '@mapbox/mapbox-gl-language';
+import { MAPBOX_TOKEN } from '../../configs/constants';
+import { useEffect, useRef, useState } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import {
+ Card,
+ CardBody,
+ Input,
+ InputGroup,
+ InputGroupAddon,
+ InputGroupText
+} from 'reactstrap';
+import { Search } from 'react-feather';
+import { initFlightBas } from '../../modules/basis/flight/models/basisFlightModel';
+import {
+ AREA_COORDINATE_LIST_SAVE,
+ AREA_DETAIL_INIT,
+ FLIGHT_PLAN_AREA_BUFFER_LIST
+} from '../../modules/basis/flight/actions/basisFlightAction';
+import { flightPlanAPI } from '../../modules/basis/flight/apis/basisFlightApi';
+import { LanncDraw } from '../../components/map/mapbox/draw/LanncDraw';
+import { drawTypeChangeAction } from '../../modules/control/map/actions/controlMapActions';
+
+export default function LaancAreaMap({
+ centeredModal,
+ handlerMapInit,
+ mapObject
+}) {
+ const dispatch = useDispatch();
+ const mapControl = useSelector(state => state.controlMapReducer);
+ const { areaCoordList } = useSelector(state => state.flightState);
+
+ const mapContainer = useRef(null);
+ const [isMapLoad, setIsMapLoad] = useState(false);
+ const [mode, setMode] = useState();
+ const [mapAreaCoordList, setMapAreaCoordList] = useState(
+ initFlightBas.initDetail.areaList
+ );
+
+ const [query, setQuery] = useState('');
+ const [searchRes, setSearchRes] = useState([]);
+ const [isSearch, setIsSearch] = useState(false);
+
+ const [number, setNumber] = useState(0);
+
+ const geojson = {
+ type: 'FeatureCollection',
+ features: []
+ };
+
+ useEffect(() => {
+ handlerMapInit('detail');
+
+ return () => {
+ dispatch(AREA_DETAIL_INIT());
+ };
+ }, []);
+
+ useEffect(() => {
+ setMode(mapControl.drawType);
+ }, [mapControl.drawType]);
+
+ useEffect(() => {
+ if (areaCoordList && mapObject) {
+ if (
+ areaCoordList[0].coordList[0].lat !== 0 &&
+ areaCoordList[0].coordList[0].lon !== 0
+ ) {
+ if (number === 0) {
+ mapObject.setCenter([
+ areaCoordList[0].coordList[0].lon,
+ areaCoordList[0].coordList[0].lat
+ ]);
+ setNumber(number + 1);
+ }
+ }
+ setMapAreaCoordList(areaCoordList);
+ }
+ }, [areaCoordList, mapObject, number]);
+
+ //지역 검색
+ const handlerSearch = async () => {
+ const res = await flightPlanAPI.searchArea({ query: query });
+ setIsSearch(true);
+ setSearchRes(res.data.items);
+ };
+
+ const handlerChange = e => {
+ const { name, value } = e.target;
+
+ if (name == 'searchInput') {
+ setQuery(value);
+ }
+ };
+
+ const handlerEnter = e => {
+ if (e.key == 'Enter') {
+ handlerSearch();
+ }
+ };
+
+ const handlerCoord = (mapx, mapy) => {
+ const numberString = [mapx, mapy];
+ const latlng = [];
+
+ numberString.map(coord => {
+ let digits = coord.split('');
+
+ if (digits[0] !== '1') {
+ digits.splice(2, 0, '.');
+ } else {
+ digits.splice(3, 0, '.');
+ }
+
+ latlng.push(Number(digits.join('')));
+ });
+
+ setIsSearch(false);
+ mapObject.setCenter(latlng);
+ mapObject.setZoom(15);
+ };
+
+ const handlerDrawType = val => {
+ dispatch(drawTypeChangeAction(val));
+ };
+
+ const handlerInitCoordinates = () => {
+ const init = initFlightBas.initDetail.areaList.concat();
+ dispatch(AREA_COORDINATE_LIST_SAVE(init));
+ };
+
+ const handlerCoordinates = areaInfo => {
+ const initAreaList = initFlightBas.initDetail.areaList.concat();
+ const coordList = [];
+
+ areaInfo.coordinates.forEach((c, idx) => {
+ const coord = Object.assign({}, initFlightBas['coord']);
+ coord.lat = c.lat;
+ coord.lon = c.lon;
+
+ coordList.push(coord);
+ });
+
+ const areaList = initAreaList.map((area, idx) => {
+ return {
+ ...area,
+ bufferZone: areaInfo.bufferZone,
+ areaType: areaInfo.areaType,
+ coordList: coordList
+ };
+ });
+
+ if (areaInfo.areaType === 'LINE' || areaInfo.areaType === 'POLYGON') {
+ dispatch(FLIGHT_PLAN_AREA_BUFFER_LIST.request(areaList));
+ } else {
+ setMapAreaCoordList(areaList);
+ }
+ };
+
+ const handlerConfirm = areaList => {
+ if (areaList === undefined) {
+ alert('영역을 설정해 주세요.');
+ return false;
+ }
+
+ dispatch(AREA_COORDINATE_LIST_SAVE(areaList));
+ };
+
+ return (
+
+
+
+
+ {isMapLoad && mapObject ? (
+
+ ) : null}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {searchRes?.length !== 0 && isSearch ? (
+ searchRes?.map(search => {
+ const title = search.title
+ .replaceAll('', '')
+ .replaceAll(' ', '');
+
+ return (
+
+ handlerCoord(search.mapx, search.mapy)
+ }
+ >
+
+
+
+
+ {title}
+
+
+
+ {search.roadAddress}
+
+
+
+
+ );
+ })
+ ) : (
+ <>>
+ )}
+
+
+
+
+
+
+
+
+ );
+}