You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

沿道路曲线旋转位置标记并存储旋转角度到Firebase Realtime DB的实现咨询

实现方案

核心逻辑

  • 优先调用设备原生传感器返回的heading值获取前进方向角度,无传感器支持时通过前后两次定位坐标计算方位角作为降级方案
  • 基于Leaflet原生DivIcon实现图标旋转,无需引入额外第三方依赖
  • 旋转角度同步写入Firebase Realtime Database持久化
  • 增加低速/静止过滤逻辑,避免角度跳变

完整修改代码

<html>
<head>
<title>Rider Module</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no"/>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>
<script src='https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js'></script>
<script src="https://www.gstatic.com/firebasejs/8.9.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.9.1/firebase-database.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.9.1/firebase-analytics.js"></script>
<style>
    html, body {
    height: 100%;
    margin: 0;
    padding: 0;
    }
</style>
</head>
<body>
    <div id="map" style="height:100%; width:100%; background-color:#eeeeee;"></div>
    <script>
    mbUrl1 = 'https://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}'; 
    mbUrl2 = 'https://{s}.google.com/vt/lyrs=s,h,p&x={x}&y={y}&z={z}';
    mbUrl3 = 'https://{s}.google.com/vt/lyrs=m@221097413,traffic&x={x}&y={y}&z={z}';
    const streets = L.tileLayer(mbUrl1, {maxZoom: 18, subdomains:['mt0','mt1','mt2','mt3']}),
    hybrid = L.tileLayer(mbUrl2, {maxZoom: 18, subdomains:['mt0','mt1','mt2','mt3']}),
    traffic = L.tileLayer(mbUrl3, {maxZoom: 18, subdomains:['mt0','mt1','mt2','mt3']});
    const map = L.map("map", {
        center: [8.4819, 77.0361],
        zoom: 18,
        maxZoom: 18,
        minZoom: 18,
        layers: [streets]
    });
    var baseLayers = {
        "Streets": streets,
        "Hybrid": hybrid,
        "Traffic": traffic
    };
    L.control.layers(baseLayers).addTo(map);
    var firebaseConfig = {
        apiKey: "XXXXXXXXXXXXXXXX",
        databaseURL: "XXXXXXXXXXXXXXXX",
        projectId: "XXXXXXXXXXXXXXXX",
        appId: "XXXXXXXXXXXXXXXX"
    };
    firebase.initializeApp(firebaseConfig);
    firebase.analytics();
    this.database = firebase.database();

    var marker;
    var ridername="Rider 1";    
    var username="rider1@abcde.com";
    var i=0;
    var pointA=[];
    var lastRotation = 0; // 存储上次生效的旋转角度

    // 两点经纬度计算方位角函数(正北为0,顺时针0-360度)
    function calculateBearing(lat1, lng1, lat2, lng2) {
        const radLat1 = lat1 * Math.PI / 180;
        const radLat2 = lat2 * Math.PI / 180;
        const diffLng = (lng2 - lng1) * Math.PI / 180;
        const y = Math.sin(diffLng) * Math.cos(radLat2);
        const x = Math.cos(radLat1) * Math.sin(radLat2) - Math.sin(radLat1) * Math.cos(radLat2) * Math.cos(diffLng);
        let bearing = Math.atan2(y, x) * 180 / Math.PI;
        return (bearing + 360) % 360;
    }

    function onLocationFound(e) 
        {
            var lat= e.latlng.lat;
            var lng= e.latlng.lng;
            var accuracy= e.accuracy/2;  
            var rotation = lastRotation;

            // 计算旋转角度逻辑
            if (e.heading !== null && e.speed > 1) {
                // 优先用设备返回的heading,速度大于1m/s时生效
                rotation = e.heading;
            } else if (i > 0) {
                // 无heading时用前后两点计算,移动距离大于5米时更新角度
                const prevLat = pointA[i-1].lat;
                const prevLng = pointA[i-1].lng;
                const distance = e.latlng.distanceTo(pointA[i-1]);
                if (distance > 5) {
                    rotation = calculateBearing(prevLat, prevLng, lat, lng);
                }
            }
            lastRotation = rotation;

            if (marker) { map.removeLayer(marker); }    
            // 用DivIcon实现旋转图标
            var markerIcon = new L.DivIcon({
                html: `<img src="markericon.png" style="transform: rotate(${rotation}deg); transform-origin: center bottom;" width="30" height="45">`,
                iconSize: [30, 45],
                iconAnchor: [15, 47],
                popupAnchor: [0, -50],
                className: '' // 清空默认类避免额外样式干扰
            });
            marker= L.marker(e.latlng,{icon: markerIcon}).addTo(map);
            marker.bindPopup("<b>Rider Name:</b> " + ridername + "<br><b>Latitude:</b> " + lat + "<br><b>Longitude:</b> " + lng + "<br><b>Accuracy:</b> " + accuracy.toFixed(2) + "M Radius<br><b>Rotation:</b> " + rotation.toFixed(2) + "°").addTo(map).openPopup();   
            firebase.database().ref('locations/users/'+username).set(
                {
                    ridername: ridername,
                    lat: lat,
                    lng : lng,
                    accuracy : accuracy,
                    rotation: rotation // 存储旋转角度到Firebase
                });
            if (i==0)
                {   
                    pointA[i] = new L.LatLng(lat, lng);
                }
            ++i;
            pointA[i]= new L.LatLng(lat, lng);
            var pointList = [pointA[i-1], pointA[i]];
            new L.Polyline(pointList, {color: 'red'}).addTo(map);           
        }
    function onLocationError(e) {
            alert(e.message);
            var user = firebase.database().ref('locations/users/');
            user.child(username).remove();      
        }
    map.on('locationfound', onLocationFound);
    map.on('locationerror', onLocationError);
    map.locate({setView: true, watch: true, enableHighAccuracy: true, maxZoom: 18});
    </script>
</body>
</html>

注意事项

  • 首次定位时无前序坐标,会使用默认0度朝向,移动后自动更新为正确角度
  • 设备需授予位置权限和传感器权限才能获取heading值,部分无陀螺仪/指南针的设备会自动降级为坐标计算模式
  • 可根据实际需求调整速度阈值和距离阈值,适配不同精度的定位场景

内容的提问来源于stack exchange,提问作者Mahesh A R

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 05:42:01