React+MapBox GL JS引入URL服务地址实现自定义图标标记地点、区域绘制功能
实现说明
-
自定义图标配置:
-
在
customIcons
对象中定义所有类型的图标URL -
支持不同类型的机房、基站和断点使用不同图标
-
-
标记创建函数:
-
addCustomMarker
是一个通用函数,用于创建带有自定义图标的标记 -
可以设置图标URL、标题和自定义CSS类
-
-
标记类型专用函数:
-
addComputerRoomMarkers
- 添加机房标记 -
addBaseStationMarkers
- 添加基站标记 -
addBreakPointMarkers
- 添加断点标记
-
-
图标样式控制:
-
通过CSS控制图标大小(32x32像素)
-
使用
background-image
显示自定义图标 -
添加了悬停提示(title属性)
-
import React, { useState, useEffect, useRef } from 'react';const MapComponent = () => {const mapContainer = useRef(null);const map = useRef(null);const [mapLoaded, setMapLoaded] = useState(false);const [markers, setMarkers] = useState([]);const [legendVisible, setLegendVisible] = useState(true);const [visibleLayers, setVisibleLayers] = useState({computerRooms: true,baseStations: true,breakPoints: true});// 模拟图标URL - 实际使用时替换为您的真实图标URLconst customIcons = {computerRoom: {core: 'https://cdn-icons-png.flaticon.com/512/149/149060.png', // 替换为您的核心机房图标edge: 'https://cdn-icons-png.flaticon.com/512/149/149025.png', // 替换为您的边缘机房图标access: 'https://cdn-icons-png.flaticon.com/512/149/149014.png' // 替换为您的接入机房图标},baseStation: {'5g': 'https://cdn-icons-png.flaticon.com/512/4618/4618295.png', // 5G基站图标'4g': 'https://cdn-icons-png.flaticon.com/512/4618/4618286.png', // 4G基站图标'3g': 'https://cdn-icons-png.flaticon.com/512/4618/4618275.png' // 3G基站图标},breakPoint: 'https://cdn-icons-png.flaticon.com/512/1828/1828665.png' // 断点图标};// 动态加载 Mapbox GL JSuseEffect(() => {if (window.mapboxgl) {setMapLoaded(true);return;}const script = document.createElement('script');script.src = 'https://api.mapbox.com/mapbox-gl-js/v2.2.0/mapbox-gl.js';script.onload = () => {const link = document.createElement('link');link.href = 'https://api.mapbox.com/mapbox-gl-js/v2.2.0/mapbox-gl.css';link.rel = 'stylesheet';document.head.appendChild(link);setMapLoaded(true);};script.onerror = () => console.error('Failed to load Mapbox GL JS');document.head.appendChild(script);return () => {document.head.removeChild(script);};}, []);// 添加带有自定义图标的标记const addCustomMarker = (lnglat, iconUrl, title, className = '') => {if (!map.current) return null;const el = document.createElement('div');el.className = `custom-marker ${className}`;el.style.width = '32px';el.style.height = '32px';el.style.backgroundImage = `url(${iconUrl})`;el.style.backgroundSize = 'contain';el.style.backgroundRepeat = 'no-repeat';el.style.cursor = 'pointer';if (title) el.title = title;const marker = new window.mapboxgl.Marker({element: el,anchor: 'bottom' // 图标底部对准坐标点}).setLngLat(lnglat).addTo(map.current);return marker;};// 添加机房标记const addComputerRoomMarkers = (rooms) => {if (!map.current) return;const newMarkers = rooms.map(room => {const marker = addCustomMarker([room.lng, room.lat],customIcons.computerRoom[room.type],room.name,`computer-room ${room.type}`);return { id: room.id, marker, type: 'computerRoom', originalType: room.type };});setMarkers(prev => [...prev, ...newMarkers]);};// 添加基站标记const addBaseStationMarkers = (stations) => {if (!map.current) return;const newStations = stations.map(station => {const marker = addCustomMarker([station.lng, station.lat],customIcons.baseStation[station.type],`${station.name} (${station.type.toUpperCase()})`,`base-station ${station.type}`);return { id: station.id, marker, type: 'baseStation', originalType: station.type };});setMarkers(prev => [...prev, ...newStations]);};// 添加断点标记const addBreakPointMarkers = (points) => {if (!map.current) return;const newMarkers = points.map(point => {const marker = addCustomMarker([point.lng, point.lat],customIcons.breakPoint,point.title,'break-point');return { id: point.id, marker, type: 'breakPoint' };});setMarkers(prev => [...prev, ...newMarkers]);};// 切换图层可见性const toggleLayerVisibility = (layerType) => {const newVisibility = !visibleLayers[layerType];setVisibleLayers(prev => ({ ...prev, [layerType]: newVisibility }));markers.forEach(marker => {if (marker.type === layerType || (layerType === 'computerRooms' && marker.type === 'computerRoom') ||(layerType === 'baseStations' && marker.type === 'baseStation') ||(layerType === 'breakPoints' && marker.type === 'breakPoint')) {const element = marker.marker.getElement();if (element) {element.style.display = newVisibility ? 'block' : 'none';}}});};// 初始化地图useEffect(() => {if (!mapLoaded || !mapContainer.current) return;map.current = new window.mapboxgl.Map({container: mapContainer.current,style: 'mapbox://styles/mapbox/streets-v11', // 使用默认地图样式center: [111.62299, 40.80772],zoom: 10,minZoom: 7,maxZoom: 19});map.current.on('load', () => {// 添加机房标记const computerRooms = [{ id: 1, name: '核心机房', lng: 111.62, lat: 40.81, type: 'core' },{ id: 2, name: '边缘机房', lng: 111.63, lat: 40.80, type: 'edge' },{ id: 3, name: '接入机房', lng: 111.61, lat: 40.805, type: 'access' }];addComputerRoomMarkers(computerRooms);// 添加基站标记const baseStations = [{ id: 101, name: '5G基站A', lng: 111.615, lat: 40.808, type: '5g' },{ id: 102, name: '4G基站B', lng: 111.625, lat: 40.806, type: '4g' },{ id: 103, name: '3G基站C', lng: 111.635, lat: 40.804, type: '3g' }];addBaseStationMarkers(baseStations);// 添加断点标记const breakPoints = [{ id: 'bp1', lng: 111.625, lat: 40.806, title: '光缆断点1' },{ id: 'bp2', lng: 111.635, lat: 40.8045, title: '光缆断点2' }];addBreakPointMarkers(breakPoints);});return () => {if (map.current) {map.current.remove();}};}, [mapLoaded]);if (!mapLoaded) {return <div style={{display: 'flex',justifyContent: 'center',alignItems: 'center',height: '100vh',fontSize: '18px',color: '#666'}}>正在加载地图资源...</div>;}return (<div style={{ position: 'relative', width: '100%', height: '100vh' }}><div ref={mapContainer} style={{ width: '100%', height: '100%' }} />{/* 图例悬浮框 */}{legendVisible && (<div style={{position: 'absolute',bottom: '20px',right: '20px',backgroundColor: 'white',padding: '15px',borderRadius: '5px',boxShadow: '0 0 10px rgba(0,0,0,0.2)',zIndex: 1,maxWidth: '250px'}}><h3 style={{ marginTop: 0, marginBottom: '15px' }}>地图图例</h3>{/* 机房图例 */}<div style={{ marginBottom: '15px' }}><label style={{ display: 'flex', alignItems: 'center', cursor: 'pointer', marginBottom: '5px' }}><input type="checkbox" checked={visibleLayers.computerRooms}onChange={() => toggleLayerVisibility('computerRooms')}style={{ marginRight: '8px' }}/><span>显示机房</span></label><div style={{ marginLeft: '25px' }}><div style={{ display: 'flex', alignItems: 'center', marginBottom: '3px' }}><img src={customIcons.computerRoom.core} style={{ width: '20px', height: '20px', marginRight: '8px' }} alt="核心机房" /><span>核心机房</span></div><div style={{ display: 'flex', alignItems: 'center', marginBottom: '3px' }}><img src={customIcons.computerRoom.edge} style={{ width: '20px', height: '20px', marginRight: '8px' }} alt="边缘机房" /><span>边缘机房</span></div><div style={{ display: 'flex', alignItems: 'center' }}><img src={customIcons.computerRoom.access} style={{ width: '20px', height: '20px', marginRight: '8px' }} alt="接入机房" /><span>接入机房</span></div></div></div>{/* 基站图例 */}<div style={{ marginBottom: '15px' }}><label style={{ display: 'flex', alignItems: 'center', cursor: 'pointer', marginBottom: '5px' }}><input type="checkbox" checked={visibleLayers.baseStations}onChange={() => toggleLayerVisibility('baseStations')}style={{ marginRight: '8px' }}/><span>显示基站</span></label><div style={{ marginLeft: '25px' }}><div style={{ display: 'flex', alignItems: 'center', marginBottom: '3px' }}><img src={customIcons.baseStation['5g']} style={{ width: '20px', height: '20px', marginRight: '8px' }} alt="5G基站" /><span>5G基站</span></div><div style={{ display: 'flex', alignItems: 'center', marginBottom: '3px' }}><img src={customIcons.baseStation['4g']} style={{ width: '20px', height: '20px', marginRight: '8px' }} alt="4G基站" /><span>4G基站</span></div><div style={{ display: 'flex', alignItems: 'center' }}><img src={customIcons.baseStation['3g']} style={{ width: '20px', height: '20px', marginRight: '8px' }} alt="3G基站" /><span>3G基站</span></div></div></div>{/* 断点图例 */}<div><label style={{ display: 'flex', alignItems: 'center', cursor: 'pointer', marginBottom: '5px' }}><input type="checkbox" checked={visibleLayers.breakPoints}onChange={() => toggleLayerVisibility('breakPoints')}style={{ marginRight: '8px' }}/><span>显示断点</span></label><div style={{ marginLeft: '25px' }}><div style={{ display: 'flex', alignItems: 'center' }}><img src={customIcons.breakPoint} style={{ width: '20px', height: '20px', marginRight: '8px' }} alt="断点" /><span>光缆断点</span></div></div></div></div>)}{/* 显示/隐藏图例按钮 */}<button onClick={() => setLegendVisible(!legendVisible)}style={{position: 'absolute',bottom: '20px',right: legendVisible ? '280px' : '20px',zIndex: 1,padding: '6px 12px',backgroundColor: 'white',border: '1px solid #ccc',borderRadius: '4px',cursor: 'pointer',fontSize: '14px',boxShadow: '0 0 5px rgba(0,0,0,0.1)'}}>{legendVisible ? '隐藏图例' : '显示图例'}</button></div>);
};export default MapComponent;