前言
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)