Mapbox gl HTML经纬度点渲染,动态轨迹播放,自定义图形以及轨迹上显示箭头方向

Mapbox gl HTML经纬度点渲染,动态轨迹播放,自定义图形以及轨迹上显示箭头方向

    • [1. 效果图](#1. 效果图)
    • [2. 源码](#2. 源码)
      • [2.1 line.html](#2.1 line.html)
      • [2.2 line_arrow.html](#2.2 line_arrow.html)
    • 参考

今天要排查个问题,需要显示多个经纬度点连接成线段的方向,于是尝试下展示。

1. mapbox渲染经纬度点,线,多线,面

  1. 运动轨迹增加箭头方向

  2. 增加一个图像(如小车等)本文用的tomcat小猫图标,实行运动轨迹可控制,开始,播放,停止

1. 效果图

线渲染:

点、线渲染:

增加箭头显示方向,以及tomcat小猫播放:


轨迹播放中截图:

增加轨迹方向箭头及小猫播放中截图如下:

修改地图背景色,实时轨迹线颜色,播放中截图如下:

2. 源码

2.1 line.html

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Add a GeoJSON line</title>
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
    <link href="https://api.mapbox.com/mapbox-gl-js/v2.1.1/mapbox-gl.css" rel="stylesheet">
    <script src="https://api.mapbox.com/mapbox-gl-js/v2.1.1/mapbox-gl.js"></script>
    <style>
        body {
            margin: 0;
            padding: 0;
        }

        #map {
            position: absolute;
            top: 0;
            bottom: 0;
            width: 100%;
        }
    </style>
</head>
<body>
<div id="map"></div>
<script>
    // TO MAKE THE MAP APPEAR YOU MUST
    // ADD YOUR ACCESS TOKEN FROM
    // https://account.mapbox.com
    mapboxgl.accessToken = 'pk.eyJ1******************Lc99g';
    var map = new mapboxgl.Map({
        container: 'map',
        style: 'mapbox://styles/mapbox/streets-v11',
// center: [-122.486052, 37.830348],
// center: [116.239749,40.0717456],
// center: [117.85448020099696,35.96263648233899],
        center: [116.25456103528076, 40.07649758667226],
        zoom: 14
    });

    map.on('load', function () {
        map.addSource('route', {
            'type': 'geojson',
            'data': {
                'type': 'FeatureCollection',
                'features': [
                    {
                        'type': 'Feature',
                        'properties': {},
                        'geometry': {
                            'type': 'LineString',
                            'coordinates': [[116.25456103528076, 40.07649758667226, 32.60466429684311],
                                [116.25459033603693, 40.076511383622965, 32.46827581245452],
                                [116.25455860845449, 40.07650334314173, 32.52390295546502],
                                [116.25460283054188, 40.07651455000795, 32.44636987615377]]
                        }
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [116.25456103528076, 40.07649758667226, 32.60466429684311]
                        }
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [116.25459033603693, 40.076511383622965, 32.46827581245452]
                        }
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [116.25455860845449, 40.07650334314173, 32.52390295546502]
                        }
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [116.25460283054188, 40.07651455000795, 32.44636987615377]
                        }
                    }
                ]
            }
        });
        map.addLayer({
            'id': 'route',
            'type': 'line',
            'source': 'route',
            'layout': {
                'line-join': 'round',
                'line-cap': 'round'
            },
            'paint': {
                'line-color': '#DC143C',
                'line-width': 5
            }
        });

        map.addLayer({
            'id': 'routePoint',
            'type': 'circle',
            'source': 'route',
            'paint': {
                'circle-radius': 6,
                'circle-color': '#0000FF'
            },
            'filter': ['==', '$type', 'Point']
        });
    });
</script>

</body>
</html>

2.2 line_arrow.html

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Add a GeoJSON line,with Arrow</title>
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
    <link href="https://api.mapbox.com/mapbox-gl-js/v2.1.1/mapbox-gl.css" rel="stylesheet">
    <script src="https://api.mapbox.com/mapbox-gl-js/v2.1.1/mapbox-gl.js"></script>
    <script src="https://unpkg.com/@turf/turf@6.3.0/turf.min.js"></script>
    <style>
        body {
            margin: 0;
            padding: 0;
        }

        .menuBar {
            position: relative;
            top: 10px;
            margin: 0 50px;
            padding: 5px;
            border-radius: 3px;
            z-index: 999;
            background-color: rgba(0, 168, 0, 0.7);
        }

        input[type=button] {
            font-size: 16px;
        }

        #map {
            position: absolute;
            top: 0;
            bottom: 0;
            width: 100%;
        }

        /* 删除mapbox logo */
        .mapboxgl-ctrl {
            display: none !important;
        }
    </style>
</head>

<body>
<div id="map"></div>
<div class="menuBar">
    <input type="button" value="开始" onclick="startClick()"/>
    <input type="button" value="暂停" onclick="pauseClick()"/>
    <input type="button" value="停止" onclick="stopClick()"/>
    <div id="canvas"></div>
</div>
<script>
    // TO MAKE THE MAP APPEAR YOU MUST ADD YOUR ACCESS TOKEN FROM
    // https://account.mapbox.com
    mapboxgl.accessToken = 'pk.eyJ********';
    var map = new mapboxgl.Map({
        container: 'map',
        style: 'mapbox://styles/mapbox/streets-v11',
        // center: [116.25456103528076, 40.07649758667226],
        // zoom: 24
        center: [116.390619, 39.924317], // starting position [lng, lat]
        zoom: 13 // starting zoom
    });
    // 背景色
    // map.setStyle('mapbox://styles/mapbox/dark-v9');

    // 箭头-右
    var svgXML =
        `<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
                <path d="M529.6128 512L239.9232 222.4128 384.7168 77.5168 819.2 512 384.7168 946.4832 239.9232 801.5872z" p-id="9085" fill="#ff00ff"></path>
            </svg>
            `
    //给图片对象写入base64编码的svg流
    var svgBase64 = 'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(svgXML)));

    map.on('load', function () {
        let arrowIcon = new Image(20, 20)
        arrowIcon.src = svgBase64
        arrowIcon.onload = function () {
            map.addImage('arrowIcon', arrowIcon)
            console.log("----------1 " + arrowIcon)
            map.loadImage('img/arrowR.png', function (error, arrowIcon2) {
                if (arrowIcon2) {
                    map.addImage('arrowIcon2', arrowIcon2);
                }
            })
            map.loadImage('img/car.png', function (error, carIcon) {
                if (carIcon) {
                    console.log("----------2 " + arrowIcon)
                    map.addImage('carIcon', carIcon);
                    setRouteData()
                }
                console.log("----------3 " + arrowIcon)
            });
        }
    })

    var isPlay = false
    var counter = 0
    var steps = 0
    let aLength = 0;
    var routeGeoJson = {
        'type': 'FeatureCollection',
        'features': [{
            'type': 'Feature',
            'geometry': {
                'type': 'LineString',
                'coordinates': [
                    [116.391844, 39.898457],
                    [116.377947, 39.898595],
                    [116.368001, 39.898341],
                    [116.357144, 39.898063],
                    [116.351934, 39.899095],
                    [116.35067, 39.905871],
                    [116.3498, 39.922329],
                    [116.349671, 39.931017],
                    [116.349225, 39.939104],
                    [116.34991, 39.942233],
                    [116.366892, 39.947263],
                    [116.387537, 39.947568],
                    [116.401988, 39.947764],
                    [116.410824, 39.947929],
                    [116.42674, 39.947558],
                    [116.427338, 39.9397],
                    [116.427919, 39.932404],
                    [116.428377, 39.923109],
                    [116.429583, 39.907094],
                    [116.41404, 39.906858],
                    [116.405321, 39.906622],
                    [116.394954, 39.906324],
                    [116.391264, 39.906308],
                    [116.390748, 39.916611]
                ]
                /*[
                    [116.25456103528076, 40.07649758667226, 32.60466429684311],
                    [116.25459033603693, 40.076511383622965, 32.46827581245452],
                    [116.25455860845449, 40.07650334314173, 32.52390295546502],
                    [116.25460283054188, 40.07651455000795, 32.44636987615377]
                ]*/
            }
        }]
    }

    var realRouteGeoJson = {
        'type': 'FeatureCollection',
        'features': [{
            'type': 'Feature',
            'geometry': {
                'type': 'LineString',
                'coordinates': []
            }
        }]
    }

    var animatePointGeoJson = {
        'type': 'FeatureCollection',
        'features': [{
            'type': 'Feature',
            'properties': {},
            'geometry': {
                'type': 'Point',
                'coordinates': []
            }
        }]
    }

    // 获取轨迹数据
    function setRouteData() {
        animatePointGeoJson.features[0].geometry.coordinates = routeGeoJson.features[0].geometry.coordinates[0]
        aLength = routeGeoJson.features[0].geometry.coordinates.length;
        newRouteGeoJson = resetRoute(routeGeoJson.features[0], 1000, 'kilometers')
        steps = newRouteGeoJson.geometry.coordinates.length

        addRoutelayer() // 添加轨迹线图层
        addRealRouteSource() // 添加实时轨迹线图层
        addArrowlayer() // 添加箭头图层
        addAnimatePointSource() // 添加动态点图层
    }

    // 添加轨迹线图层
    function addRoutelayer() {
        map.addLayer({
            'id': 'routeLayer',
            'type': 'line',
            'source': {
                'type': 'geojson',
                'lineMetrics': true,
                'data': routeGeoJson
            },
            'paint': {
                'line-width': 10,
                'line-opacity': 1,
                'line-color': '#009EFF',
            }
        });
    }

    // 添加实时轨迹线
    function addRealRouteSource() {
        map.addLayer({
            'id': 'realRouteLayer',
            'type': 'line',
            'source': {
                'type': 'geojson',
                'lineMetrics': true,
                'data': realRouteGeoJson
            },
            'paint': {
                'line-width': 3,
                'line-opacity': 1,
                'line-color': '#FF0000',
            }
        });
    }

    // 添加箭头图层
    function addArrowlayer() {
        console.log("-------addArrowlayer")
        map.addLayer({
            'id': 'arrowLayer',
            'type': 'symbol',
            'source': {
                'type': 'geojson',
                'data': routeGeoJson //轨迹geojson格式数据
            },
            'layout': {
                'symbol-placement': 'line',
                'symbol-spacing': 50, // 图标间隔,默认为250
                'icon-image': 'arrowIcon2', //箭头图标
                'icon-size': 0.5,
                'icon-rotate': ['get', 'bearing'],
                'icon-rotation-alignment': 'map',
                'icon-allow-overlap': true,
                'icon-ignore-placement': true
            }
        });
        console.log("-------addArrowlayer end...")
    }

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

        animate()
    }

    function animate() {
        if (counter >= steps) {
            return
        }
        var startPnt, endPnt
        if (counter == 0) {
            realRouteGeoJson.features[0].geometry.coordinates = []
            startPnt = newRouteGeoJson.geometry.coordinates[counter]
            endPnt = newRouteGeoJson.geometry.coordinates[counter + 1]
        } else if (counter !== 0) {
            startPnt = newRouteGeoJson.geometry.coordinates[counter - 1]
            endPnt = newRouteGeoJson.geometry.coordinates[counter]
        }

        animatePointGeoJson.features[0].properties.bearing = turf.bearing(
            turf.point(startPnt),
            turf.point(endPnt)
        ) - 90;
        animatePointGeoJson.features[0].geometry.coordinates = newRouteGeoJson.geometry.coordinates[counter];
        realRouteGeoJson.features[0].geometry.coordinates.push(animatePointGeoJson.features[0].geometry.coordinates)

        map.getSource('animatePointLayer').setData(animatePointGeoJson);
        map.getSource('realRouteLayer').setData(realRouteGeoJson);
        if (isPlay) {
            requestAnimationFrame(animate);
        }
        counter = counter + 1;
    }

    function resetRoute(route, nstep, units) {
        var newroute = {
            'type': 'Feature',
            'geometry': {
                'type': 'LineString',
                'coordinates': []
            }
        }
        var lineDistance = turf.lineDistance(route);
        var nDistance = lineDistance / nstep;
        for (let i = 0; i < aLength - 1; i++) {
            var from = turf.point(route.geometry.coordinates[i]);
            var 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 = lineMore(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
    }

    function lineMore(from, to, distance, splitLength, units) {
        var step = parseInt(distance / splitLength)
        var leftLength = distance - step * splitLength
        var rings = []
        var route = turf.lineString([from.geometry.coordinates, to.geometry.coordinates])
        for (let i = 1; i <= step; i++) {
            let nlength = i * splitLength
            let pnt = turf.along(route, nlength, {
                units: units
            });
            rings.push(pnt.geometry.coordinates)
        }
        if (leftLength > 0) {
            rings.push(to.geometry.coordinates)
        }
        return rings
    }

    function startClick() {
        if (!isPlay) {
            isPlay = true
            animate()
        }
    }

    function pauseClick() {
        isPlay = false
        animate()
    }

    function stopClick() {
        isPlay = false
        counter = 0
        animate()
    }
</script>

</body>

</html>

参考

相关推荐
沉默璇年1 小时前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder1 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
2401_882727572 小时前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架
SoaringHeart2 小时前
Flutter进阶:基于 MLKit 的 OCR 文字识别
前端·flutter
会发光的猪。2 小时前
css使用弹性盒,让每个子元素平均等分父元素的4/1大小
前端·javascript·vue.js
天下代码客2 小时前
【vue】vue中.sync修饰符如何使用--详细代码对比
前端·javascript·vue.js
猫爪笔记2 小时前
前端:HTML (学习笔记)【1】
前端·笔记·学习·html
前端李易安3 小时前
Webpack 热更新(HMR)详解:原理与实现
前端·webpack·node.js
红绿鲤鱼3 小时前
React-自定义Hook与逻辑共享
前端·react.js·前端框架
Domain-zhuo3 小时前
什么是JavaScript原型链?
开发语言·前端·javascript·jvm·ecmascript·原型模式