From 2ece4d0ddc538c399f83994b17f6be7f31795c0f 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: Wed, 6 Sep 2023 17:32:29 +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=20polyline,=20polygon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/css/custom.css | 3 +- .../basis/flight/plan/FlightPlanAreaMapBox.js | 105 ++++--- src/components/map/mapbox/draw/MapBoxDraw.js | 256 ++++++++++++++---- 3 files changed, 270 insertions(+), 94 deletions(-) diff --git a/src/assets/css/custom.css b/src/assets/css/custom.css index b3189fd7..23b8dabf 100644 --- a/src/assets/css/custom.css +++ b/src/assets/css/custom.css @@ -845,4 +845,5 @@ background-size: 75% auto; .box_4n{display:flex;flex-wrap: wrap;} .box_4n div{width:50%;height:50vh;} .mapboxgl-popup-content {padding: 0 !important;border-radius:6px !important;} -.mapboxgl-popup-tip {display: none!important;} \ No newline at end of file +.mapboxgl-popup-tip {display: none!important;} +.absolute {position: absolute!important;} \ No newline at end of file diff --git a/src/components/basis/flight/plan/FlightPlanAreaMapBox.js b/src/components/basis/flight/plan/FlightPlanAreaMapBox.js index 88f33c80..6eb4f5e1 100644 --- a/src/components/basis/flight/plan/FlightPlanAreaMapBox.js +++ b/src/components/basis/flight/plan/FlightPlanAreaMapBox.js @@ -51,6 +51,11 @@ export const FlightPlanAreaMapBox = props => { const [formModal, setFormModal] = useState(false); + const geojson = { + type: 'FeatureCollection', + features: [] + }; + useEffect(() => { mapBoxMapInit(); @@ -110,37 +115,7 @@ export const FlightPlanAreaMapBox = props => { maxZoom: 16 }); map.setTerrain({ source: 'mapbox-dem', exaggeration: 1.5 }); - map.addLayer({ - id: 'contour-labels', - type: 'symbol', - source: { - type: 'vector', - url: 'mapbox://mapbox.mapbox-terrain-v2' - }, - 'source-layer': 'contour', - layout: { - visibility: 'visible', - 'symbol-placement': 'line', - 'text-field': ['concat', ['to-string', ['get', 'ele']], 'm'] - }, - paint: { - 'icon-color': '#877b59', - 'icon-halo-width': 1, - 'text-color': '#877b59', - 'text-halo-width': 1 - } - }); - map.addLayer({ - id: 'sky', - type: 'sky', - paint: { - 'sky-type': 'atmosphere', - 'sky-atmosphere-sun': [0.0, 90.0], - 'sky-atmosphere-sun-intensity': 15 - } - }); - // 지형 3d end - // 등고선 start + map.addLayer({ id: 'contours', type: 'line', @@ -159,8 +134,6 @@ export const FlightPlanAreaMapBox = props => { 'line-width': 1 } }); - // 등고선 end - // 3d building map.addLayer( { id: 'add-3d-buildings', @@ -198,7 +171,65 @@ export const FlightPlanAreaMapBox = props => { }, labelLayerId ); - // 3d building + + //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.1 + }, + filter: ['in', '$type', 'LineString'] + }); + map.addLayer({ + id: 'polyline', + type: 'line', + source: 'geojson', + layout: { + 'line-cap': 'round', + 'line-join': 'round' + }, + paint: { + 'line-color': '#283046', + 'line-width': 1 + }, + filter: ['in', '$type', 'LineString'] + }); + map.addLayer({ + id: 'polygon', + type: 'fill', + source: 'geojson', + layout: {}, + paint: { + 'fill-color': '#7367F0', + 'fill-opacity': 0.5, + 'fill-outline-color': '#000000' + }, + filter: ['in', '$type', 'Polygon'] + }); setMapObject(map); setIsMapLoad(true); }); @@ -270,9 +301,9 @@ export const FlightPlanAreaMapBox = props => { ref={mapContainer} style={{ width: '100%', height: '60vh' }} > - {/* 여기에 draw 하는 파일 들어와야 함 */} - {mapObject ? ( + {isMapLoad && mapObject ? ( { setDragCircle={setDragCircle} /> ) : null} -
+
diff --git a/src/components/map/mapbox/draw/MapBoxDraw.js b/src/components/map/mapbox/draw/MapBoxDraw.js index 84ca6ab0..4522a2fe 100644 --- a/src/components/map/mapbox/draw/MapBoxDraw.js +++ b/src/components/map/mapbox/draw/MapBoxDraw.js @@ -1,8 +1,7 @@ import { useDispatch, useSelector } from 'react-redux'; import { InfoModal } from '../../../modal/InfoModal'; import * as turf from '@turf/turf'; -import { useEffect, useState } from 'react'; -import MapboxDraw from '@mapbox/mapbox-gl-draw'; +import { useCallback, useEffect, useState } from 'react'; export const MapBoxDraw = props => { const dispatch = useDispatch(); @@ -12,11 +11,6 @@ export const MapBoxDraw = props => { const isDone = props.isDone; const isDisabled = props.isDisabled; - const [pastPolyline, setPastPolyline] = useState(); - const [pastBuffer, setBuffer] = useState(); - const [pastPolygon, setPastPolygon] = useState(); - const [pastCircle, setCircle] = useState([]); - const pastDragCircle = props.pastDragCircle; const setDragCircle = props.setDragCircle; @@ -37,38 +31,54 @@ export const MapBoxDraw = props => { let mode = props.mode; - let areaInfo; - let lastDistance; - - let polyline; - let guideline; - let bufferPolygon; - - let polygon; - let circle; let radiusline; - let event = { + const eventListener = { clickEvent: '', - mousedownEvent: '', + mousemoveEvent: '', rightClickEvent: '' }; - let dragCircle = []; - let dragCircleEvent = []; + const geojson = props.geojson; + let wayPoint; + let guideLine; + let lineString; - let distanceMarker = []; + let polygon; useEffect(() => { setRadiusCircle(props.dragSize); }, [props.dragSize]); useEffect(() => { - drawInit(); + if (mapControl.drawType) drawInit(); }, [mapControl.drawType]); + const initFeature = type => { + return { + type: 'Feature', + geometry: { + type: type, + coordinates: [] + }, + properties: { id: '' } + }; + }; + const drawInit = () => { + wayPoint = initFeature('Point'); + wayPoint.properties.id = 'point'; + + guideLine = initFeature('LineString'); + guideLine.properties.id = 'guideline'; + + lineString = initFeature('LineString'); + lineString.properties.id = 'polyline'; + + polygon = initFeature('Polygon'); + polygon.properties.id = 'polygon'; + const drawType = mapControl.drawType; handlerButtonClick(drawType); }; @@ -85,55 +95,189 @@ export const MapBoxDraw = props => { }; const handlerClearMode = () => { - // if (pastPolyline) { - // pastP - // } + // geojson.features = []; + // wayPoint.geometry.coordinates = []; + // guideLine.geometry.coordinates = []; + // lineString.geometry.coordinates = []; + // mapObject.getSource('geojson').setData(geojson); + + finishDraw(); + }; + + const finishDraw = () => { + removeListener(); + + // geojson.features = []; + // wayPoint.geometry.coordinates = []; + // guideLine.geometry.coordinates = []; + // lineString.geometry.coordinates = []; + // mapObject.getSource('geojson').setData(geojson); }; const handlerStartMode = mode => { if (!mode) return; - if (pastClickEvent) { - mapObject.removeEventListener(pastClickEvent); + eventListener.clickEvent = e => { + if (mode === 'LINE') onClickPolyline(e); + if (mode === 'POLYGON') onClickPolygon(e); + if (mode === 'RESET') handlerClearMode(); + }; + mapObject.on('click', eventListener.clickEvent); + }; + + const removeListener = () => { + mapObject.off('click', eventListener.clickEvent); + mapObject.off('mousemove', eventListener.mousemoveEvent); + mapObject.off('contextmenu', eventListener.rightClickEvent); + + eventListener.clickEvent = ''; + eventListener.mousemoveEvent = ''; + eventListener.rightClickEvent = ''; + }; + + const onClickPolyline = e => { + const features = mapObject.queryRenderedFeatures(e.point, { + layers: ['waypoint'] + }); + + lineString.properties.id = 'polyline'; + if (geojson.features.length > 1) { + geojson.features = geojson.features.filter( + geo => geo.properties?.id !== 'polyline' + ); + } + + if (features.length) { + const id = features[0].properties.id; + geojson.features = geojson.features.filter( + point => point.properties.id !== id + ); + } else { + const point = { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [e.lngLat.lng, e.lngLat.lat] + }, + properties: { id: 'point' } + }; + geojson.features.push(point); } - // event.clickEvent = mapObject.addEventListener('click', function (e) { - // if (mode === 'LINE') { - // onClickPolyline(e); - // } - // else if (mode === 'POLYGON') { - // onClickPolygon(e); - // } else if (mode === 'CIRCLE') { - // onClickCircle(e); - // } - // }); - if (mode === 'CIRCLE') setPastClickEvent(event.clickEvent); + guideLine.properties.id = 'guideline'; + guideLine.geometry.coordinates = [[e.lngLat.lng, e.lngLat.lat]]; + + if (geojson.features.length > 1) { + //point들의 좌표만 뽑아서 polyline의 좌표로 넣어줌 + lineString.geometry.coordinates = geojson.features + .filter(point => point.properties?.id === 'point') + .map(point => point.geometry.coordinates); + + geojson.features.push(lineString); + console.log(lineString.geometry, '>>>lineString'); + } else { + eventListener.rightClickEvent = () => { + finishDraw(); + }; + mapObject.on('contextmenu', eventListener.rightClickEvent); + + eventListener.mousemoveEvent = e => { + onMouseMovePolyline(e); + }; + mapObject.on('mousemove', eventListener.mousemoveEvent); + } + mapObject.getSource('geojson').setData(geojson); }; - const removeListener = () => { - mapObject.removeEventListener(event.clickEvent); - mapObject.removeEventListener(pastClickEvent); - setPastClickEvent(); + const onMouseMovePolyline = e => { + if (guideLine.geometry.coordinates.length > 1) { + guideLine.geometry.coordinates.pop(); + + //기존 guideline 제거 + geojson.features = geojson.features.filter( + point => point.properties?.id !== 'guideline' + ); + //새로운 가이드라인 push + geojson.features.push(guideLine); + } + guideLine.geometry.coordinates.push([e.lngLat.lng, e.lngLat.lat]); - mapObject.removeEventListener(event.mousedownEvent); - mapObject.removeEventListener(event.rightClickEvent); - if (!circle) $(document).off('mousemove.measure'); + mapObject.getSource('geojson').setData(geojson); }; - // const handlerFinishDraw = () => { - // removeListener(); + const onClickPolygon = e => { + const features = mapObject.queryRenderedFeatures(e.point, { + layers: ['waypoint'] + }); - // if(polyline) { - // if(guideline) { - // guideline - // } - // } - // } + polygon.properties.id = 'polygon'; + if (geojson.features.length > 1) { + geojson.features = geojson.features.filter( + geo => geo.properties?.id !== 'polygon' + ); + } - const onClickPolyline = e => { - const coord = e.coord; + if (features.length) { + const id = features[0].properties.id; + geojson.features = geojson.features.filter( + point => point.properties.id !== id + ); + } else { + const point = { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [e.lngLat.lng, e.lngLat.lat] + }, + properties: { id: 'point' } + }; + geojson.features.push(point); + } + + guideLine.properties.id = 'guideline'; + guideLine.geometry.coordinates = [[e.lngLat.lng, e.lngLat.lat]]; + + if (geojson.features.length > 1) { + //point들의 좌표만 뽑아서 Polygon의 좌표로 넣어줌 + polygon.geometry.coordinates = [ + geojson.features + .filter(point => point.properties?.id === 'point') + .map(point => point.geometry.coordinates) + ]; + geojson.features.push(polygon); + } else { + eventListener.rightClickEvent = () => { + finishDraw(); + }; + mapObject.on('contextmenu', eventListener.rightClickEvent); + + eventListener.mousemoveEvent = e => { + onMouseMovePolygon(e); + }; + mapObject.on('mousemove', eventListener.mousemoveEvent); + } + mapObject.getSource('geojson').setData(geojson); + }; - console.log(coord, '>>>coord'); + const onMouseMovePolygon = e => { + 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(); + + geojson.features = geojson.features.filter( + point => point.properties?.id !== 'polygon' + ); + + geojson.features.push(polygon); + } + guideLine.geometry.coordinates.push([e.lngLat.lng, e.lngLat.lat]); + polygon.geometry.coordinates[0].push([e.lngLat.lng, e.lngLat.lat]); + + mapObject.getSource('geojson').setData(geojson); + } + } }; const addMileStone = (coord, text) => {