【WebGIS实例】(1)MapboxGL绘制端点有符号的线

一、前言

工作上来了个需求,就是在地图上画一条端点有箭头的线(其实就是画带箭头轨迹线)。
因为第一次做,所以想了挺久,因为知识面不够,最后只能用最蠢的方法:硬画,步骤如下:① 画线段 ② 画起点的符号 ③画终点的符号

用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的一些方法:

// 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' // 图片的位置
  }
})

到这一步就完成了,然后需要注意的是,在想要删除这个端点带符号的线的时候,需要:

  1. 删除图片:map.removeImage('lineStartPoint')
  2. 删除线图层:map.removeLayer('trackLayer')
  3. 删除线数据源:map.removeSource('trackSource')
  4. 删除符号图层:map.removeLayer('trackStart')
  5. 删除符号数据源: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)
    }
  }
}


四、写在最后

我觉得这个方法很一般,如果您有更好的实现方案,请评论或私信提出。谢谢谢谢!!!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值