mapbox动态线条

1. 效果

动态线路

2. RouteReplay

import * as turf from '@turf/turf'

export default class RouteReplay {
    /**
     *
     * @param {*} map mapbox实例对象
     * @param {*} routejson 路径geojson type = lineString
     * @param {*} nsteps nsteps type = number
     * @param {*} realRouteLayerId 线条realRouteLayerId type = string
     * @param {*} animatePointLayerId animatePointLayerId type = string
     * @param {*} width 线条width type = number
     * @param {*} opacity 线条opacity type = number
     * @param {*} color 线条color type = string
     */

    map: any

    json: any

    realRouteId: string

    animatePointId: string

    animated: boolean

    counter: number

    steps: number

    nsteps: number

    newRouteGeoJson: any

    timer: any

    layerList: Array

    width: number

    opacity: number

    color: string

    constructor(map: any,
                routejson: {
                   type: string;
                   features: {
                       type: string;
                       geometry: {
                           type: string;
                           coordinates: number[][]
                       }
                   }[] | {
                       type: string;
                       geometry: {
                           type: string;
                           coordinates: number[][]
                       }
                   }[] },
                nsteps: number,
                realRouteLayerId: string,
                animatePointLayerId: string,
                width: {
                    type: number;
                    default: 5
                },
                opacity: {
                    type: number,
                    default: 1
                },
                color: {
                    type: string,
                    default: 'rgba(0,255,255,1)'
                }) {
        this.map = map
        this.json = routejson
        this.realRouteId = realRouteLayerId
        this.animatePointId = animatePointLayerId
        this.animated = false
        this.counter = 0
        this.steps = 0
        this.nsteps = nsteps
        this.newRouteGeoJson = null
        this.timer = null
        this.layerList = []
        this.width = width
        this.opacity = opacity
        this.color = color

        // 进中的路线
        this.realRouteGeoJson = {
            'type': 'FeatureCollection',
            'features': [{
                'type': 'Feature',
                'geometry': {
                    'type': 'LineString',
                    'coordinates': []
                }
            }]
        }
        // 实时点
        this.animatePointGeoJson = {
            'type': 'FeatureCollection',
            'features': [{
                'type': 'Feature',
                'properties': {},
                'geometry': {
                    'type': 'Point',
                    'coordinates': []
                }
            }]
        }

        this.init()
    }

    init() {

        // eslint-disable-next-line prefer-destructuring
        this.animatePointGeoJson.features[0].geometry.coordinates = this.json.features[0].geometry.coordinates[0]

        // 轨迹点json
        this.newRouteGeoJson = this.resetRoute(this.json.features[0], this.nsteps, 'kilometers')

        // 轨迹点json的点数量
        this.steps = this.newRouteGeoJson.geometry.coordinates.length

        this.addRealRouteSource(this.realRouteId) // 添加实时轨迹线图层

        this.addAnimatePointSource(this.animatePointId) // 添加动态点图层

        this.layerList.push(`realRouteLayer${this.realRouteId}`, `animatePointLayer${this.animatePointId}`,)
    }

    animate() {
        if (this.counter >= this.steps) {
            return
        }
        let startPnt
        let endPnt
        if (this.counter === 0) { // 开始
            this.realRouteGeoJson.features[0].geometry.coordinates = []
            startPnt = this.newRouteGeoJson.geometry.coordinates[this.counter]
            endPnt = this.newRouteGeoJson.geometry.coordinates[this.counter]
        } else if (this.counter !== 0) {
            startPnt = this.newRouteGeoJson.geometry.coordinates[this.counter - 1]
            endPnt = this.newRouteGeoJson.geometry.coordinates[this.counter]
        }

        this.animatePointGeoJson.features[0].geometry.coordinates = this.newRouteGeoJson.geometry.coordinates[this.counter]
        this.realRouteGeoJson.features[0].geometry.coordinates.push(this.animatePointGeoJson.features[0].geometry.coordinates)

        // 已经走过的轨迹更新
        this.map.getSource(`realRouteLayer${this.realRouteId}`).setData(this.realRouteGeoJson)
        if (this.animated) {
            this.timer = requestAnimationFrame(() => { this.animate() })
        }
        // eslint-disable-next-line no-plusplus
        this.counter++
    }

    addRealRouteSource(layerId) {
        this.map.addLayer({
            'id': `realRouteLayer${layerId}`,
            'type': 'line',
            'source': {
                'type': 'geojson',
                'lineMetrics': true,
                'data': this.realRouteGeoJson
            },
            'paint': {
                'line-width': this.width,
                'line-opacity': this.opacity,
                'line-color': this.color,
            }
        })
    }

    // 添加动态点图层
    addAnimatePointSource(layerId) {
        this.map.addLayer({
            'id': `animatePointLayer${layerId}`,
            'type': 'symbol',
            'source': {
                'type': 'geojson',
                'data': this.animatePointGeoJson
            },
            // 'layout': {
            //     'icon-image': '',
            //     'icon-size': 0,
            //     'icon-rotate': ['get', 'bearing'],
            //     'icon-rotation-alignment': 'map',
            //     'icon-allow-overlap': true,
            //     'icon-ignore-placement': true
            // }
        })
    }

    resetRoute(route, nstep, units) {
        const newroute = {
            'type': 'Feature',
            'geometry': {
                'type': 'LineString',
                'coordinates': []
            }
        }
        // 指定点集合的总路长
        const lineDistance = turf.lineDistance(route)

        // 每一段的平均距离
        const nDistance = lineDistance / nstep
        const {length} = this.json.features[0].geometry.coordinates
        // eslint-disable-next-line no-plusplus
        for (let i = 0; i < length - 1; i++) {
            let from = turf.point(route.geometry.coordinates[i]) // type 为 point的feature
            let to = turf.point(route.geometry.coordinates[i + 1])
            let lDistance = turf.distance(from, to, { // 两个点之间的距离
                units: units
            })
            if (i === 0) { // 起始点直接推入
                newroute.geometry.coordinates.push(route.geometry.coordinates[0])
            }
            if (lDistance > nDistance) { // 两点距离大于每段值,将这条线继续分隔
                let rings = this.splitLine(from, to, lDistance, nDistance, units)
                newroute.geometry.coordinates = newroute.geometry.coordinates.concat(rings)
            } else { // 两点距离小于每次移动的距离,直接推入
                newroute.geometry.coordinates.push(route.geometry.coordinates[i + 1])
            }
        }
        return newroute
    }

    // 过长的两点轨迹点分段
    // eslint-disable-next-line class-methods-use-this
    splitLine(from, to, distance, splitLength, units) {
        var step = parseInt(distance / splitLength, 10)

        const leftLength = distance - step * splitLength
        const rings = []
        const route = turf.lineString([from.geometry.coordinates, to.geometry.coordinates])
        // eslint-disable-next-line no-plusplus
        for (let i = 1; i <= step; i++) {
            let nlength = i * splitLength
            // turf.alone返回沿着route<LineString>距离为nlength<number>的点
            let pnt = turf.along(route, nlength, {
                units: units
            });
            rings.push(pnt.geometry.coordinates)
        }
        if (leftLength > 0) {
            rings.push(to.geometry.coordinates)
        }
        return rings
    }

    start() {
        if (!this.animated) {
            this.animated = true
            this.animate()
        }
    }

    pause() {
        this.animated = false
        this.animate()
    }

    end() {
        window.cancelAnimationFrame(this.timer)
        this.animated = false
        this.counter = 0
        this.animate()
    }

    remove() {
        window.cancelAnimationFrame(this.timer)
        // eslint-disable-next-line array-callback-return
        this.layerList.map(layer => {
            // console.log(layer)
            if (this.map.getSource(layer)) {
                this.map.removeLayer(layer)
                this.map.removeSource(layer)
            }
        })
    }
}

3. 使用

new RouteReplay(map, routeGeoJson, 1000, 'Layerid1', 'Layerid2', 5,1,'rgba(0,255,255,1)')
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在mapbox中实现动态线条方向,可以利用mapbox-gl图层Layer中的icon-rotate属性来旋转箭头图标的角度。首先需要注意的是,默认情况下,目标与正北方向之间有90°的夹角,所以需要将目标的方向角度(bearing)减去90°。具体的代码如下: let iconRotate = Number(bearing - 90); 然后,在配置layout中添加symbol-placement和symbol-spacing属性,并设置symbol-placement为'line',symbol-spacing为图标之间的间隔(默认为250)。同时,还需要设置icon-image为箭头图标的名称,icon-size为图标的大小。以下是添加箭头图层的核心代码: function addArrowlayer() { map.addLayer({ 'id': 'arrowLayer', 'type': 'symbol', 'source': { 'type': 'geojson', 'data': routeGeoJson //轨迹geojson格式数据 }, 'layout': { 'symbol-placement': 'line', 'symbol-spacing': 50, // 图标间隔,默认为250 'icon-image': 'arrowIcon', //箭头图标 'icon-size': 0.5 } }); } 通过以上代码,可以在mapbox中实现动态线条方向,箭头图标会根据目标的方向角度进行旋转。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [mapbox-gl 实现末尾带箭头的线](https://blog.csdn.net/qq_24343389/article/details/130868812)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [mapboxgl实现带箭头轨迹线](https://blog.csdn.net/gisarmory/article/details/112034622)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值