【问题标题】:Using the Haversine Formula in Javascript在 Javascript 中使用 Haversine 公式
【发布时间】:2013-01-11 17:31:43
【问题描述】:

我正在尝试使用 Haversine 距离公式(可在此处找到:http://www.movable-type.co.uk/scripts/latlong.html),但无法使用,请参阅以下代码

    function test() { 
    var lat2 = 42.741; 
    var lon2 = -71.3161; 
    var lat1 = 42.806911; 
    var lon1 = -71.290611; 

    var R = 6371; // km 
    //has a problem with the .toRad() method below.
    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; 

    alert(d); 
}

错误是:

Uncaught TypeError: Object -0.06591099999999983 has no method 'toRad' 

我理解是因为它需要执行以下操作:

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

但是当我把它放在函数下面时,它仍然返回相同的错误消息。如何使它使用辅助方法?或者是否有另一种方法来编写代码以使其正常工作?谢谢!

【问题讨论】:

标签: javascript haversine


【解决方案1】:

此代码正在运行:

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

var lat2 = 42.741; 
var lon2 = -71.3161; 
var lat1 = 42.806911; 
var lon1 = -71.290611; 

var R = 6371; // km 
//has a problem with the .toRad() method below.
var x1 = lat2-lat1;
var dLat = x1.toRad();  
var x2 = lon2-lon1;
var dLon = x2.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; 

alert(d);

注意我是如何定义 x1 和 x2 的。 与它一起玩:https://tinker.io/3f794

【讨论】:

  • 我遇到了同样的问题。我将 toRadians() 定义为实用函数,并像这样计算 dLat 和 dLon: toRadians(lat1 - lat2) ... 这给了我错误的答案,而不是首先明确计算差异并将其存储在变量中。这是为什么呢?
  • 我也不知道为什么会这样@Parijat Kalia,我也遇到了同样的问题。
【解决方案2】:

这是基于其他 3 个答案的重构函数!

请注意,坐标参数是 [longitude, latitude]。

function haversineDistance(coords1, coords2, isMiles) {
  function toRad(x) {
    return x * Math.PI / 180;
  }

  var lon1 = coords1[0];
  var lat1 = coords1[1];

  var lon2 = coords2[0];
  var lat2 = coords2[1];

  var R = 6371; // km

  var x1 = lat2 - lat1;
  var dLat = toRad(x1);
  var x2 = lon2 - lon1;
  var dLon = toRad(x2)
  var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
    Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
    Math.sin(dLon / 2) * Math.sin(dLon / 2);
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  var d = R * c;

  if(isMiles) d /= 1.60934;

  return d;
}

【讨论】:

  • 一件事让我对你的功能感到困惑,你期待:haversineDistance([lng, lat], [lng, lat], isMiles);
  • 再次,不确定我是否实施了错误,但这个答案给出了不准确的结果,而当前接受的答案,没有重构,给出了与谷歌地图相同的结果。有关更多详细信息,请参阅我对 @Harry Mumford-Turner 的回答的评论。
  • 太好了——谢谢!作为参考,这里是相同的函数,除了压缩并且总是返回 KM:function distKM(lat1,lon1,lat2,lon2){var a=Math,r=(lat2-lat1)*a.PI/180,c=(lon2-lon1)*a.PI/180,e=a.sin(r/2)*a.sin(r/2)+a.cos(lat1*a.PI/180)*a.cos(lat2*a.PI/180)*a.sin(c/2)*a.sin(c/2);return d=2*a.atan2(a.sqrt(e),a.sqrt(1-e))*6371}
【解决方案3】:

ES6 JavaScript/NodeJS 重构版本:

   /**
     * Calculates the haversine distance between point A, and B.
     * @param {number[]} latlngA [lat, lng] point A
     * @param {number[]} latlngB [lat, lng] point B
     * @param {boolean} isMiles If we are using miles, else km.
     */
    const haversineDistance = ([lat1, lon1], [lat2, lon2], isMiles = false) => {
      const toRadian = angle => (Math.PI / 180) * angle;
      const distance = (a, b) => (Math.PI / 180) * (a - b);
      const RADIUS_OF_EARTH_IN_KM = 6371;

      const dLat = distance(lat2, lat1);
      const dLon = distance(lon2, lon1);

      lat1 = toRadian(lat1);
      lat2 = toRadian(lat2);

      // Haversine Formula
      const a =
        Math.pow(Math.sin(dLat / 2), 2) +
        Math.pow(Math.sin(dLon / 2), 2) * Math.cos(lat1) * Math.cos(lat2);
      const c = 2 * Math.asin(Math.sqrt(a));

      let finalDistance = RADIUS_OF_EARTH_IN_KM * c;

      if (isMiles) {
        finalDistance /= 1.60934;
      }

      return finalDistance;
    };

有关针对已接受答案的测试,请参阅 codepen:https://codepen.io/harrymt/pen/dyYvLpJ?editors=1011

【讨论】:

  • 您正在访问latlngAlatlngB 参数的第一个元素来计算纬度的增量,但该函数的文档块指出第一个元素是经度。跨度>
  • 在针对以下坐标对 Google 地图进行测试时,我使用此解决方案得到了不准确的结果:const latlngA = [52.375603, 4.903206];常量 latlngB = [52.366059, 4.926692];此解决方案返回 2.8 公里,而当前接受的答案正确返回 1.92 公里(与谷歌地图给出的 1.91 公里非常匹配)。
  • @bigsee 谢谢,我已将公式修正为更准确和易于理解
  • 这是我在堆栈上看到的最准确的,并且与 googlemaps 完美匹配,干得好
  • 那是一些不错的干净代码。我期待着回家后对其进行测试
【解决方案4】:

为什么不尝试直接的解决方案?无需扩展 Number 原型,只需将 toRad 定义为常规函数即可:

function toRad(x) {
   return x * Math.PI / 180;
}

然后到处打电话给toRad

var dLat = toRad(lat2-lat1); 

扩展 Number 原型并不总是按预期工作。例如调用 123.toRad() 不起作用。我认为如果你做var x1 = lat2 - lat1; x1.toRad(); 比做(lat2-lat1).toRad() 效果更好

【讨论】:

    【解决方案5】:

    当我把它放在函数下面时

    您只需将其放在您调用test() 的位置上方。 test 函数本身的声明位置无关紧要。

    【讨论】:

      【解决方案6】:

      在函数中调用这些扩展之前,您需要扩展 Number 原型。

      所以只要确保

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

      被调用之前你的函数被调用。

      【讨论】:

      • 不,不需要放在函数定义之前。
      • @bergi - 对不起,你是对的 - 它需要在调用函数之前定义 - 将编辑我的答案。
      【解决方案7】:

      另一个减少冗余并与 Google LatLng 对象兼容的变体:

        function haversine_distance(coords1, coords2) {
      
           function toRad(x) {
               return x * Math.PI / 180;
          }
      
        var dLat = toRad(coords2.latitude - coords1.latitude);
        var dLon = toRad(coords2.longitude - coords1.longitude)
      
        var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                Math.cos(toRad(coords1.latitude)) * 
                Math.cos(toRad(coords2.latitude)) *
                Math.sin(dLon / 2) * Math.sin(dLon / 2);
      
        return 12742 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
      }
      

      【讨论】:

      • 这还是公里吗?应该这样评论它,以及使它返回里程所需的更改。
      【解决方案8】:

      这是另一个用 JavaScript 重构的答案:

      getHaversineDistance = (firstLocation, secondLocation) => {
          const earthRadius = 6371; // km 
      
          const diffLat = (secondLocation.lat-firstLocation.lat) * Math.PI / 180;  
          const diffLng = (secondLocation.lng-firstLocation.lng) * Math.PI / 180;  
      
          const arc = Math.cos(
                          firstLocation.lat * Math.PI / 180) * Math.cos(secondLocation.lat * Math.PI / 180) 
                          * Math.sin(diffLng/2) * Math.sin(diffLng/2)
                          + Math.sin(diffLat/2) * Math.sin(diffLat/2);
          const line = 2 * Math.atan2(Math.sqrt(arc), Math.sqrt(1-arc));
      
          const distance = earthRadius * line; 
      
          return distance;
      }
      
      const philly = { lat: 39.9526, lng: -75.1652 }
      const nyc = { lat: 40.7128, lng: -74.0060 }
      const losAngeles = { lat: 34.0522, lng: -118.2437 }
      
      console.log(getHaversineDistance(philly, nyc)) //129.61277152662188
      console.log(getHaversineDistance(philly, losAngeles)) //3843.4534005980404
      

      【讨论】:

        【解决方案9】:

        这是上面talkol解决方案的java实现。他或她的解决方案对我们非常有效。我不想回答这个问题,因为最初的问题是针对 javascript 的。我只是分享给定 javascript 解决方案的 java 实现,以防其他人发现它有用。

        // this was a pojo class we used internally...
        public class GisPostalCode {
        
            private String country;
            private String postalCode;
            private double latitude;
            private double longitude;
        
            // getters/setters, etc.
        }
        
        
        public static double distanceBetweenCoordinatesInMiles2(GisPostalCode c1, GisPostalCode c2) {
        
            double lat2 = c2.getLatitude();
            double lon2 = c2.getLongitude();
            double lat1 = c1.getLatitude();
            double lon1 = c1.getLongitude();
        
            double R = 6371; // km
            double x1 = lat2 - lat1;
            double dLat = x1 * Math.PI / 180;
            double x2 = lon2 - lon1;
            double dLon = x2 * Math.PI / 180;
        
            double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
                Math.cos(lat1*Math.PI/180) * Math.cos(lat2*Math.PI/180) *
                Math.sin(dLon/2) * Math.sin(dLon/2);
        
            double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
            double d = R * c;
        
            // convert to miles
            return d / 1.60934;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-10-21
          • 2011-09-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-03-05
          • 2013-04-26
          • 1970-01-01
          相关资源
          最近更新 更多