在Javascript中使用Haversine公式

时间:2013-01-28 11:26:05

标签: javascript haversine

我正在尝试使用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;
}

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

9 个答案:

答案 0 :(得分:42)

此代码正常运行:

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

播放

答案 1 :(得分:21)

这是基于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;
}

答案 2 :(得分:13)

为什么不尝试直接解决方案?而不是扩展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()

更好

答案 3 :(得分:8)

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 = (latlngA, latlngB, isMiles) => {
  const toRad = x => (x * Math.PI) / 180;
  const R = 6371; // km

  const dLat = toRad(latlngB[0] - latlngA[0]);
  const dLatSin = Math.sin(dLat / 2);
  const dLon = toRad(latlngB[1] - latlngA[1]);
  const dLonSin = Math.sin(dLon / 2);

  const a = (dLatSin * dLatSin) +
            (Math.cos(toRad(latlngA[1])) * Math.cos(toRad(latlngB[1])) * dLonSin * dLonSin);
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  let distance = R * c;

  if (isMiles) distance /= 1.60934;

  return distance;
}

答案 4 :(得分:1)

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

所以请确保

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

被称为

答案 5 :(得分:1)

  

当我把它放在功能

之下时

您只需将其置于调用test()的位置之上。声明test函数本身并不重要。

答案 6 :(得分:1)

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

答案 7 :(得分:0)

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

答案 8 :(得分:0)

这是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