Browse Source

비행계획서 draw기능 - drawType 변경시 꼬임 현상 제외 완료

pull/2/head
junh_eee(이준희) 1 year ago
parent
commit
9f49f5bbe9
  1. 4
      src/components/basis/flight/plan/FlightPlanAreaMapBox.js
  2. 421
      src/components/map/mapbox/draw/MapBoxDraw.js

4
src/components/basis/flight/plan/FlightPlanAreaMapBox.js

@ -108,10 +108,6 @@ export const FlightPlanAreaMapBox = props => {
};
}, []);
// useEffect(() => {
// setIsMapLoad(true);
// }, [airArea]);
useEffect(() => {
setMode(mapControl.drawType);
}, [mapControl.drawType]);

421
src/components/map/mapbox/draw/MapBoxDraw.js

@ -50,7 +50,6 @@ export const CalculateDistance = (mouse, center) => {
// 꼭짓점 두개 찍었을 때는 영역 or 라인이 안보이기 때문
// 2. drawType을 바꾸고 그린 다음 도형을 수정하면
// move중에 처음 그린 도형 표출되는 현상도 수정해야 함
// 9. 타입 바꾸면 도형 꼬이는거 해결
export const MapBoxDraw = props => {
const mapControl = useSelector(state => state.controlMapReducer);
const mapObject = props.mapObject;
@ -66,20 +65,16 @@ export const MapBoxDraw = props => {
});
const canvas = mapObject.getCanvasContainer();
const [pastPoint, setPoint] = useState([]);
const [pastPolyline, setPolyline] = useState();
const [pastBuffer, setBuffer] = useState();
const [pastPolygon, setPolygon] = useState();
const [pastCircle, setCircle] = useState();
const [pastMarker, setMarker] = useState([]);
// const [pastPoint, setPoint] = useState([]);
// const [pastPolyline, setPolyline] = useState();
// const [pastBuffer, setBuffer] = useState();
// const [pastPolygon, setPolygon] = useState();
// const [pastCircle, setCircle] = useState();
//도형들이 온전히 그려진 후 변경 될 때 마다 감지
const [isDrawDone, setIsDrawDone] = useState(false);
//polygon, circle에서 pastObj들 렌더링 문제로
//아직 undefined인 것과 onMouseDown이벤트 중복 막기 위한...
const [isRegistEvent, setIsRegistEvent] = useState(false);
const [mouseDownEve, setMouseDownEve] = useState(false);
const areaInfo = {
coordinates: [],
@ -104,8 +99,6 @@ export const MapBoxDraw = props => {
let vertex = InitFeature('MultiPoint', 'vertex');
let dragCircleIdx;
let distanceMarker = [];
useEffect(() => {
if (mapControl.drawType) drawInit();
}, [mapControl.drawType]);
@ -118,8 +111,8 @@ export const MapBoxDraw = props => {
}, [isDrawDone]);
useEffect(() => {
const areaCoordList = props.areaCoordList[0];
if (areaCoordList.areaType && areaCoordList.areaType !== '') {
const area = props.areaCoordList[0];
if (area.areaType && area.areaType !== '') {
handlerPastDraw();
}
}, [props.areaCoordList]);
@ -144,8 +137,6 @@ export const MapBoxDraw = props => {
console.log('clearMode');
removeGeoJson();
setIsRegistEvent(false);
finishDraw();
props.handlerInitCoordinates();
};
@ -168,21 +159,21 @@ export const MapBoxDraw = props => {
mapObject.off('mousemove', onMouseMovePolyline);
mapObject.off('mousemove', onMouseMovePolygon);
mapObject.off('contextmenu', finishDraw);
// 이거 있나 없나 뭔 차이지?
setMouseDownEve(false);
};
const removeGeoJson = () => {
console.log('removeGeoJson');
setPolyline();
setBuffer();
setPolygon();
setCircle();
setPoint([]);
// setPolyline();
// setBuffer();
// setPolygon();
// setCircle();
// setPoint([]);
pastMarker?.forEach(marker => {
marker.remove();
});
setMarker([]);
handlerRemoveMarker();
guideLine.geometry.coordinates = [];
lineString.geometry.coordinates = [];
@ -225,7 +216,6 @@ export const MapBoxDraw = props => {
if (path.length > 2) {
polygon.geometry.coordinates[0] = path;
addMileStone(handlerGetPolygonCoord(polygon), '');
handlerReplaceDuplicate('polygon', polygon);
handlerSaveAreaInfo(polygon.geometry.coordinates[0], polygon);
} else {
@ -238,8 +228,8 @@ export const MapBoxDraw = props => {
props.handlerDrawType('RESET');
mapObject.on('click', clickEve);
}
mapObject.getSource('geojson').setData(geojson);
}
mapObject.getSource('geojson').setData(geojson);
}
};
@ -271,16 +261,7 @@ export const MapBoxDraw = props => {
geo => geo.properties?.id === 'point'
).length;
const wayPoint = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: formatCoord
},
properties: { id: 'point', index: index }
};
point.push(wayPoint);
const wayPoint = handlerCreatePoint(formatCoord, index);
handlerReplaceDuplicate('Point', wayPoint);
}
@ -362,16 +343,7 @@ export const MapBoxDraw = props => {
const onMouseDown = e => {
e.preventDefault();
console.log('down');
// console.log(
// mapObject.getLayer('waypoint')._eventedParent._otherSourceCaches.geojson
// ._source._data.features,
// '>>>>'
// );
const features = mapObject.queryRenderedFeatures(e.point, {
layers: ['waypoint']
});
// console.log(features, '>>>???');
//타입 교체만 하면 왜 처음엔 down이 두번 잡힐까...
canvas.style.cursor = 'grab';
@ -430,33 +402,41 @@ export const MapBoxDraw = props => {
canvas.style.cursor = '';
console.log('up');
setIsRegistEvent(true);
mapObject.off('mousedown', 'waypoint', onMouseDown);
mapObject.off('mousemove', onMouseMove);
mapObject.off('mouseup', onMouseUp);
mapObject.off('click', clickEve);
setMouseDownEve(false);
const obj = handlerMatchObj(mapControl.drawType);
if (obj) {
const id = obj.properties?.id;
const coord =
id === 'polyline'
? obj.geometry.coordinates
: obj.geometry.coordinates[0];
if (obj.geometry.coordinates.length > 0) {
if (id === 'circle') {
mapObject.on('click', clickEve);
handlerSaveAreaInfo('', circle);
const radius = CalculateDistance(
circle.properties.center,
circle.geometry.coordinates[0][0]
);
addMileStone(circle.properties.center, radius);
} else {
handlerSaveAreaInfo(coord, obj);
handlerSaveMarkerInfo(obj);
}
} 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, lineString);
} else if (type === 'POLYGON') {
handlerSaveAreaInfo(polygon.geometry.coordinates[0], polygon);
} else if (type === 'CIRCLE') {
handlerSaveAreaInfo('', circle);
}
}
};
@ -466,7 +446,16 @@ export const MapBoxDraw = props => {
let bufferZone = 100;
if (polygon.geometry.coordinates.length > 0) bufferZone = 0;
const prePath = [];
if (path) {
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') {
path.forEach(item => {
const p = {
lat: item[1],
@ -476,7 +465,6 @@ export const MapBoxDraw = props => {
});
}
areaInfo.areaType = mapControl.drawType;
areaInfo.coordinates = prePath;
areaInfo.bufferZone = bufferZone;
if (areaInfo.areaType === 'CIRCLE') {
@ -490,6 +478,7 @@ export const MapBoxDraw = props => {
circle.properties.center
);
}
props.handlerCoordinates(areaInfo);
};
@ -499,99 +488,158 @@ export const MapBoxDraw = props => {
console.log('pastDraw');
const areas = props.areaCoordList[0];
const paths = [];
areas.coordList.forEach(coord => paths.push([coord.lon, coord.lat]));
const findObj = () => {
if (areas.areaType === 'LINE') {
return pastPolyline;
} else if (areas.areaType === 'POLYGON') {
return pastPolygon;
} else if (areas.areaType === 'CIRCLE') {
return pastCircle;
}
};
console.log(pastPolyline, '>>>>poly');
console.log(pastPolygon, '>>>polygon');
const obj = findObj();
const id = obj?.properties?.id;
if (id === 'polyline' || id === 'polygon') {
geojson.features.push(obj);
//서버에 좌표 전달 편하게 하기 위해서 vertex에 좌표를 저장해주지만
//geojson에 넣어줄 데이터는 point임(index 때문에)
const length = paths.length;
for (let i = 0; i < length; i++) {
const wayPoint = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: paths[i]
},
properties: { id: 'point', index: i }
};
point.push(wayPoint);
vertex.geometry.coordinates.push(paths[i]);
// geojson.features.push(point);
}
// handlerReplaceDuplicate('Point', point);
handlerReplaceDuplicate('point', '');
point.forEach(p => geojson.features.push(p));
} else if (id === 'circle') {
const radius = props.areaCoordList[0].bufferZone;
if (areas.areaType) {
if (areas.areaType === 'CIRCLE') {
const radius = areas.bufferZone;
const circleCoords = handlerGetCircleCoord(paths[0], radius);
const content = handlerGetReplaceContent(radius);
if (pastMarker.length > 0) {
pastMarker[0].setHTML(content);
}
circle.properties.center = paths[0];
circle.geometry.coordinates = circleCoords;
// setCircle(circle);
geojson.features.push(circle);
}
} else {
if (areas.areaType === 'LINE') {
lineString.geometry.coordinates = paths;
// setPolyline(lineString);
geojson.features.push(lineString);
// 버퍼 생성
if (id === 'polyline') {
if (areas.bufferCoordList) {
const bufferPaths = [];
areas.bufferCoordList.forEach(bfCoord => {
const path = [bfCoord.lon, bfCoord.lat];
bufferPaths.push(path);
});
if (pastBuffer) {
handlerReplaceDuplicate('buffer', '');
}
areas.bufferCoordList.forEach(bfCoord =>
bufferPaths.push([bfCoord.lon, bfCoord.lat])
);
bufferPolyline.geometry.coordinates = bufferPaths;
setBuffer(bufferPolyline);
// setBuffer(bufferPolyline);
geojson.features.push(bufferPolyline);
handlerReplaceDuplicate('buffer', bufferPolyline);
}
} else if (areas.areaType === 'POLYGON') {
polygon.geometry.coordinates = [paths];
// setPolygon(polygon);
geojson.features.push(polygon);
}
// 꼭짓점 이벤트 추가
// 포인트 생성
paths.forEach((p, i) => handlerCreatePoint(p, i));
handlerReplaceDuplicate('point', '');
point.forEach(p => geojson.features.push(p));
// if (!isRegistEvent) {
if (!mouseDownEve) {
mapObject.on('mousedown', 'waypoint', onMouseDown);
setIsRegistEvent(false);
// }
setMouseDownEve(true);
}
}
// 기존 마커 제거 후 재 생성
handlerRemoveMarker();
handlerCreateAllMarker(paths);
if (id) {
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 content = handlerGetReplaceContent(distance);
const popup = new props.mapboxgl.Popup({
offset: anchor,
closeButton: false,
closeOnClick: false
})
.setLngLat(lngLat)
.setHTML(content)
.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 handlerGetReplaceContent = 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;
};
//중복되는 obj 제거 or 제거 후 새로 생성
const handlerReplaceDuplicate = (id, obj) => {
geojson.features = geojson.features.filter(
@ -603,15 +651,15 @@ export const MapBoxDraw = props => {
//도형 완성된 좌표 생성 or 변경 될 때 동작
const handlerSaveAreaInfo = (coord, obj) => {
if (obj.properties?.id === 'polyline') {
setPolyline(obj);
// setPolyline(obj);
} else if (obj.properties?.id === 'polygon') {
setPolygon(obj);
// setPolygon(obj);
} else if (obj.properties?.id === 'circle') {
setCircle(obj);
// setCircle(obj);
}
if (obj.properties?.id !== 'circle') {
setPoint(point);
// setPoint(point);
}
handlerAreaInfo(coord);
@ -625,53 +673,6 @@ export const MapBoxDraw = props => {
.map(geo => geo.geometry.coordinates);
};
// 두 좌표 간의 중간 지점 좌표 반환
const handlerGetMidPoint = (dis1, dis2) => {
return [(dis1[0] + dis2[0]) / 2, (dis1[1] + dis2[1]) / 2];
};
/**
* number면 m붙여서, string이면 그대로 html에 포함시켜서 반환
* @param {number | string} distance
*/
const handlerGetReplaceContent = distance => {
const text = () => {
if (typeof distance === 'number') {
return fromMetersToText(distance);
} else {
return distance;
}
};
const content =
'<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>';
return content;
};
// 모든 popup 좌표 및 위치 수정
const handlerSaveMarkerInfo = obj => {
const id = obj.properties?.id;
const coord =
id === 'polyline'
? obj.geometry.coordinates
: // : obj.geometry.coordinates[0];
handlerGetPolygonCoord(obj);
const markers = pastMarker.map((marker, idx) => {
if (idx > 0) {
marker.setLngLat(handlerGetMidPoint(coord[idx], coord[idx - 1]));
const distance = CalculateDistance(coord[idx], coord[idx - 1]);
marker.setHTML(handlerGetReplaceContent(distance));
} else {
marker.setLngLat([coord[0][0], coord[0][1]]);
}
return marker;
});
setMarker(markers);
};
// drawType에 따른 obj 반환
const handlerMatchObj = drawType => {
if (drawType === 'LINE') {
@ -683,12 +684,6 @@ export const MapBoxDraw = props => {
}
};
// polygon 좌표 배열에 첫 좌표 붙여서 반환
const handlerGetPolygonCoord = obj => {
const coord = obj.geometry.coordinates[0];
return [...coord, coord[0]];
};
// circle 360도 좌표 반환
const handlerGetCircleCoord = (center, distance) => {
const options = {
@ -698,61 +693,19 @@ export const MapBoxDraw = props => {
return turf.circle(center, distance / 1000, options).geometry.coordinates;
};
// 새로운 popup추가 or circle일 때 popup정보 수정
const addMileStone = (coord, radius) => {
const len = coord.length;
let lngLat = coord;
let anchor;
let distance;
if (coord[0].length) {
if (mapControl.drawType !== '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') {
anchor = [20, 35];
distance = radius;
} else {
anchor = [0, -10];
distance = 'Start';
}
}
const content = handlerGetReplaceContent(distance);
const popup = new props.mapboxgl.Popup({
offset: anchor,
closeButton: false,
closeOnClick: false
})
.setLngLat(lngLat)
.setHTML(content);
// popup 중복 제어
if (distanceMarker.length > 0) {
if (mapControl.drawType === 'CIRCLE') {
distanceMarker[0].setLngLat(lngLat);
distanceMarker[0].setHTML(content);
} else {
if (coord.length > distanceMarker.length) {
popup.addTo(mapObject);
distanceMarker.push(popup);
setMarker(prev => [...prev, popup]);
}
}
} else {
popup.addTo(mapObject);
distanceMarker.push(popup);
setMarker(prev => [...prev, popup]);
}
// 포인트 생성
const handlerCreatePoint = (coord, index) => {
const wayPoint = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: coord
},
properties: { id: 'point', index: index }
};
point.push(wayPoint);
const fromMetersToText = meters => {
meters = meters || 0;
const text = parseFloat(meters.toFixed(1)) + 'm';
return text;
return wayPoint;
};
return <InfoModal modal={alertModal} setModal={setAlertModal} />;

Loading…
Cancel
Save