From 9f49f5bbe9c79b5e3ba03995ba986be7d1b5fc9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?junh=5Feee=28=EC=9D=B4=EC=A4=80=ED=9D=AC=29?= Date: Thu, 21 Sep 2023 19:07:21 +0900 Subject: [PATCH] =?UTF-8?q?=EB=B9=84=ED=96=89=EA=B3=84=ED=9A=8D=EC=84=9C?= =?UTF-8?q?=20draw=EA=B8=B0=EB=8A=A5=20-=20drawType=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EC=8B=9C=20=EA=BC=AC=EC=9E=84=20=ED=98=84=EC=83=81=20=EC=A0=9C?= =?UTF-8?q?=EC=99=B8=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basis/flight/plan/FlightPlanAreaMapBox.js | 4 - src/components/map/mapbox/draw/MapBoxDraw.js | 453 ++++++++---------- 2 files changed, 203 insertions(+), 254 deletions(-) diff --git a/src/components/basis/flight/plan/FlightPlanAreaMapBox.js b/src/components/basis/flight/plan/FlightPlanAreaMapBox.js index 49c2ea7..3fc1972 100644 --- a/src/components/basis/flight/plan/FlightPlanAreaMapBox.js +++ b/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]); diff --git a/src/components/map/mapbox/draw/MapBoxDraw.js b/src/components/map/mapbox/draw/MapBoxDraw.js index bcb47e1..5233e4e 100644 --- a/src/components/map/mapbox/draw/MapBoxDraw.js +++ b/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); - const id = obj.properties?.id; - const coord = - id === 'polyline' - ? obj.geometry.coordinates - : obj.geometry.coordinates[0]; - if (obj.geometry.coordinates.length > 0) { + 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('', 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,108 +478,168 @@ export const MapBoxDraw = props => { circle.properties.center ); } + props.handlerCoordinates(areaInfo); }; - //let이라서 지워지는 도형 재 생성 + // 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])); - const findObj = () => { - if (areas.areaType === 'LINE') { - return pastPolyline; - } else if (areas.areaType === 'POLYGON') { - return pastPolygon; - } else if (areas.areaType === 'CIRCLE') { - return pastCircle; - } - }; + if (areas.areaType) { + if (areas.areaType === 'CIRCLE') { + const radius = areas.bufferZone; + const circleCoords = handlerGetCircleCoord(paths[0], radius); - 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; - 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 (areas.bufferCoordList) { + const bufferPaths = []; + + areas.bufferCoordList.forEach(bfCoord => + bufferPaths.push([bfCoord.lon, bfCoord.lat]) + ); + + bufferPolyline.geometry.coordinates = bufferPaths; + // setBuffer(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 (!mouseDownEve) { + mapObject.on('mousedown', 'waypoint', onMouseDown); + setMouseDownEve(true); + } } - circle.properties.center = paths[0]; - circle.geometry.coordinates = circleCoords; + // 기존 마커 제거 후 재 생성 + handlerRemoveMarker(); + handlerCreateAllMarker(paths); - geojson.features.push(circle); + mapObject.setPaintProperty('waypoint', 'circle-radius', 8); + mapObject.getSource('geojson').setData(geojson); } + } + }; - //버퍼 생성 - if (id === 'polyline') { - if (areas.bufferCoordList) { - const bufferPaths = []; + // 새로운 popup 한 개 추가 (coord의 뒤에서 두개의 좌표 이용) + const addMileStone = (coord, radius) => { + const len = coord.length; + let lngLat = coord; + let anchor; + let distance; - areas.bufferCoordList.forEach(bfCoord => { - const path = [bfCoord.lon, bfCoord.lat]; - bufferPaths.push(path); - }); + 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'; + } + } - if (pastBuffer) { - handlerReplaceDuplicate('buffer', ''); - } + const content = handlerGetReplaceContent(distance); + const popup = new props.mapboxgl.Popup({ + offset: anchor, + closeButton: false, + closeOnClick: false + }) + .setLngLat(lngLat) + .setHTML(content) + .addTo(mapObject); + }; - bufferPolyline.geometry.coordinates = bufferPaths; - setBuffer(bufferPolyline); + // 좌표 기반으로 모든 마커 재 생성 + const handlerCreateAllMarker = coord => { + console.log('allCreateMarker'); + const areas = props.areaCoordList[0]; - geojson.features.push(bufferPolyline); + 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 (!isRegistEvent) { - mapObject.on('mousedown', 'waypoint', onMouseDown); - setIsRegistEvent(false); - // } - - if (id) { - mapObject.setPaintProperty('waypoint', 'circle-radius', 8); - mapObject.getSource('geojson').setData(geojson); + 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 ( + '
' + + text + + '
' + ); + }; + + // 미터 반환(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 = - '
' + - text() + - '
'; - 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 ;