Newer
Older
DH_Apicture / public / static / libs / mapbox / extend / TrackLayer.js
@wudi wudi 29 days ago 12 KB 1
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ?
        module.exports = factory() :
        typeof define === 'function' && define.amd ? define(factory) :
            (global = typeof globalThis !== 'undefined' ? globalThis :
                global || self, global.mapboxgl1.TrackLayer = factory());
}(window, (function () {
    const icon = 'data:image/png;base64,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'
    const arrow = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAnElEQVQ4T63TsQ0CMQyF4f/NgMQQ0CBR0FIx190cFIiWhhFoKdgEiRUeSoF0gO8cjkub+Evs2OLPpc9422dgBRwlNZn/BtjeApdOUJsh0QuuwKYWiYAFcAKWHaSR1EbpfAHlkO0ICdMJgV+QXmAAmUu6v9IZA8wkPVKgtg7TF7H25t4UbN+A9ahGmqqVD8AO2GdzUF45+I3ZJJb9JxbwRhEhB66xAAAAAElFTkSuQmCC'

    function base64ToImg(base64) {
        return new Promise((resolve, reject) => {
            const img = new Image()
            img.src = base64
            img.onload = function () {
                resolve(img)
            }
            img.onerror = function (e) {
                reject(e)
            }
        })
    }

    return class {

        constructor(map, json, fields = { lng: 'l', lat: 'a' },callback) {
            this._data = json || [];
            this._map = map;
            this._callback = callback;
            this._features = this._data.map(i => turf.point([i.l,i.a].map(Number),i));
            this._json = turf.lineString(json.map(i => [i[fields.lng], i[fields.lat]].map(Number)));
            this._play = true;
            this.init();
        }

        init() {
            if (this._data.length <= 1) return;
            this.destory();
            const that = this
            that._index = 0
            that._count = 5500
            that._step = turf.length(that._json) / that._count
            that._flag = 0
            that._playId = 'play-' + Date.now()

            // 添加路径图层
            that._map.addSource(that._playId, {
                type: 'geojson',
                data: that._json
            })
            that._map.addLayer({
                id: that._playId,
                type: 'line',
                source: that._playId,
                'layout': {
                    'line-cap': 'round',
                    'line-join': 'round'
                },
                'paint': {
                    'line-color': '#aaaaaa',
                    'line-width': 5
                }
            })
            // 添加已播放路径
            that._map.addSource(that._playId + '-played', {
                type: 'geojson',
                data: that._json
            })
            that._map.addLayer({
                id: that._playId + '-played',
                type: 'line',
                source: that._playId + '-played',
                'layout': {
                    'line-cap': 'round',
                    'line-join': 'round'
                },
                'paint': {
                    'line-color': '#09801a',
                    'line-width': 5
                }
            })

            // 添加路径上的箭头
            base64ToImg(arrow).then(img => {
                that._map.addImage(that._playId + '-arrow', img)
                that._map.addLayer({
                    'id': that._playId + '-arrow',
                    'source': that._playId,
                    'type': 'symbol',
                    'layout': {
                        'symbol-placement': 'line',
                        'symbol-spacing': 50,
                        'icon-image': that._playId + '-arrow',
                        'icon-size': 0.6,
                        'icon-allow-overlap': true
                    }
                })
            })
            // 添加动态图标
            base64ToImg(icon).then(img => {
                that._map.addImage(that._playId + '-icon', img)
                that._map.addSource(that._playId + '-point', {
                    'type': 'geojson',
                    'data': that._getDataByCoords()
                })
                that._map.addLayer({
                    'id': that._playId + '-point',
                    'source': that._playId + '-point',
                    'type': 'symbol',
                    'layout': {
                        // 'icon-image': 'pz-point',
                        'icon-image': that._playId + '-icon',
                        'icon-size': 0.4,
                        'icon-allow-overlap': true,
                        'icon-rotation-alignment': 'map',
                        'icon-pitch-alignment': 'map',
                        'icon-rotate': 50
                    }
                })
                that._animatePath()
            })

        }

        _animatePath() {
            if (this._index > this._count) {
                window.cancelAnimationFrame(this._flag)
            } else {
                const coords = turf.along(this._json, this._step * this._index).geometry.coordinates
                // 已播放的线
                const start = turf.along(this._json, 0).geometry.coordinates
                this._map.getSource(this._playId + '-played').setData(turf.lineSlice(start, coords, this._json))

                // 车的图标位置
                this._map.getSource(this._playId + '-point').setData(this._getDataByCoords(coords))
                // 计算旋转角度
                const nextIndex = this._index === this._count ? this._count - 1 : this._index + 1
                const coordsNext = turf.along(this._json, this._step * nextIndex).geometry.coordinates
                let angle = turf.bearing(turf.point(coords), turf.point(coordsNext)) - 90
                if (this._index === this._count) angle += 180
                this._map.setLayoutProperty(this._playId + '-point', 'icon-rotate', angle)
                this._index++

                const feature = turf.nearestPoint(this._getDataByCoords(coords),turf.featureCollection(this._features));
                this._callback && this._callback(feature.properties,this._index);
                if (this._play) this._flag = requestAnimationFrame(() => {
                    this._animatePath()
                })
            }
        }

        _getDataByCoords(coords) {
            if (!coords || coords.length !== 2) return null
            return turf.point(coords, {
                'label': this._formatDistance(this._step * this._index)
            })
        }

        _formatDistance(dis) {
            if (dis < 1) {
                dis = dis * 1000
                return dis.toFixed(0) + '米'
            } else {
                return dis.toFixed(2) + '千米'
            }
        }

        status(flag = true) {
            this._play = flag;
            this._animatePath()
        }

        restart() {
            this.init();
        }

        show() {
            this.setLayerParams(this._playId + '-point', { visibility: 'visible' });
            this.setLayerParams(this._playId + '-played', { visibility: 'visible' });
            this.setLayerParams(this._playId, { visibility: 'visible' });
            this.status();
        }

        hide() {
            this.setLayerParams(this._playId + '-point', { visibility: 'none' });
            this.setLayerParams(this._playId + '-played', { visibility: 'none' });
            this.setLayerParams(this._playId, { visibility: 'none' });
            this.status(false);
        }

        setOpacity(opacity) {
            this.setLayerParams(this._playId + '-point', {}, { 'raster-opacity': opacity });
            this.setLayerParams(this._playId + '-played', {}, { 'raster-opacity': opacity });
            this.setLayerParams(this._playId, {}, { 'raster-opacity': opacity });
        }

        destory() {
            window.cancelAnimationFrame(this._flag);
            if (!(this._playId)) return;
            if (this._map.getSource(this._playId + '-point')) {
                this._map.removeLayer(this._playId + '-point')
                // this._map.removeLayer(this._playId + '-label')
                this._map.removeSource(this._playId + '-point')
            }
            if (this._map.getSource(this._playId)) {
                this._map.removeLayer(this._playId);
                this._map.removeLayer(this._playId + '-arrow');
                this._map.removeSource(this._playId)
            }

            if (this._map.getSource(this._playId + '-played')) {
                this._map.removeLayer(this._playId + '-played')
                this._map.removeSource(this._playId + '-played')
            }
            this._popup && this._popup.remove();
        }

        setLayerParams(id, layout = {}, paint = {}) {
            let layoutKeys = Object.keys(layout);
            let paintKeys = Object.keys(paint);
            layoutKeys.forEach(key => this._map.setLayoutProperty(id, key, layout[key]));
            paintKeys.forEach(key => this._map.setPaintProperty(id, key, paint[key]));
        }
    }
})));