在Javascript中使用Haversine公式

在Javascript中使用Haversine公式,javascript,haversine,Javascript,Haversine,我试图使用哈弗森距离公式(如这里所示:),但我无法让它工作,请参阅以下代码 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. va

我试图使用哈弗森距离公式(如这里所示:),但我无法让它工作,请参阅以下代码

    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;
}

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

为什么不试试直截了当的解决方案呢?不要扩展数字原型,只需将toRad定义为常规函数:

function toRad(x) {
   return x * Math.PI / 180;
}
然后到处调用
toRad

var dLat = toRad(lat2-lat1); 

扩展数字原型并不总是像预期的那样工作。例如,调用123.toRad()不起作用。我认为如果你做
varx1=lat2-lat1;x1.toRad()比执行
(lat2-lat1)工作得更好。toRad()

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

所以只要确保

Number.prototype.toRad = function() {
  return this * Math.PI / 180;
}
在调用函数之前调用

当我把它放在函数下面时

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

此代码正在工作:

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的。
在以下位置使用它:

这是上面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;
}

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

请注意,coords参数是[经度,纬度]

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;
}

另一个减少冗余并与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));
}

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以获取针对已接受答案的测试:

以下是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

不,它不需要放在函数定义之前。@bergi-很抱歉,你是对的-它需要在调用函数之前定义-将编辑我的答案。我遇到了同样的问题。我将一个toRadians()定义为一个实用函数,并像这样计算dLat和dLon:toRadians(lat1-lat2)。。。这给了我错误的答案,而不是先显式地计算差异,然后将其存储在变量中。为什么会这样?我也不知道为什么会这样@Parijat Kalia,我也有同样的问题。有一件事让我对你的函数感到困惑,你期望:
haversineddance([lng,lat],[lng,lat],isMiles)同样,不确定我是否实现了错误,但是这个答案给出了一个不准确的结果,而当前接受的答案,没有重构,给出了与谷歌地图相同的结果。更多细节请参见我对@Harry Mumford Turner答案的评论。这是否仍在km中?应该这样注释,以及使其返回英里数所需的更改。您正在访问
latlngA
latlngB
参数的第一个元素,以计算纬度的增量,但是函数的docblock声明第一个元素是经度。在针对以下坐标对Google Maps进行测试时,我使用此解决方案得到了一个不准确的结果:const latlngA=[52.375603,4.903206];常量latlngB=[52.366059,4.926692];此解决方案返回2.8km,而当前接受的答案正确返回1.92km(与Google Maps给出的1.91km非常匹配)。@Bigse谢谢,我已经修正了公式,使其更加准确和易于理解。这是我在stack上看到的最准确的结果,与Google Maps完美匹配,嗯,这是一些很好的干净的代码。我期待着回家后对它进行测试。关于OP给出的相同链接,这个问题的公认答案似乎很好地回答了这个问题: