MapBox GL地图上绘制圆形区域,在区域中心点添加标记点及文本提示的实现方法:
// 绘制影响区域
const addArea = (circle) => {if (!map.current || !circle) return;const areaId = `circle-area`;const epicenterId = `circle-epicenter`;const radiusKm = circle.strongRadius;// 使用传入的中心点坐标const epicenter = [parseFloat(circle.longitude), parseFloat(circle.latitude)];// 清除旧图层clearLayers();// 创建圆形多边形const polygon = createCirclePolygon(epicenter, radiusKm);// 添加数据源map.current.addSource(areaId, {type: 'geojson',data: polygon});// 添加区域填充层map.current.addLayer({id: `${areaId}-fill`,type: 'fill',source: areaId,paint: {'fill-color': '#FF4D4F','fill-opacity': 0.2}});// 添加区域边框层map.current.addLayer({id: `${areaId}-border`,type: 'line',source: areaId,paint: {'line-color': '#FF4D4F','line-width': 3,'line-dasharray': [2, 2]}});// 创建文字元素并添加到地图const textElement = document.createElement('div');textElement.className = 'circle-text';textElement.style.cssText = `position: absolute;transform: translate(-50%, -100%);background: rgba(0, 0, 0, 0.9);color: white;padding: 6px 10px;border-radius: 6px;font-size: 12px;font-weight: bold;white-space: pre-line;text-align: center;max-width: 150px;word-break: break-word;z-index: 10;pointer-events: none;border: 2px solid #ff4d4f;box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);line-height: 1.4;`;textElement.textContent = `覆盖半径为${radiusKm || 'XX'}公里`;// 将文字元素添加到地图容器const mapContainerEl = map.current.getContainer();mapContainerEl.appendChild(textElement);// 更新文字位置随地图移动const updateTextPosition = () => {const point = map.current.project(epicenter);textElement.style.left = `${point.x}px`;textElement.style.top = `${point.y}px`;};map.current.on('move', updateTextPosition);updateTextPosition();// 创建图标标记 - 只创建一个标记const iconElement = document.createElement('div');iconElement.className = 'circle-icon';iconElement.style.width = '40px';iconElement.style.height = '40px';iconElement.style.backgroundImage = `url(${require('@assets/images/map/earthquakeCenter.png')})`;iconElement.style.backgroundSize = 'contain';iconElement.style.backgroundRepeat = 'no-repeat';iconElement.style.cursor = 'pointer';iconElement.title = `覆盖半径为${radiusKm || 'XX'}公里`;const marker = new window.mapboxgl.Marker({element: iconElement,anchor: 'center'}).setLngLat(epicenter).addTo(map.current);// 管理标记状态setMarkers(prev => [...prev, {id: epicenterId,marker: marker,element: iconElement,type: 'circle',textElement: textElement,updateTextPosition: updateTextPosition}]);
};// 清理图层
const clearLayers = () => {const areaId = 'circle-area';const epicenterId = 'circle-epicenter';if (map.current) {// 移除图层if (map.current.getLayer(`${areaId}-fill`)) map.current.removeLayer(`${areaId}-fill`);if (map.current.getLayer(`${areaId}-border`)) map.current.removeLayer(`${areaId}-border`);if (map.current.getSource(areaId)) map.current.removeSource(areaId);// 使用函数式更新来访问最新的markers状态setMarkers(prev => {// 在当前作用域内清理标记prev.forEach(marker => {if (marker.id === epicenterId) {if (marker.marker) {marker.marker.remove();}if (marker.textElement && marker.textElement.parentNode) {marker.textElement.parentNode.removeChild(marker.textElement);}if (marker.updateTextPosition) {map.current.off('move', marker.updateTextPosition);}}});// 从状态中移除标记return prev.filter(marker => marker.id !== epicenterId);});}
};// 创建精确的圆形多边形(考虑投影变形)const createCirclePolygon = (center, radiusKm, points = 64) => {const coords = [];const latRad = center[1] * Math.PI / 180;// 计算经度和纬度的每公里度数const latKm = 1 / 110.574;const lngKm = 1 / (111.320 * Math.cos(latRad));for (let i = 0; i < points; i++) {const angle = (i / points) * 2 * Math.PI;const dx = radiusKm * Math.cos(angle) * lngKm;const dy = radiusKm * Math.sin(angle) * latKm;coords.push([center[0] + dx, center[1] + dy]);}// 闭合多边形coords.push(coords[0]);return {type: 'Feature',geometry: {type: 'Polygon',coordinates: [coords]},properties: {radius: radiusKm}};};
上述方法的调用:
addArea({depth: 10,latitude: 40.62,magnitude: 3.55,strongRadius: 4.5,longitude: 115.369
});