Browse Source

거리측정 피처 이동 제한 기능 및 draw 관련 함수 작성

master
김장현 2 months ago
parent
commit
5892ad8660
  1. 5
      package-lock.json
  2. 1
      package.json
  3. 75
      src/components/map/mapbox/MapBoxMap.js
  4. 72
      src/containers/flight/OperationApprovalsContainer.js
  5. 101
      src/utility/MapUtils.js

5
package-lock.json generated

@ -14305,6 +14305,11 @@
} }
} }
}, },
"mapbox-gl-draw-waypoint": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/mapbox-gl-draw-waypoint/-/mapbox-gl-draw-waypoint-1.2.3.tgz",
"integrity": "sha512-O6vAAKjPTcTyPth0Y4DI/QDLljFitRYM5f3iGgTNNgJa/KKPd1MrqbM0h5HPQ6EfTWpUGG878dsIsMq8agOUsw=="
},
"marker-clusterer-plus": { "marker-clusterer-plus": {
"version": "2.1.4", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/marker-clusterer-plus/-/marker-clusterer-plus-2.1.4.tgz", "resolved": "https://registry.npmjs.org/marker-clusterer-plus/-/marker-clusterer-plus-2.1.4.tgz",

1
package.json

@ -60,6 +60,7 @@
"leaflet": "1.6.0", "leaflet": "1.6.0",
"mapbox-gl": "^2.15.0", "mapbox-gl": "^2.15.0",
"mapbox-gl-draw-circle": "1.1.2", "mapbox-gl-draw-circle": "1.1.2",
"mapbox-gl-draw-waypoint": "1.2.3",
"nouislider": "14.6.2", "nouislider": "14.6.2",
"nouislider-react": "3.3.8", "nouislider-react": "3.3.8",
"postcss-rtl": "1.5.0", "postcss-rtl": "1.5.0",

75
src/components/map/mapbox/MapBoxMap.js

@ -96,7 +96,7 @@ export default function MapBoxMap({ handlerDrawObjInit }) {
// 노탐 정보 모달 // 노탐 정보 모달
const [centeredModal, setCenteredModal] = useState(false); const [centeredModal, setCenteredModal] = useState(false);
const mouseCursorRef = useRef(null); // 거리 측정
// 비행예상경로 geoJson 정보 // 비행예상경로 geoJson 정보
const [planGeo, setPlanGeo] = useState({ const [planGeo, setPlanGeo] = useState({
@ -572,72 +572,11 @@ export default function MapBoxMap({ handlerDrawObjInit }) {
setMapLoaded(true); setMapLoaded(true);
}); });
map.on('click', e => {
console.log(drawObj.getMode());
if (drawObj.getMode() === 'draw_line_string') {
startPoint = e.lngLat;
}
});
map.on('mousemove', e => {
let distance = 0;
if (startPoint) {
// console.log(drawObj.getAll());
const feature = [];
drawObj
.getAll()
.features[0].geometry.coordinates.map(i => feature.push(i));
const obj = {
geometry: {
coordinates: [...feature],
type: 'LineString'
},
type: 'Feature'
};
distance = turf.length(obj, { units: 'kilometers' });
distance = distance * 1000;
distance = distance.toFixed(2);
mouseCursorRef.current.style.left = e.originalEvent.pageX + 'px';
mouseCursorRef.current.style.top = e.originalEvent.pageY + 45 + 'px';
mouseCursorRef.current.innerText = `${distance.toLocaleString()}m`;
// const endPoint = e.lngLat;
// const distance = calculateDistance(startPoint, endPoint);
// mouseCursorRef.current.innerText = `Distance from start point: ${distance.toFixed(
// 2
// )} meters`;
if (drawObj.getMode() === 'simple_select') {
// startPoint = null;
const box = document.getElementById('distance_box');
box.innerText = `${distance.toLocaleString()}m`;
box.style.display = 'block';
mouseCursorRef.current.style.display = 'none';
mouseCursorRef.current.style.innerText = '';
}
if (drawObj.getMode() === 'direct_select') {
const box = document.getElementById('distance_box');
box.innerText = `${distance.toLocaleString()}km`;
}
} else {
if (drawObj.getMode() === 'draw_line_string') {
mouseCursorRef.current.style.display = 'block';
mouseCursorRef.current.style.left = e.originalEvent.pageX + 'px';
mouseCursorRef.current.style.top = e.originalEvent.pageY + 45 + 'px';
mouseCursorRef.current.innerText = '시작점 선택';
}
}
});
handlerDrawObjInit(drawObj); handlerDrawObjInit(drawObj);
setMapObject(map); setMapObject(map);
dispatch(clientMapInit(map)); dispatch(clientMapInit(map));
}; };
function calculateDistance(point1, point2) {
return mapboxgl.MercatorCoordinate.fromLngLat(point1).distanceTo(
mapboxgl.MercatorCoordinate.fromLngLat(point2)
);
}
return ( return (
<> <>
<div <div
@ -645,18 +584,6 @@ export default function MapBoxMap({ handlerDrawObjInit }) {
ref={mapContainer} ref={mapContainer}
style={{ width: '100%', height: '100vh' }} style={{ width: '100%', height: '100vh' }}
></div> ></div>
<div
ref={mouseCursorRef}
style={{
position: 'absolute',
display: 'none',
background: '#8a1c05',
color: '#fff',
padding: '5px',
borderRadius: '5px',
fontWeight: 500
}}
></div>
{isMapLoaded && mapObject ? ( {isMapLoaded && mapObject ? (
<> <>

72
src/containers/flight/OperationApprovalsContainer.js

@ -9,7 +9,8 @@ import {
flightlayerPolyline, flightlayerPolyline,
flightlayerPolygon, flightlayerPolygon,
flightlayerBuffer, flightlayerBuffer,
handlerStartMode handlerStartMode,
handlerOnClickDrawLineString
} from '../../utility/MapUtils'; } from '../../utility/MapUtils';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import useMapType from '@hooks/useMapType'; import useMapType from '@hooks/useMapType';
@ -48,6 +49,7 @@ import { X } from 'react-feather';
import mapboxgl from 'mapbox-gl'; import mapboxgl from 'mapbox-gl';
import * as turf from '@turf/turf'; import * as turf from '@turf/turf';
let distanceMarkers = [];
export default function OperationApprovalsContainer({ mode }) { export default function OperationApprovalsContainer({ mode }) {
const dispatch = useDispatch(); const dispatch = useDispatch();
const history = useHistory(); const history = useHistory();
@ -62,6 +64,7 @@ export default function OperationApprovalsContainer({ mode }) {
// 비행구역 그리기 // 비행구역 그리기
const [drawObj, setDrawObj] = useState(); const [drawObj, setDrawObj] = useState();
const [filter, setFilter] = useState(''); const [filter, setFilter] = useState('');
const mouseCursorRef = useRef(null);
// 지도 // 지도
const [mapObject, setMapObject] = useState(); const [mapObject, setMapObject] = useState();
@ -133,6 +136,7 @@ export default function OperationApprovalsContainer({ mode }) {
window._mapbox = map; window._mapbox = map;
let mapInstance = mode === 'container' ? map : window.opener._mapbox; let mapInstance = mode === 'container' ? map : window.opener._mapbox;
setMapObject(mapInstance); setMapObject(mapInstance);
handlerOnClickDrawLineString(mapInstance, handlerDrawMarker);
} }
}, [map]); }, [map]);
@ -462,11 +466,8 @@ export default function OperationApprovalsContainer({ mode }) {
}; };
const handlerDrawTypeChange = (e, val) => { const handlerDrawTypeChange = (e, val) => {
// const cursor = document.getElementById('distance_cursor'); drawObj.deleteAll();
// cursor.style.display = 'block'; distanceMarkers.map(i => i.remove());
// cursor.style.left = e.clientX + 'px';
// cursor.style.top = e.clientY + 45 + 'px';
// cursor.innerText = '시작점 선택';
dispatch(clientChangeDrawType(val)); dispatch(clientChangeDrawType(val));
handlerStartMode(val, drawObj); handlerStartMode(val, drawObj);
}; };
@ -480,6 +481,36 @@ export default function OperationApprovalsContainer({ mode }) {
setMapType(val); setMapType(val);
}; };
const handlerDistanceClose = () => {
drawObj.deleteAll();
dispatch(clientChangeDrawType(''));
document.getElementById('distance_box').style.display = 'none';
};
const handlerDrawMarker = (mapInstance, markerList, startPoint) => {
const marker = markerList.filter(i => {
return i.coord[0][0] === startPoint.lng;
});
if (marker.length > 0) {
let distanceMarker = new mapboxgl.Popup({
closeButton: false,
closeOnClick: false,
anchor: 'bottom',
focusAfterOpen: false
})
.setLngLat([
marker[0].coord[0][0].toFixed(6),
marker[0].coord[0][1].toFixed(6)
])
.setHTML(
`<div style="color:#000000;font-weight:400;">${marker[0].text}</div>`
)
.addTo(mapInstance);
distanceMarkers.push(distanceMarker);
}
};
const handlerLogout = async () => { const handlerLogout = async () => {
const { payload } = await dispatch(setLogout()); const { payload } = await dispatch(setLogout());
@ -493,6 +524,18 @@ export default function OperationApprovalsContainer({ mode }) {
return ( return (
<> <>
<div className='map' style={{ width: '100%' }}> <div className='map' style={{ width: '100%' }}>
<div
ref={mouseCursorRef}
style={{
position: 'absolute',
display: 'none',
background: '#8a1c05',
color: '#fff',
padding: '5px',
borderRadius: '5px',
fontWeight: 500
}}
></div>
<div className='test_modal'> <div className='test_modal'>
<Button <Button
color='primary' color='primary'
@ -770,10 +813,14 @@ export default function OperationApprovalsContainer({ mode }) {
</ButtonGroup> </ButtonGroup>
</div> </div>
</div> </div>
<div className='data-box-btn-list'> <div
id='distance_box'
className='data-box-btn-list'
style={{ display: 'none' }}
>
<h4>측정</h4> <h4>측정</h4>
<div className='distance-check'> <div className='distance-check'>
<div class='btn-box'> <div className='btn-box'>
<Button <Button
className='btn-icon rounded-circle' className='btn-icon rounded-circle'
color='primary' color='primary'
@ -792,6 +839,7 @@ export default function OperationApprovalsContainer({ mode }) {
className='btn-icon rounded-circle' className='btn-icon rounded-circle'
color='dark' color='dark'
size='sm' size='sm'
onClick={handlerDistanceClose}
> >
<X size={16} /> <X size={16} />
</Button> </Button>
@ -799,7 +847,13 @@ export default function OperationApprovalsContainer({ mode }) {
<div className='text'> <div className='text'>
<p> <p>
지도에서 지점을 클릭하여 거리를 측정하세요. 지도에서 지점을 클릭하여 거리를 측정하세요.
<span className='finish'> 거리 : 1050.24m</span> <span
id='total_distance'
className='finish'
style={{ display: 'none' }}
>
거리 : 1050.24m
</span>
</p> </p>
</div> </div>
</div> </div>

101
src/utility/MapUtils.js

@ -1,6 +1,7 @@
import * as turf from '@turf/turf'; import * as turf from '@turf/turf';
import mapboxgl from 'mapbox-gl'; import mapboxgl from 'mapbox-gl';
import MapboxDraw from '@mapbox/mapbox-gl-draw'; import MapboxDraw from '@mapbox/mapbox-gl-draw';
import * as MapboxDrawWaypoint from 'mapbox-gl-draw-waypoint';
import { import {
CircleMode, CircleMode,
DragCircleMode, DragCircleMode,
@ -477,8 +478,9 @@ export const flightlayerBuffer = source => {
// 비행구역 상세맵 draw 정보 셋팅 // 비행구역 상세맵 draw 정보 셋팅
export const getDraw = mode => { export const getDraw = mode => {
let drawObj = null;
if (mode === 'laanc') { if (mode === 'laanc') {
return new MapboxDraw({ drawObj = new MapboxDraw({
displayControlsDefault: false, displayControlsDefault: false,
userProperties: true, userProperties: true,
boxSelect: false, boxSelect: false,
@ -568,16 +570,17 @@ export const getDraw = mode => {
] ]
}); });
} else { } else {
return new MapboxDraw({ let modes = MapboxDraw.modes;
modes = MapboxDrawWaypoint.enable(modes);
drawObj = new MapboxDraw({
displayControlsDefault: false, displayControlsDefault: false,
userProperties: true, userProperties: true,
boxSelect: false, boxSelect: false,
modes: { modes: {
...MapboxDraw.modes, ...modes,
draw_circle: CircleMode, draw_circle: CircleMode,
drag_circle: DragCircleMode, drag_circle: DragCircleMode
direct_select: DirectMode,
simple_select: SimpleSelectMode
}, },
styles: [ styles: [
{ {
@ -643,6 +646,7 @@ export const getDraw = mode => {
// vertex points // vertex points
id: 'gl-draw-polygon-and-line-vertex-active', id: 'gl-draw-polygon-and-line-vertex-active',
type: 'circle', type: 'circle',
filter: ['all', ['==', '$type', 'Point']],
paint: { paint: {
'circle-radius': 6, 'circle-radius': 6,
'circle-color': '#8a1c05' 'circle-color': '#8a1c05'
@ -651,6 +655,8 @@ export const getDraw = mode => {
] ]
}); });
} }
return drawObj;
}; };
/** /**
@ -666,3 +672,86 @@ export const handlerStartMode = (mode, drawObj) => {
drawObj.changeMode('draw_circle', { initialRadiusInKm: 100 / 1000 }); drawObj.changeMode('draw_circle', { initialRadiusInKm: 100 / 1000 });
} }
}; };
export const handlerOnClickDrawLineString = (mapInstance, callback) => {
const originClickHandler = MapboxDraw.modes.draw_line_string.onClick;
const originMouseMoveHandler = MapboxDraw.modes.draw_line_string.onMouseMove;
let startPoint;
let markerList = [];
MapboxDraw.modes.draw_line_string.onClick = function (state, e) {
originClickHandler.call(this, state, e);
startPoint = e.lngLat;
state.line.coordinates.map((i, idx) => {
if (
i[0] !== state.line.coordinates[state.line.coordinates.length - 1][0]
) {
const obj = {
geometry: {
coordinates: [i, state.line.coordinates[idx + 1]],
type: 'LineString'
},
type: 'Feature'
};
let distance = turf.length(obj, { units: 'kilometers' });
distance = distance * 1000;
distance = distance.toFixed(2);
markerList.push({
text: `${distance.toLocaleString()}m`,
coord: [state.line.coordinates[idx + 1]]
});
}
});
callback(mapInstance, markerList, startPoint);
};
MapboxDraw.modes.draw_line_string.onMouseMove = function (state, e) {
originMouseMoveHandler.call(this, state, e);
let distance = 0;
console.log;
if (startPoint) {
console.log(state);
const drawGeometry = state.line.coordinates;
if (drawGeometry) {
const feature = [];
drawGeometry.map(i => feature.push(i));
const obj = {
geometry: {
coordinates: [...feature],
type: 'LineString'
},
type: 'Feature'
};
distance = turf.length(obj, { units: 'kilometers' });
distance = distance * 1000;
distance = distance.toFixed(2);
// mouseCursorRef.current.style.display = 'none';
}
// if (drawObj.getMode() === 'simple_select') {
// // startPoint = null;
// const totalDistance = document.getElementById('total_distance');
// totalDistance.style.display = 'block';
// totalDistance.innerText = `총 거리 : ${distance.toLocaleString()}m`;
// mouseCursorRef.current.style.display = 'none';
// mouseCursorRef.current.style.innerText = '';
// }
// if (drawObj.getMode() === 'direct_select') {
// distanceMarkers.map(i => i.remove());
// distanceMarkers = [];
// const totalDistance = document.getElementById('total_distance');
// totalDistance.innerText = `총 거리 : ${distance.toLocaleString()}m`;
// }
}
// else {
// if (drawObj.getMode() === 'draw_line_string') {
// document.getElementById('distance_box').style.display = 'block';
// mouseCursorRef.current.style.display = 'block';
// mouseCursorRef.current.style.left = e.originalEvent.pageX + 'px';
// mouseCursorRef.current.style.top = e.originalEvent.pageY + 45 + 'px';
// mouseCursorRef.current.innerText = '시작점 선택';
// }
// }
};
};

Loading…
Cancel
Save