前言
Mapbox GL JS 版本:3.6.0
该博客仅供学习参考,如果您是计划在实际项目中实现该功能,也推荐您直接使用已有的功能库:
- 官方案例:Draw a polygon and calculate its area
- mapbox-gl-draw:mapbox/mapbox-gl-draw: Draw tools for mapbox-gl-js
绘制线段
/*** @description: 绘制线*/
export class DrawLine {nodeCoordinateList; // 节点坐标pointFeatureCollection; // 点GeoJSON集合lineFeatureCollection; // 线GeoJSON集合constructor(map, callback) {this.map = map;this.addPoint = this.addPoint.bind(this);this.end = this.end.bind(this);this.dynamicAction = this.dynamicAction.bind(this);this.callback = callback; // 回调函数}// 初始化图层init() {// 添加线图层this.map.addLayer({id: "_lineLayer",type: "line",source: {type: "geojson",data: this.lineFeatureCollection,},paint: {"line-color": "#ffff00","line-width": 2,"line-opacity": 1,"line-dasharray": [2, 4],},});// 添加动态线图层this.map.addLayer({id: "_dynamicLineLayer",type: "line",source: {type: "geojson",data: this.lineFeatureCollection,},paint: {"line-color": "#ffff00","line-width": 2,"line-opacity": 1,"line-dasharray": [2, 4],},});// 添加点图层this.map.addLayer({id: "_pointLayer",type: "circle",source: {type: "geojson",data: this.pointFeatureCollection,},paint: {"circle-color": "#ffffff","circle-radius": 3,"circle-stroke-width": 2,"circle-stroke-color": "#ffff00",},});}// 重置数据reset() {this.nodeCoordinateList = [];this.pointFeatureCollection = {type: "FeatureCollection",features: [],};this.lineFeatureCollection = {type: "FeatureCollection",features: [],};}// 开始绘制start() {this.reset();this.init();this.map.doubleClickZoom.disable(); // 禁止双击缩放this.map.getCanvas().style.cursor = "crosshair"; // 设置鼠标样式this.map.on("click", this.addPoint);this.map.on("dblclick", this.end);this.map.on("mousemove", this.dynamicAction);}// 结束绘制end(e) {this.addPoint(e);this.map.off("click", this.addPoint);this.map.off("dblclick", this.end);this.map.off("mousemove", this.dynamicAction);// this.map.once('click', (e) => { // ! 连续绘制// this.clear()// this.start()// })this.map.getCanvas().style.cursor = ""; // 恢复鼠标样式this.callback && this.callback(this.nodeCoordinateList);}// 清除绘制clear() {this.map.getCanvas().style.cursor = ""; // 恢复鼠标样式this.map.doubleClickZoom.enable(); // 恢复双击缩放this.map.off("click", this.addPoint);this.map.off("dblclick", this.end);this.map.off("mousemove", this.dynamicAction);if (this.map.getSource("_pointLayer")) {this.map.removeLayer("_pointLayer");this.map.removeSource("_pointLayer");}if (this.map.getSource("_lineLayer")) {this.map.removeLayer("_lineLayer");this.map.removeSource("_lineLayer");}if (this.map.getSource("_dynamicLineLayer")) {this.map.removeLayer("_dynamicLineLayer");this.map.removeSource("_dynamicLineLayer");}}// 添加点addPoint(e) {const lngLat = [e.lngLat.lng, e.lngLat.lat];const pointList = this.pointFeatureCollection;const lineList = this.lineFeatureCollection;if (pointList.features.length > 0) {let prev = pointList.features[pointList.features.length - 1];lineList.features.push({type: "Feature",geometry: {type: "LineString",coordinates: [prev.geometry.coordinates, lngLat],},});this.map.getSource("_lineLayer").setData(lineList);}pointList.features.push({type: "Feature",geometry: {type: "Point",coordinates: lngLat,},});this.map.getSource("_pointLayer").setData(pointList);this.nodeCoordinateList.push(lngLat);}// 鼠标移动事件dynamicAction(e) {const pointList = this.pointFeatureCollection;const lngLat = [e.lngLat.lng, e.lngLat.lat];if (pointList.features.length > 0) {const prev = pointList.features[pointList.features.length - 1];const json = {type: "Feature",geometry: {type: "LineString",coordinates: [prev.geometry.coordinates, lngLat],},};this.map.getSource("_dynamicLineLayer").setData(json);}}
}
使用方法
import { DrawLine } from "./map/index.js"; // 引入// 实例化
const drawVector = new DrawLine(map, (data) => {console.log('绘制完毕后的回调', data);
});// 开始绘制
drawVector.start();// 结束并清除绘制结果
drawVector.clear();
绘制多边形
/*** @description: 绘制多边形*/
export class DrawPolygon {map; // 地图对象nodeCoordinateList; // 节点坐标pointFeatureCollection; // 点GeoJSON集合lineFeatureCollection; // 线GeoJSON集合polygonFeatureCollection; // 面GeoJSON集合constructor(map, callback) {this.map = map;this.addPoint = this.addPoint.bind(this);this.end = this.end.bind(this);this.dynamicAction = this.dynamicAction.bind(this);this.callback = callback; // 回调函数}// 初始化图层init() {// 添加面图层this.map.addLayer({id: "_fillLayer",type: "fill",source: {type: "geojson",data: this.lineFeatureCollection,},paint: {"fill-color": "#ff0000","fill-opacity": 0.3,},});// 添加点图层this.map.addLayer({id: "_pointLayer2",type: "circle",source: {type: "geojson",data: this.pointFeatureCollection,},paint: {"circle-color": "#ffffff","circle-radius": 3,"circle-stroke-width": 2,"circle-stroke-color": "#ff0000",},});}// 重置数据reset() {this.nodeCoordinateList = [];this.pointFeatureCollection = {type: "FeatureCollection",features: [],};this.lineFeatureCollection = {type: "FeatureCollection",features: [],};this.polygonFeatureCollection = {type: "FeatureCollection",features: [],};}// 开始绘制start() {this.reset();this.init();this.map.doubleClickZoom.disable(); // 禁止双击缩放this.map.getCanvas().style.cursor = "crosshair"; // 设置鼠标样式this.map.on("click", this.addPoint);this.map.on("dblclick", this.end);this.map.on("mousemove", this.dynamicAction);}// 结束绘制end(e) {this.addPoint(e);this.map.off("click", this.addPoint);this.map.off("dblclick", this.end);this.map.off("mousemove", this.dynamicAction);this.map.getCanvas().style.cursor = ""; // 恢复鼠标样式this.callback && this.callback(this.polygonFeatureCollection);}// 清除绘制clear() {this.map.getCanvas().style.cursor = ""; // 恢复鼠标样式this.map.doubleClickZoom.enable(); // 恢复双击缩放this.map.off("click", this.addPoint);this.map.off("dblclick", this.end);this.map.off("mousemove", this.dynamicAction);if (this.map.getSource("_pointLayer2")) {this.map.removeLayer("_pointLayer2");this.map.removeSource("_pointLayer2");}if (this.map.getSource("_fillLayer")) {this.map.removeLayer("_fillLayer");this.map.removeSource("_fillLayer");}}// 隐藏绘制show(flag) {if (flag) {this.map.setLayoutProperty("_pointLayer2", "visibility", "visible");this.map.setLayoutProperty("_fillLayer", "visibility", "visible");} else {this.map.setLayoutProperty("_pointLayer2", "visibility", "none");this.map.setLayoutProperty("_fillLayer", "visibility", "none");}}// 添加点addPoint(e) {const lngLat = [e.lngLat.lng, e.lngLat.lat];const pointList = this.pointFeatureCollection;pointList.features.push({type: "Feature",geometry: {type: "Point",coordinates: lngLat,},});this.map.getSource("_pointLayer2").setData(pointList);this.nodeCoordinateList.push(lngLat);}// 鼠标移动事件dynamicAction(e) {const pointList = this.pointFeatureCollection;const lngLat = [e.lngLat.lng, e.lngLat.lat];const len = pointList.features.length;if (len > 1) {let pts = this.nodeCoordinateList.concat([lngLat]);pts = pts.concat([this.nodeCoordinateList[0]]);const json = {type: "Feature",geometry: {type: "Polygon",coordinates: [pts],},};this.map.getSource("_fillLayer").setData(json);this.polygonFeatureCollection = {type: "FeatureCollection",features: [json],};}}
}
使用方法
import { DrawPolygon } from "./map/index.js"; // 引入// 实例化
const drawVector = new DrawPolygon(map, (data) => {console.log('绘制完毕后的回调', data);
});// 开始绘制
drawVector.start();// 结束并清除绘制结果
drawVector.clear();
写在最后
当前版本的 Mapbox GL JS 的绘制功能真是不如 OpenLayers 的,OpenLayers 已经封装有一整套方法了
这边还有徒手画线的实现参考:【WebGIS实例】(19)MapboxGL 实现徒手画线(Freehand Drawing)