实现功能如下:
-
拖拽顶点:修改形状
-
拖拽内部:移动多边形
-
按住Shift拖拽任意顶点:围绕中心点旋转
-
拖拽过程中按Esc取消
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Mapbox GL Draw 多边形编辑(中心点旋转)</title> <script src='https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.js'></script> <link href='https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.css' rel='stylesheet' /> <script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.1/mapbox-gl-draw.js'></script> <link href='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.1/mapbox-gl-draw.css' rel='stylesheet' /> <style> body { margin: 0; padding: 0; } #map { position: absolute; top: 0; bottom: 0; width: 100%; } .control-panel { position: absolute; top: 10px; right: 10px; background: white; padding: 15px; border-radius: 5px; box-shadow: 0 1px 3px rgba(0,0,0,0.3); max-width: 300px; max-height: 80vh; overflow-y: auto; } button { margin: 5px; padding: 8px 15px; background: #4264fb; color: white; border: none; border-radius: 3px; cursor: pointer; } button:hover { background: #3254eb; } .hint { position: absolute; bottom: 10px; left: 10px; background: white; padding: 10px; border-radius: 5px; box-shadow: 0 1px 3px rgba(0,0,0,0.3); font-size: 12px; } </style> </head> <body>多边形编辑
<button id="loadPolygon">加载多边形</button> <button id="editMode">编辑模式</button> <button id="viewMode">查看模式</button></body> </html><script> // 设置Mapbox访问令牌 mapboxgl.accessToken = ''; // 初始化地图 const map = new mapboxgl.Map({ container: 'map', style: 'mapbox://styles/mapbox/streets-v12', center: [116.4074, 39.9042], zoom: 12 }); // 初始化Draw插件 const draw = new MapboxDraw({ displayControlsDefault: false, controls: { polygon: true, trash: true }, styles: [ { 'id': 'gl-draw-polygon-fill', 'type': 'fill', 'filter': ['all', ['==', '$type', 'Polygon']], 'paint': { 'fill-color': '#fbb03b', 'fill-opacity': 0.3 } }, { 'id': 'gl-draw-polygon-stroke', 'type': 'line', 'filter': ['all', ['==', '$type', 'Polygon']], 'paint': { 'line-color': '#fbb03b', 'line-width': 2 } }, { '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' } }, { 'id': 'gl-draw-polygon-and-line-vertex-active', 'type': 'circle', 'filter': ['all', ['==', 'meta', 'vertex'], ['==', '$type', 'Point'], ['!=', 'mode', 'static']], 'paint': { 'circle-radius': 6, 'circle-color': '#fbb03b' } } ] }); // 添加Draw控件到地图 map.addControl(draw); // 添加导航控件 map.addControl(new mapboxgl.NavigationControl()); // 存储当前多边形的坐标 let currentPolygonCoordinates = null; let rotationData = { isRotating: false, startPoint: null, originalFeature: null, centerPoint: null, startAngle: null }; // 示例多边形坐标(不规则多边形) const samplePolygon = { type: 'Feature', geometry: { type: 'Polygon', coordinates: [[ [116.3906, 39.9186], [116.4031, 39.9150], [116.4050, 39.9074], [116.3950, 39.9050], [116.3906, 39.9186] ]] }, properties: {} }; // 加载多边形 document.getElementById('loadPolygon').addEventListener('click', () => { draw.deleteAll(); draw.add(samplePolygon); currentPolygonCoordinates = samplePolygon.geometry.coordinates[0]; updateCoordinatesDisplay(); }); // 进入编辑模式 document.getElementById('editMode').addEventListener('click', () => { const features = draw.getAll(); if (features.features.length > 0) { const featureIds = features.features.map(f => f.id); draw.changeMode('simple_select', { featureIds: featureIds }); } else { alert('请先加载多边形!'); } }); // 进入查看模式 document.getElementById('viewMode').addEventListener('click', () => { draw.changeMode('simple_select'); }); // 更新坐标显示 function updateCoordinatesDisplay() { if (currentPolygonCoordinates) { const formattedCoords = currentPolygonCoordinates.map(coord => `[${coord[0].toFixed(6)}, ${coord[1].toFixed(6)}]` ).join('<br>'); document.getElementById('coordinates').innerHTML = ` <h4>当前坐标:</h4> <div style="font-size: 12px; max-height: 200px; overflow-y: auto;"> ${formattedCoords} </div> <p>顶点数:${currentPolygonCoordinates.length}</p> `; } } // 计算多边形中心点 function calculatePolygonCenter(coords) { let sumLng = 0; let sumLat = 0; const count = coords.length - 1; // 排除闭合点 for (let i = 0; i < count; i++) { sumLng += coords[i][0]; sumLat += coords[i][1]; } return { lng: sumLng / count, lat: sumLat / count }; } // 旋转多边形(围绕中心点) function rotatePolygonAroundCenter(coords, center, angle) { return coords.map(coord => { // 将坐标转换为相对于中心点的向量 const dx = coord[0] - center.lng; const dy = coord[1] - center.lat; // 应用旋转矩阵(弧度) const cosA = Math.cos(angle); const sinA = Math.sin(angle); // 旋转后的坐标 const rotatedX = dx * cosA - dy * sinA; const rotatedY = dx * sinA + dy * cosA; // 转换回绝对坐标 return [ center.lng + rotatedX, center.lat + rotatedY ]; }); } // 计算两点之间的角度(相对于水平方向) function calculateAngle(point1, point2, center) { const dx1 = point1.lng - center.lng; const dy1 = point1.lat - center.lat; const dx2 = point2.lng - center.lng; const dy2 = point2.lat - center.lat; const angle1 = Math.atan2(dy1, dx1); const angle2 = Math.atan2(dy2, dx2); return angle2 - angle1; } // 监听地图加载完成 map.on('load', () => { // 监听Draw的更新事件 map.on('draw.update', (e) => { const features = e.features; if (features.length > 0) { const feature = features[0]; if (feature.geometry.type === 'Polygon') { currentPolygonCoordinates = feature.geometry.coordinates[0]; updateCoordinatesDisplay(); } } }); // 监听Draw的创建事件 map.on('draw.create', (e) => { const features = e.features; if (features.length > 0) { const feature = features[0]; if (feature.geometry.type === 'Polygon') { currentPolygonCoordinates = feature.geometry.coordinates[0]; updateCoordinatesDisplay(); } } }); // 监听Draw的删除事件 map.on('draw.delete', (e) => { currentPolygonCoordinates = null; document.getElementById('coordinates').innerHTML = ''; }); // 监听Draw的选择变化事件 map.on('draw.selectionchange', (e) => { if (e.features.length > 0) { console.log('进入编辑模式,可以拖拽顶点或按住Shift旋转'); } }); // 鼠标按下事件 - 开始旋转 map.on('mousedown', (e) => { const features = draw.getSelected().features; if (features.length > 0 && e.originalEvent.shiftKey) { const feature = features[0]; if (feature.geometry.type === 'Polygon') { rotationData.isRotating = true; rotationData.startPoint = e.lngLat; rotationData.originalFeature = JSON.parse(JSON.stringify(feature)); // 计算多边形中心点 const coords = feature.geometry.coordinates[0]; rotationData.centerPoint = calculatePolygonCenter(coords); // 计算起始角度(从中心点到鼠标点的角度) rotationData.startAngle = Math.atan2( e.lngLat.lat - rotationData.centerPoint.lat, e.lngLat.lng - rotationData.centerPoint.lng ); e.preventDefault(); } } }); // 鼠标移动事件 - 执行旋转 map.on('mousemove', (e) => { if (rotationData.isRotating && rotationData.startPoint && rotationData.originalFeature && rotationData.centerPoint) { const currentPoint = e.lngLat; // 计算当前角度 const currentAngle = Math.atan2( currentPoint.lat - rotationData.centerPoint.lat, currentPoint.lng - rotationData.centerPoint.lng ); // 计算旋转角度 const rotationAngle = currentAngle - rotationData.startAngle; // 应用旋转 const rotatedCoords = rotatePolygonAroundCenter( rotationData.originalFeature.geometry.coordinates[0], rotationData.centerPoint, rotationAngle ); // 更新图形 const newFeature = { ...rotationData.originalFeature, geometry: { type: 'Polygon', coordinates: [rotatedCoords] } }; // 删除旧图形并添加新图形 const selectedIds = draw.getSelectedIds(); draw.delete(rotationData.originalFeature.id); const addedFeature = draw.add(newFeature); // 保持选中状态 draw.changeMode('simple_select', { featureIds: [addedFeature.id] }); e.preventDefault(); } }); // 鼠标松开事件 - 结束旋转 map.on('mouseup', (e) => { if (rotationData.isRotating) { rotationData.isRotating = false; // 获取最终坐标并更新显示 const features = draw.getSelected().features; if (features.length > 0) { const feature = features[0]; if (feature.geometry.type === 'Polygon') { currentPolygonCoordinates = feature.geometry.coordinates[0]; updateCoordinatesDisplay(); } } // 重置旋转数据 rotationData = { isRotating: false, startPoint: null, originalFeature: null, centerPoint: null, startAngle: null }; } }); // 添加旋转中心点标记(可选,用于调试) function addCenterMarker(center) { const marker = new mapboxgl.Marker({ color: '#ff0000', draggable: false }) .setLngLat([center.lng, center.lat]) .addTo(map); setTimeout(() => marker.remove(), 1000); } // 监听键盘事件,显示旋转中心点 map.on('keydown', (e) => { if (e.key === 'Shift') { const features = draw.getSelected().features; if (features.length > 0) { const feature = features[0]; if (feature.geometry.type === 'Polygon') { const coords = feature.geometry.coordinates[0]; const center = calculatePolygonCenter(coords); addCenterMarker(center); } } } }); // 添加提示信息 const hint = document.createElement('div'); hint.className = 'hint'; hint.innerHTML = ` <strong>操作指南:</strong><br> 1. 拖拽顶点:修改形状<br> 2. 拖拽内部:移动多边形<br> 3. <strong>按住Shift拖拽任意顶点:围绕中心点旋转</strong><br> 4. 拖拽过程中按Esc取消 `; document.getElementById('map').appendChild(hint); // 添加旋转角度显示 const angleDisplay = document.createElement('div'); angleDisplay.className = 'hint'; angleDisplay.style.bottom = '60px'; angleDisplay.style.fontSize = '11px'; angleDisplay.innerHTML = '旋转角度: 0°'; document.getElementById('map').appendChild(angleDisplay); // 监听鼠标移动,更新角度显示 map.on('mousemove', (e) => { if (rotationData.isRotating && rotationData.startAngle !== null) { const currentPoint = e.lngLat; const currentAngle = Math.atan2( currentPoint.lat - rotationData.centerPoint.lat, currentPoint.lng - rotationData.centerPoint.lng ); const rotationAngle = currentAngle - rotationData.startAngle; const rotationDegrees = (rotationAngle * 180 / Math.PI).toFixed(1); angleDisplay.innerHTML = `旋转角度: ${rotationDegrees}°`; angleDisplay.style.display = 'block'; } else { angleDisplay.style.display = 'none'; } }); }); // 防止Shift键被浏览器默认行为占用 window.addEventListener('keydown', (e) => { if (e.key === 'Shift') { e.preventDefault(); } }); window.addEventListener('keyup', (e) => { if (e.key === 'Shift') { e.preventDefault(); } }); </script>