MapBox GL地图上绘制圆形区域,在区域中心点添加标记点及文本提示的实现方法:
javascript
// 绘制影响区域
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
}
};
};
上述方法的调用:
javascript
addArea({
depth: 10,
latitude: 40.62,
magnitude: 3.55,
strongRadius: 4.5,
longitude: 115.369
});