如何使用 Haversine 公式计算行驶距离(不是位移)?

Posted

技术标签:

【中文标题】如何使用 Haversine 公式计算行驶距离(不是位移)?【英文标题】:How to calculate distance (NOT DISPLACEMENT) travelled using Haversine Formula? 【发布时间】:2015-07-01 09:52:50 【问题描述】:

我正在尝试编写一些 JS 代码,使用 Haversine 公式计算用户行进的距离。

现在,Haversine 公式接受起始坐标和当前坐标,并给出这两个坐标之间的分隔。然而,这只是 DISPLACEMENT,而不是用户可能已经到达终点的距离。

我需要有人帮助我编写下面的代码(给出位移)并建议我应该做更多的事情以便计算距离。

//Geolocation
var startPos;
var watchId;
//Start Tracking Position
function startTracking() 
    if(navigator.geolocation) 
        if (document.getElementById('startBtn').innerhtml === " Stop Tracking?")
        
            stopTracking();
            stopClock();
            return;
        
        document.getElementById('startBtn').innerHTML = " Stop Tracking?";
        //Get Position
        navigator.geolocation.getCurrentPosition(showPosition, showError);
        //Watch Position
        watchId = navigator.geolocation.watchPosition(showPositionUpdate, showError);
    
    else 
        alert('Geolocation is not supported by your browser');
    

//Show Start Position
function showPosition(position) 
    startPos = position;
    document.getElementById('startLat').innerHTML = startPos.coords.latitude;
    document.getElementById('startLon').innerHTML = startPos.coords.longitude;

//Updated Position
function showPositionUpdate(position) 
    document.getElementById('currentLat').innerHTML = position.coords.latitude;
    document.getElementById('currentLon').innerHTML = position.coords.longitude;
    var distance = calculateDistance(startPos.coords.latitude, startPos.coords.longitude, position.coords.latitude, position.coords.longitude);
    if (distance < 1) 
        document.getElementById('distance').innerHTML = (Math.round(distance * 1000)) + "m";
     else 
        document.getElementById('distance').innerHTML = (Math.round(distance * 100) / 100) + "Km";
    

//Error Handler
function showError(error) 
    switch(error.code) 
        case error.PERMISSION_DENIED:
            alert('User denied the request for Geolocation');
            break;
        case error.POSITION_UNAVAILABLE:
            alert('Location Not Available');
            break;
        case error.TIMEOUT:
            alert('The request has timed-out');
            break;
        case error.UNKNOWN_ERROR:
            alert('There was an unknown error');
            break;
    

//Calculate the distance between start and updated, Haversine Formula
function calculateDistance(lat1, lon1, lat2, lon2) 
    var R = 6371; // km  
    var dLat = (lat2-lat1).toRad();  
    var dLon = (lon2-lon1).toRad();   
    var a = Math.sin(dLat/2) * Math.sin(dLat/2) +  
            Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *   
            Math.sin(dLon/2) * Math.sin(dLon/2);   
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));   
    var d = R * c;
    return d;

Number.prototype.toRad = function() 
    return this * Math.PI / 180;

//Stop Tracking
function stopTracking() 
    navigator.geolocation.clearWatch(watchId);
    alert('Tracking has stopped');
    document.getElementById('startBtn').innerHTML = "Start Tracking?";

【问题讨论】:

什么? Haversine 计算球体(例如地球)表面上一条线的长度,它给出了从 A 点到 B 点的距离。如果您在谈论路由、使用道路等,那完全是 不同的问题,您可能最好使用地图路由 API(google、yahoo、OSM 等)。 我考虑过,但我相信这会增加真正沉重的开销。我是一个发展中国家的人,在所有地方都没有那么强大或可靠的蜂窝网络。因此,我想尽可能轻松地做到这一点。 我看不出你的代码有什么问题。您正在订阅位置信息,这将为您提供坐标流,对吗?保存最后一个位置并计算最后一个位置和新位置之间的位置。所以总距离 = a-&gt;b + b-&gt;c + c-&gt;d,而不是 a-&gt;d 【参考方案1】:

只需每隔 x 秒左右记录一次位移,然后对位移求和。你会有一个相当好的近似值。或者只是先记录不同时间的点,最后再计算。

var checkpoints = [],
    interval = null;

function record() 
  checkpoints.push( /* current position */ )


function distance(a, b) 
  return /* your distance calculation */


function start() 
  checkpoints = [];
  interval = setInterval(record, 1000); // record current pos every second


function end() 
  clearInterval(interval);

  var distanceTravelled = checkpoints.reduce(function(x, current, i, checkpoints)
    return x + distance(current, checkpoints[i-1]);
  , 0);

  // do something nice with the distance

更新

我刚刚意识到您将watch 与回调一起使用。你可以使用以下方法,

var lastPoint = null,
    travelled = 0;

function update(point) 
  if (lastPoint != null)
    travelled += calculateDistance(lastPoint.long, lastPoint.lat, point.long, point.lat);      

  lastPoint = point;

更新 2 工作解决方案:http://jsfiddle.net/ymz3a5pb/

【讨论】:

我这样做的灵感主要来自您的回答。唉,它有一些引用错误。你能调查一下吗? jsfiddle.net/sa4m0u1z 什么引用错误?也许您可以发布错误消息?另外,您选择了哪种方法? drive.google.com/file/d/0BwUagjky4-pbdjBuQjZDZU9WXzA/… 此类错误。我的 HTML 实体调用 startTracking 函数。所以我没有选择方法。我只是单击页面上的“开始跟踪”并调用 startTracking 函数。抱歉,我是 JS 新手。 好的,谢谢!这工作得很好。您正在将 startPos 初始化为 currentPos。好了,我懂了。昨天我做了类似的事情,猜想犯了一些语法错误(对一种语言来说是新手)。非常感谢! :) 您实际上可以在此处重新发布小提琴,以便我可以将其标记为已接受的解决方案并关闭此问题以供将来参考。我的意思是它只是我的第二个问题,这个问题,在论坛上,但我想这是一个很好的做法。

以上是关于如何使用 Haversine 公式计算行驶距离(不是位移)?的主要内容,如果未能解决你的问题,请参考以下文章

计算两个latitude-longitude点之间的距离? (Haversine公式)

Haversine 公式错误 - 距离不正确 - Arduino

Haversine 公式 - 数学略有偏差,不确定原因

对haversine公式使用列表推导

球形余弦Haversine公式

使用haversine公式,结果太高