From 956a99d6ec2b13f983c63215fc50a27a88c79280 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, 12 Oct 2023 13:34:44 +0900 Subject: [PATCH] =?UTF-8?q?laanc=20=EC=83=81=EC=84=B8=EB=A7=B5=20=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=EB=A1=9C=20=EC=9E=AC?= =?UTF-8?q?=EA=B5=AC=EC=B6=95=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../map/mapbox/draw/LaancDrawControl.js | 244 ++++++++++++++++++ src/components/map/mapbox/draw/LaancDrawRe.js | 23 +- src/views/laanc/FlightArea.js | 106 +++++++- src/views/laanc/LaancAreaMap.js | 26 +- 4 files changed, 365 insertions(+), 34 deletions(-) create mode 100644 src/components/map/mapbox/draw/LaancDrawControl.js diff --git a/src/components/map/mapbox/draw/LaancDrawControl.js b/src/components/map/mapbox/draw/LaancDrawControl.js new file mode 100644 index 0000000..a118372 --- /dev/null +++ b/src/components/map/mapbox/draw/LaancDrawControl.js @@ -0,0 +1,244 @@ +import { useEffect, useState } from 'react'; +import { InfoModal } from '../../../modal/InfoModal'; +import { useSelector } from 'react-redux'; +import { + CalculateDistance, + handlerGetHtmlContent, + handlerGetMidPoint +} from '../../../../utility/DrawUtil'; + +export const LaancDrawControl = props => { + const mapControl = useSelector(state => state.controlMapReducer); + const drawObj = props.drawObj; + const mapObject = props.mapObject; + + const [bufferId, setBufferId] = useState(); + const [geojson, setGeoJson] = useState(); + + const [alertModal, setAlertModal] = useState({ + isOpen: false, + title: '', + desc: '' + }); + + useEffect(() => { + drawInit(); + }, [mapControl.drawType]); + + useEffect(() => { + if (mapObject) { + mapObject.on('draw.create', handlerCreateObj); + mapObject.on('draw.update', handlerFormatAreaInfo); + + mapObject.on('click', handlerOnClick); + } + }, [mapObject]); + + useEffect(() => { + handlerPastDraw(); + }, [props.areaCoordList]); + + const handlerOnClick = e => { + const featureIds = drawObj.getFeatureIdsAt(e.point); + const feature = drawObj.get(featureIds[0]); + const type = handlerReturnMode(drawObj.getMode()); + + if (type) { + if (feature) { + // 뒷 부분 + const coordinates = feature.geometry.coordinates; + const coords = [ + ...new Set( + type === 'LINE' + ? coordinates.map(JSON.stringify) + : coordinates[0].map(JSON.stringify) + ) + ].map(JSON.parse); + + const len = coords.length; + const lngLat = handlerGetMidPoint(coords[len - 2], coords[len - 1]); + const text = CalculateDistance(coords[len - 2], coords[len - 1]); + + handlerCreateOneMarker([0, 0], lngLat, text); + } else { + // Start + handlerCreateOneMarker([0, -10], [e.lngLat.lng, e.lngLat.lat], 'Start'); + } + } + }; + + // 도형 첫 생성하면 properties.id에 현재 drawType 넣어줌 + const handlerCreateObj = e => { + const id = e.features[0].id; + const mode = handlerReturnMode(drawObj.getMode()); + if (mode) { + drawObj.setFeatureProperty(id, 'id', mode); + handlerFormatAreaInfo(e); + } + }; + + const handlerFormatAreaInfo = e => { + const coord = e.features[0].geometry.coordinates; + const mode = drawObj.get(e.features[0].id).properties.id; + + const data = + mode === 'LINE' + ? coord + : mode === 'POLYGON' + ? coord[0] + : mode === 'CIRCLE' + ? e.features[0].properties.center + : null; + + handlerSaveAreaInfo(data, mode); + }; + + const handlerSaveAreaInfo = (coord, mode) => { + if (!coord || !mode) { + alert('에러 발생. 다시 시도해 주세요.'); + return; + } + + const areaInfo = { + coordinates: [], + bufferZone: 0, + areaType: '' + }; + + const bufferZone = mode === 'POLYGON' ? 0 : 100; + const prePath = []; + + areaInfo.areaType = mode; + + 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 obj = drawObj.getAll(); + const feature = obj.features[0]; + const point = { + lat: coord[1], + lon: coord[0] + }; + areaInfo.coordinates = [point]; + areaInfo.bufferZone = feature.properties.radiusInKm * 1000; + } + props.handlerCoordinates(areaInfo); + }; + + const handlerPastDraw = () => { + if (props.areaCoordList) { + console.log(props.areaCoordList[0], '>>>>areaCoordList---'); + + const areas = props.areaCoordList[0]; + const paths = []; + areas.coordList.forEach(coord => paths.push([coord.lon, coord.lat])); + + if (areas.areaType) { + console.log('pastDraw'); + const drawGeoJson = drawObj.getAll(); + if (areas.areaType === 'CIRCLE') { + } else { + if (areas.areaType === 'LINE') { + // 버퍼 생성 + if (areas.bufferCoordList) { + const bufferPaths = []; + + areas.bufferCoordList.forEach(bfCoord => + bufferPaths.push([bfCoord.lon, bfCoord.lat]) + ); + + const buffer = drawObj.get(bufferId); + if (buffer) drawObj.delete(bufferId); + + const newBuffer = { + type: 'LineString', + coordinates: bufferPaths + }; + const newBufferId = drawObj.add(newBuffer); + setBufferId(newBufferId); + } + } else if (areas.areaType === 'POLYGON') { + } + } + // 현재 그려진 도형 저장 + setGeoJson(drawGeoJson); + + // 마커 재 생성 + handlerRemoveMarker(); + handlerCreateAllMarker(paths); + } + } + }; + + const handlerRemoveMarker = () => { + const ele = document.getElementsByClassName('mapboxgl-popup'); + const eleArr = Array.from(ele); + eleArr?.forEach(marker => marker.remove()); + }; + + const handlerCreateOneMarker = (anchor, lngLat, text) => { + const popup = new props.mapboxgl.Popup({ + offset: anchor, + closeButton: false, + closeOnClick: false + }) + .setLngLat(lngLat) + .setHTML(handlerGetHtmlContent(text)) + .addTo(mapObject); + }; + + const handlerCreateAllMarker = coords => { + for (let i = 0; i < coords.length; i++) { + if (i === 0) { + handlerCreateOneMarker([0, -10], coords[i], 'Start'); + } else { + const lngLat = handlerGetMidPoint(coords[i - 1], coords[i]); + const text = CalculateDistance(coords[i - 1], coords[i]); + handlerCreateOneMarker([0, 0], lngLat, text); + } + } + }; + + const drawInit = () => { + drawObj.deleteAll(); + handlerRemoveMarker(); + + props.handlerInitCoordinates(); + + const mode = mapControl.drawType; + if (!mode || mode === 'RESET') { + return; + } + handlerStartMode(mode); + }; + + const handlerStartMode = mode => { + console.log('startMode'); + if (mode === 'LINE') { + drawObj.changeMode('draw_line_string'); + } else if (mode === 'POLYGON') { + drawObj.changeMode('draw_polygon'); + } else if (mode === 'CIRCLE') { + drawObj.changeMode('draw_circle'); + } + }; + + // drawMode에 따른 drawType 매칭 + const handlerReturnMode = mode => { + if (mode.includes('line') || mode.includes('Line')) return 'LINE'; + if (mode.includes('polygon') || mode.includes('Polygon')) return 'POLYGON'; + if (mode.includes('circle') || mode.includes('Circle')) return 'CIRCLE'; + }; + + return ; +}; diff --git a/src/components/map/mapbox/draw/LaancDrawRe.js b/src/components/map/mapbox/draw/LaancDrawRe.js index e967ec0..0cca722 100644 --- a/src/components/map/mapbox/draw/LaancDrawRe.js +++ b/src/components/map/mapbox/draw/LaancDrawRe.js @@ -50,6 +50,7 @@ export const LaancDrawRe = props => { useEffect(() => { const areaType = props.areaCoordList[0].areaType; + console.log(props.areaCoordList, '>>----'); if (areaType && areaType !== '' && detailLayer) { handlerPastDraw(); @@ -76,6 +77,7 @@ export const LaancDrawRe = props => { console.log('removeEvent'); mapObject.off('click', onClickDrawObj); mapObject.off('mousemove', onMouseMoveDrawObj); + mapObject.off('mousedown', 'polygon', onMouseDown); mapObject.off('contextmenu', onRightClick); }, [mode]); @@ -104,9 +106,10 @@ export const LaancDrawRe = props => { handlerSetDrawObj('Polygon', 'POLYGON'); } else if (mode === 'CIRCLE') { handlerSetDrawObj('Polygon', 'CIRCLE'); + mapObject.on('click', onClickCircle); } - mapObject.on('click', onClickDrawObj); + if (mode !== 'CIRCLE') mapObject.on('click', onClickDrawObj); }; const onClickDrawObj = useCallback( @@ -258,7 +261,6 @@ export const LaancDrawRe = props => { handlerReplaceDuplicate(id, ''); return; } - // if (coord.length === 0) return; console.log('down'); if (id === 'CIRCLE') { @@ -289,6 +291,7 @@ export const LaancDrawRe = props => { const center = drawObj.properties.center; const circleCoords = handlerGetCircleCoord(center, distance); drawObj.geometry.coordinates = circleCoords; + handlerReplaceDuplicate('CIRCLE', drawObj); } else { geojson.features = geojson.features.map(geo => { const coord = formatCoord; @@ -370,7 +373,8 @@ export const LaancDrawRe = props => { drawObj.properties.id = 'CIRCLE'; drawObj.geometry.type = 'Polygon'; - geojson.features.push(drawObj); + // geojson.features.push(drawObj); + handlerReplaceDuplicate('CIRCLE', drawObj); } else { if (areas.areaType === 'LINE') { drawObj.geometry.coordinates = paths; @@ -411,14 +415,13 @@ export const LaancDrawRe = props => { // mouseDown이벤트 mapObject.on('mousedown', 'waypoint', onMouseDown); - - // 기존 마커 제거 후 재 생성 - handlerRemoveMarker(); - handlerCreateAllMarker(paths); - - mapObject.setPaintProperty('waypoint', 'circle-radius', 8); - handlerGetSourceSetData(); } + // 기존 마커 제거 후 재 생성 + handlerRemoveMarker(); + handlerCreateAllMarker(paths); + + mapObject.setPaintProperty('waypoint', 'circle-radius', 8); + handlerGetSourceSetData(); } }; diff --git a/src/views/laanc/FlightArea.js b/src/views/laanc/FlightArea.js index c377e67..173886f 100644 --- a/src/views/laanc/FlightArea.js +++ b/src/views/laanc/FlightArea.js @@ -223,7 +223,95 @@ export default function FlightArea({ drag_circle: DragCircleMode, direct_select: DirectMode, simple_select: SimpleSelectMode - } + }, + styles: [ + // line stroke + { + id: 'gl-draw-line', + type: 'line', + filter: [ + 'all', + ['==', '$type', 'LineString'], + ['!=', 'mode', 'static'] + ], + layout: { + 'line-cap': 'round', + 'line-join': 'round' + }, + paint: { + 'line-color': '#8a1c05', + 'line-dasharray': [0.2, 2], + 'line-width': 2 + } + }, + // polygon fill + { + id: 'gl-draw-polygon-fill', + type: 'fill', + filter: ['all', ['==', '$type', 'Polygon'], ['!=', 'mode', 'static']], + paint: { + 'fill-color': '#8a1c05', + 'fill-outline-color': '#8a1c05', + 'fill-opacity': 0.1 + } + }, + // polygon mid points + { + id: 'gl-draw-polygon-midpoint', + type: 'circle', + filter: ['all', ['==', '$type', 'Point'], ['==', 'meta', 'midpoint']], + paint: { + 'circle-radius': 4, + 'circle-color': '#8a1c05' + } + }, + // polygon outline stroke + // This doesn't style the first edge of the polygon, which uses the line stroke styling instead + { + id: 'gl-draw-polygon-stroke-active', + type: 'line', + filter: ['all', ['==', '$type', 'Polygon'], ['!=', 'mode', 'static']], + layout: { + 'line-cap': 'round', + 'line-join': 'round' + }, + paint: { + 'line-color': '#8a1c05', + 'line-dasharray': [0.2, 2], + 'line-width': 2 + } + }, + // vertex point halos + { + id: 'gl-draw-polygon-and-line-vertex-halo-active', + type: 'circle', + filter: [ + 'all', + ['==', 'meta', 'vertex'], + ['==', '$type', 'Point'], + ['!=', 'mode', 'static'] + ], + paint: { + 'circle-radius': 8, + 'circle-color': '#fff' + } + }, + // vertex points + { + id: 'gl-draw-polygon-and-line-vertex-active', + type: 'circle', + filter: [ + 'all', + ['==', 'meta', 'vertex'], + ['==', '$type', 'Point'], + ['!=', 'mode', 'static'] + ], + paint: { + 'circle-radius': 6, + 'circle-color': '#8a1c05' + } + } + ] }); setDrawObj(draw); @@ -439,8 +527,8 @@ export default function FlightArea({