junh_eee(이준희)
1 year ago
6 changed files with 1259 additions and 29 deletions
@ -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 ( |
||||
'<div style="display:inline-block;padding:5px;text-align:center;background-color:#fff;border:1px solid #000;font-size:13px;color:#ff0000;"><span>' + |
||||
text + |
||||
'</span></div>' |
||||
); |
||||
}; |
||||
|
||||
// 미터 반환(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 <InfoModal modal={alertModal} setModal={setAlertModal} />; |
||||
}; |
@ -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 ( |
||||
<Card className='mb-0'> |
||||
<CardBody> |
||||
<div style={{ position: 'relative' }}> |
||||
<div |
||||
id='detail' |
||||
ref={mapContainer} |
||||
style={{ width: '100%', height: '70vh' }} |
||||
> |
||||
{isMapLoad && mapObject ? ( |
||||
<LanncDraw |
||||
mapObject={mapObject} |
||||
mapboxgl={mapboxgl} |
||||
mode={mode} |
||||
areaCoordList={mapAreaCoordList} |
||||
handlerCoordinates={handlerCoordinates} |
||||
handlerInitCoordinates={handlerInitCoordinates} |
||||
handlerConfirm={handlerConfirm} |
||||
geojson={geojson} |
||||
handlerDrawType={handlerDrawType} |
||||
/> |
||||
) : null} |
||||
|
||||
<div className='d-flex search-comp absolute'> |
||||
<div className=''> |
||||
<InputGroup className='search-feather'> |
||||
<InputGroupAddon addonType='prepend'> |
||||
<InputGroupText> |
||||
<Search bssize={14} onClick={handlerSearch} /> |
||||
</InputGroupText> |
||||
</InputGroupAddon> |
||||
<Input |
||||
type='text' |
||||
id='searchInput' |
||||
name='searchInput' |
||||
bssize='sm' |
||||
autoComplete='off' |
||||
placeholder='검색명을 입력하세요.' |
||||
onChange={handlerChange} |
||||
onKeyPress={handlerEnter} |
||||
/> |
||||
</InputGroup> |
||||
<div className='search-result-comp'> |
||||
<ul> |
||||
{searchRes?.length !== 0 && isSearch ? ( |
||||
searchRes?.map(search => { |
||||
const title = search.title |
||||
.replaceAll('<b>', '') |
||||
.replaceAll('</b>', ''); |
||||
|
||||
return ( |
||||
<li |
||||
key={search.mapx + search.mapy} |
||||
onClick={() => |
||||
handlerCoord(search.mapx, search.mapy) |
||||
} |
||||
> |
||||
<a> |
||||
<div className='search-result'> |
||||
<div className='title'> |
||||
<span> |
||||
<strong>{title}</strong> |
||||
</span> |
||||
</div> |
||||
<div className='address'> |
||||
<span>{search.roadAddress}</span> |
||||
</div> |
||||
</div> |
||||
</a> |
||||
</li> |
||||
); |
||||
}) |
||||
) : ( |
||||
<></> |
||||
)} |
||||
</ul> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</CardBody> |
||||
</Card> |
||||
); |
||||
} |
Loading…
Reference in new issue