一、前言
工作上来了个需求,就是在地图上画一条端点有箭头的线(其实就是画带箭头轨迹线)。
因为第一次做,所以想了挺久,因为知识面不够,最后只能用最蠢的方法:硬画,步骤如下:① 画线段 ② 画起点的符号 ③画终点的符号
用Mapbox GL JS 来实现的话,就是绘制line图层、symbol图层就可以了,效果如下:
仅在线要素的两端绘制
在线段的每一个端点绘制
二、实现
@turf/turf 版本:6.5.0
mapbox-gl 版本: 2.10.0
vue 版本:2.6.14
首先是创建个地图,这部分不是本篇博客的重点,有需要可以参考这篇博客:
搭建基于Vue的Mapbox GL开发框架 | ╰ 羽翼 ╮的博客 (fengwc.cn)
turf.js的下载:npm install @turf/turf
1. 画线
数据格式:GeoJSON
const testJson =
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"coordinates": [
[
112.87972209292184,
22.784618902192463
],
[
113.68808368655016,
23.434484281968352
],
[
113.0473822753035,
23.511377287669646
]
],
"type": "LineString"
}
}
]
}
向地图中添加类型为geojson
的数据源,然后再添加类型为line
的图层。
map.addSource('trackSource', {
type: 'geojson',
data: testJson
})
map.addLayer({
id: 'trackLayer',
type: 'line',
source: 'trackSource',
paint: {
'line-color': '#FFFF00',
'line-width': 3
}
})
2. 获取端点坐标
这部分使用了turf.js的一些方法:
- 几何对象遍历:geomEach
- 线段遍历:segmentEach
- 由一个坐标生成一个点:point
// turf.geomEach可以遍历几何对象(一个完整的多段线),从而获得线串头和尾的坐标
const that = this
turf.geomEach(testJson, function (lineString) {
// 获取线头和线尾,并将其转化为GeoJSON格式(turf.point)
const lineStart = turf.point(lineString.coordinates[0])
const lineEnd = turf.point(lineString.coordinates[lineString.coordinates.length - 1])
// 将点存起来
that.lineStartList.push(lineStart)
that.lineEndList.push(lineEnd)
})
3. 绘制端点符号
上面的过程中,我们以及绘制好了线要素,也得到了端点的geojson并存放在了一个数组中(lineStartList
),现在就依据端点数组开始画符号。
这一步实际上就是为地图添加一个symbol
图层。
添加符号
// 加载图片
map.loadImage(require('@/assets/jugg.png'), function (_error, img) {
map.addImage('lineStartPoint', img)
})
添加数据源
map.addSource('lineStartPoint-source', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: this.lineStartList // 这个就是那一堆端点
}
})
添加图层
map.addLayer({
id: 'trackStart', // 图层ID
type: 'symbol', // 图层类型
source: 'lineStartPoint-source', // 数据源
layout: {
'icon-image': 'lineStartPoint', // 图片ID
'icon-size': 0.5, // 图片的大小
'icon-anchor': 'bottom' // 图片的位置
}
})
到这一步就完成了,然后需要注意的是,在想要删除这个端点带符号的线的时候,需要:
- 删除图片:
map.removeImage('lineStartPoint')
- 删除线图层:
map.removeLayer('trackLayer')
- 删除线数据源:
map.removeSource('trackSource')
- 删除符号图层:
map.removeLayer('trackStart')
- 删除符号数据源:
map.removeSource('lineStartPoint-source')
这样才能彻底将它从地图上删去。
2022年10月31日更新: 若图片和数据源不会要求切换的话,可以写在
constructor() {}
里,这样在首次创建类的时候加入图片,而不是每次触发方法时加入图片。
三、完整代码
/*
* @Date: 2022-10-26 16:38:03
* @LastEditTime: 2022-10-26 17:29:44
* @FilePath: \mapbox-vue\src\views\endpointDraw\drawTrackLine.js
* @Description:绘制端点带符号的线。
*/
import { map } from '@/utils/creatMapbox' // 地图主体
import * as turf from '@turf/turf' // 引入turf.js
import testJson from '@/assets/line.json' // 引入测试数据
export default class DrawTrackLine {
lineStartList = [] // 线头坐标列表
lineEndList = [] // 线尾坐标列表
constructor () {
console.log('早上好')
}
// 绘制轨迹线
drawTrackLine () {
map.addSource('trackSource', {
type: 'geojson',
data: testJson
})
map.addLayer({
id: 'trackLayer',
type: 'line',
source: 'trackSource',
paint: {
'line-color': '#FFFF00',
'line-width': 3
}
})
// 遍历轨迹线获取头尾坐标
const that = this
// turf.segmentEach可以遍历每一个线段
/* (本段注释代码中便是在线段每个端点绘制的代码)
turf.segmentEach(testJson, function (lineString) {
console.log(lineString)
// 获取线头和线尾,并将其转化为GeoJSON格式(turf.point)
const lineStart = turf.point(lineString.geometry.coordinates[0])
const lineEnd = turf.point(lineString.geometry.coordinates[lineString.geometry.coordinates.length - 1])
that.lineStartList.push(lineStart)
that.lineEndList.push(lineEnd)
})
*/
// turf.geomEach可以遍历几何对象(一个完整的多段线)
turf.geomEach(testJson, function (lineString) {
// 获取线头和线尾,并将其转化为GeoJSON格式(turf.point)
const lineStart = turf.point(lineString.coordinates[0])
const lineEnd = turf.point(lineString.coordinates[lineString.coordinates.length - 1])
that.lineStartList.push(lineStart)
that.lineEndList.push(lineEnd)
})
this.drawLineSymbol() // 绘制线头线尾的符号
}
// 绘制线头线尾符号
drawLineSymbol () {
// 加载图片
map.loadImage(require('@/assets/jugg.png'), function (_error, img) {
map.addImage('lineStartPoint', img)
})
map.loadImage(require('@/assets/ernaut.png'), function (_error, img) {
map.addImage('lineEndPoint', img)
})
// 添加数据源
map.addSource('lineStartPoint-source', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: this.lineStartList
}
})
map.addSource('lineEndPoint-source', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: this.lineEndList
}
})
// 添加图层
map.addLayer({
id: 'trackStart', // 图层ID
type: 'symbol', // 图层类型
source: 'lineStartPoint-source', // 数据源
layout: {
'icon-image': 'lineStartPoint', // 图片ID
'icon-size': 0.5, // 图片的大小
'icon-anchor': 'bottom' // 图片的位置
}
})
map.addLayer({
id: 'lineEnd', // 图层ID
type: 'symbol', // 图层类型
source: 'lineEndPoint-source', // 数据源
layout: {
'icon-image': 'lineEndPoint', // 图片ID
'icon-size': 0.5,
'icon-anchor': 'bottom'
}
})
}
// 清除该功能绘制的线段符号等等等等
clear () {
// 清空端点坐标列表
this.lineStartList = []
this.lineEndList = []
// 删除图层与数据源
if (map.hasImage('lineStartPoint')) map.removeImage('lineStartPoint')
if (map.hasImage('lineEndPoint')) map.removeImage('lineEndPoint')
try {
map.removeLayer('trackLayer')
map.removeLayer('trackStart')
map.removeLayer('lineEnd')
map.removeSource('trackSource')
map.removeSource('lineStartPoint-source')
map.removeSource('lineEndPoint-source')
} catch (error) {
console.log(error)
}
}
}
四、写在最后
我觉得这个方法很一般,如果您有更好的实现方案,请评论或私信提出。谢谢谢谢!!!