Mapbox GL初探
一、准备工作
1. 注册Mapbox账号并获取Token
-
访问 Mapbox官网 注册账号。
-
在Dashboard中创建新项目,生成Access Token(用于地图初始化)
2.安装Mapbox GL JS
在HTML中引入Mapbox库:
<script src='https://api.mapbox.com/mapbox-gl-js/v3.2.0/mapbox-gl.js'></script>
<link href='https://api.mapbox.com/mapbox-gl-js/v3.2.0/mapbox-gl.css' rel='stylesheet' />
二、基础地图展示
1. 初始化地图
<div id="map" style="width: 100%; height: 600px;"></div> <!-- 地图容器 --><script>mapboxgl.accessToken = 'YOUR_ACCESS_TOKEN'; const map = new mapboxgl.Map({container: 'map', // 容器IDstyle: 'mapbox://styles/mapbox/streets-v12', // 地图样式center: [116.4, 39.9], // 初始中心点 [经度, 纬度]zoom: 10, // 缩放级别pitch: 45, // 倾斜角度(3D效果)bearing: -17.6 // 旋转角度});
</script>
2. 添加控件
map.addControl(new mapboxgl.NavigationControl(), 'top-left'); // 缩放/旋转控件
map.addControl(new mapboxgl.ScaleControl()); // 比例尺
三、图层管理:数据展示核心
Mapbox通过数据源(Source) 和 图层(Layer) 分离机制管理地图内容。
1. 数据源类型
2.添加栅格图像
map.on('load', () => {map.addSource('radar', {'type': 'image','url': 'https://docs.mapbox.com/mapbox-gl-js/assets/radar.gif','coordinates': [[-80.425, 46.437],[-71.516, 46.437],[-71.516, 37.936],[-80.425, 37.936]]});map.addLayer({id: 'radar-layer','type': 'raster','source': 'radar','paint': {'raster-fade-duration': 0,'raster-emissive-strength': 1}});
});
3.自定义编辑
基于Mapbox GL Draw实现绘图组件,支持地图绘制、测量、标签显示功能。同时实现UI和逻辑分离的设计,使用return暴露必要方法给组件。
import { ref } from 'vue';
import MapboxDraw from '@mapbox/mapbox-gl-draw';
import * as turf from '@turf/turf';
import type { FeaturesMeta, MapLabelMeta } from '@/types/map-features';
import type {AllGeoJSON,Feature,FeatureCollection,Geometry,GeometryCollection,LineString
} from '@turf/turf';export const useMapToolBarHook = () => {// 定义 map 和 drawTool 的引用const mapRef = ref<mapboxgl.Map | null>(null);const drawToolRef = ref<MapboxDraw | null>(null);// 清除地图上的测量标签图层与数据源const clearMeasure = (id?: string) => {try {mapRef.value?.removeLayer(id!);mapRef.value?.removeSource(id!);} catch (error) {console.error(error);}};// 初始化地图并绑定绘制事件const initMap = (map: mapboxgl.Map, drawTool: MapboxDraw) => {mapRef.value = map;drawToolRef.value = drawTool;map.addControl(drawTool);map.on('draw.create', createMeasure);map.on('draw.update', updateMeasure);map.on('draw.changeMode', clearMeasure);};// 计算线的中点坐标const getCenterOfLine = (coords: Feature<any> | FeatureCollection<any> | GeometryCollection) => {const totalLength = turf.length(coords, { units: 'kilometers' });const midPoint = turf.along(coords as Feature<LineString>, totalLength / 2);return midPoint.geometry.coordinates;};// 创建地图文本标签const createLabel = (text: string,coords: any,feature: FeaturesMeta) => {const _id = `label-${feature.id}`;const label = {type: 'FeatureCollection',features: [{type: 'Feature',geometry: {type: 'Point',coordinates: coords},properties: { text }}]};mapRef.value?.addSource(_id, { type: 'geojson', data: label });mapRef.value?.addLayer({id: _id,source: _id,type: 'symbol',layout: {'text-field': ['get', 'text'],'text-size': 16,'text-offset': [0, -1]},paint: {'text-color': '#ffffff'}});return label;};// 添加长度标签(单位:km)const addLengthLabel = (length: number,coords: any,feature: FeaturesMeta) => {const label = `${length.toFixed(2)} km`;createLabel(label, coords, feature);};// 添加面积标签(单位:km²)const addAreaLabel = (area: number,coords: any,feature: FeaturesMeta) => {const label = `${area.toFixed(2)} km²`;createLabel(label, coords, feature);};// 创建测量逻辑封装:直接调用 updateMeasureconst createMeasure = (e: any) => {updateMeasure(e, true);};// 更新测量:根据类型计算长度/面积,并添加标签const updateMeasure = (e: any, iscreate = false) => {const features: FeaturesMeta = e.features[0];if (!features) return;// 不是创建行为时,清除旧标签if (!iscreate) clearMeasure(`label-${features.id}`);const handlerMap = {LineString: (feature: FeaturesMeta) => {const length = turf.length(feature, { units: 'kilometers' });const coords = getCenterOfLine(feature);addLengthLabel(length, coords, feature);},Polygon: (feature: FeaturesMeta) => {const area = turf.area(feature as AllGeoJSON) / 1000000;const coords = turf.centroid(feature as AllGeoJSON).geometry.coordinates;addAreaLabel(area, coords, feature);}};const geometryType = features.geometry.type;const handler = (handlerMap as any)[geometryType];if (handler) handler(features);};// 启用绘制多边形模式const drawPolygon = () => {drawToolRef.value?.changeMode('draw_polygon');};// 启用绘制线模式const drawLine = () => {drawToolRef.value?.changeMode('draw_line_string');};// 启用绘制点模式const drawPoint = () => {drawToolRef.value?.changeMode('draw_point');};// 删除选中的绘制元素并清除对应标签const deleteDraw = () => {const ids = drawToolRef.value?.getSelectedIds();if (!ids || ids.length === 0) return;const selection = drawToolRef.value?.getSelected() as MapLabelMeta;if (selection?.features?.length > 0 &&selection.features[0].geometry.type !== 'Point') {clearMeasure(`label-${ids[0]}`);}drawToolRef.value?.delete(ids);};// 将函数暴露给组件外部使用return({initMap,drawPolygon,drawLine,drawPoint,deleteDraw});
}