如何计算由经纬度指定的两点之间的距离?

为了澄清,我想用千米来表示距离;这些点使用WGS84系统,我想了解可用方法的相对准确性。


当前回答

在我的项目中,我需要计算很多点之间的距离,所以我继续尝试优化我在这里找到的代码。平均而言,在不同的浏览器中,我的新实现的运行速度比获得最多好评的答案快2倍。

function distance(lat1, lon1, lat2, lon2) {
  var p = 0.017453292519943295;    // Math.PI / 180
  var c = Math.cos;
  var a = 0.5 - c((lat2 - lat1) * p)/2 + 
          c(lat1 * p) * c(lat2 * p) * 
          (1 - c((lon2 - lon1) * p))/2;

  return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km
}

您可以在这里使用我的jsPerf并查看结果。

最近我需要在python中做同样的事情,所以这里是一个python实现:

from math import cos, asin, sqrt, pi

def distance(lat1, lon1, lat2, lon2):
    p = pi/180
    a = 0.5 - cos((lat2-lat1)*p)/2 + cos(lat1*p) * cos(lat2*p) * (1-cos((lon2-lon1)*p))/2
    return 12742 * asin(sqrt(a)) #2*R*asin...

为了完整起见:维基百科上的Haversine。

其他回答

计算距离——尤其是大距离——的主要挑战之一是解释地球的曲率。如果地球是平的,计算两点之间的距离就会像计算直线一样简单!哈弗辛公式包括一个常数(下面是R变量),它表示地球的半径。根据你是用英里还是公里来测量,它分别等于3956英里或6367公里。 基本公式是:

Dlon = lon2 - lon1 dat = lat2 - lat1 = (sin (dlat / 2)) ^ 2 + cos (lat1) * cos (lat2) * (sin (dlon / 2)) ^ 2 C = 2 * atan2(√(a),√(1-a)) distance = R * c(其中R为地球半径) R = 6367公里OR 3956英里

     lat1, lon1: The Latitude and Longitude of point 1 (in decimal degrees)
     lat2, lon2: The Latitude and Longitude of point 2 (in decimal degrees)
     unit: The unit of measurement in which to calculate the results where:
     'M' is statute miles (default)
     'K' is kilometers
     'N' is nautical miles

样本

function distance(lat1, lon1, lat2, lon2, unit) {
    try {
        var radlat1 = Math.PI * lat1 / 180
        var radlat2 = Math.PI * lat2 / 180
        var theta = lon1 - lon2
        var radtheta = Math.PI * theta / 180
        var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
        dist = Math.acos(dist)
        dist = dist * 180 / Math.PI
        dist = dist * 60 * 1.1515
        if (unit == "K") {
            dist = dist * 1.609344
        }
        if (unit == "N") {
            dist = dist * 0.8684
        }
        return dist
    } catch (err) {
        console.log(err);
    }
}
function getDistanceFromLatLonInKm(position1, position2) {
    "use strict";
    var deg2rad = function (deg) { return deg * (Math.PI / 180); },
        R = 6371,
        dLat = deg2rad(position2.lat - position1.lat),
        dLng = deg2rad(position2.lng - position1.lng),
        a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
            + Math.cos(deg2rad(position1.lat))
            * Math.cos(deg2rad(position2.lat))
            * Math.sin(dLng / 2) * Math.sin(dLng / 2),
        c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    return R * c;
}

console.log(getDistanceFromLatLonInKm(
    {lat: 48.7931459, lng: 1.9483572},
    {lat: 48.827167, lng: 2.2459745}
));

非常感谢这一切。我在Objective-C iPhone应用程序中使用了以下代码:

const double PIx = 3.141592653589793;
const double RADIO = 6371; // Mean radius of Earth in Km

double convertToRadians(double val) {

   return val * PIx / 180;
}

-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 {

        double dlon = convertToRadians(place2.longitude - place1.longitude);
        double dlat = convertToRadians(place2.latitude - place1.latitude);

        double a = ( pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))) * cos(convertToRadians(place2.latitude)) * pow(sin(dlon / 2), 2);
        double angle = 2 * asin(sqrt(a));

        return angle * RADIO;
}

纬度和经度是十进制的。我没有在asin()调用中使用min(),因为我使用的距离非常小,以至于它们不需要min()。

它给出了错误的答案,直到我传入弧度的值-现在它几乎与从苹果地图应用程序中获得的值相同:-)

额外的更新:

如果你使用的是iOS4或更高版本,那么苹果会提供一些方法来实现相同的功能:

-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 {

    MKMapPoint  start, finish;


    start = MKMapPointForCoordinate(place1);
    finish = MKMapPointForCoordinate(place2);

    return MKMetersBetweenMapPoints(start, finish) / 1000;
}
function distance($lat1, $lon1, $lat2, $lon2) { 
    $pi80 = M_PI / 180; 
    $lat1 *= $pi80; $lon1 *= $pi80; $lat2 *= $pi80; $lon2 *= $pi80; 
    $dlat = $lat2 - $lat1; 
    $dlon = $lon2 - $lon1; 
    $a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlon / 2) * sin($dlon / 2);  
    $km = 6372.797 * 2 * atan2(sqrt($a), sqrt(1 - $a)); 
    return $km; 
}

这是一个简单的javascript函数,从这个链接可能是有用的。不知何故相关,但我们使用谷歌地球javascript插件而不是地图

function getApproximateDistanceUnits(point1, point2) {

    var xs = 0;
    var ys = 0;

    xs = point2.getX() - point1.getX();
    xs = xs * xs;

    ys = point2.getY() - point1.getY();
    ys = ys * ys;

    return Math.sqrt(xs + ys);
}

单位不是距离,而是相对于坐标的比率。还有其他相关的计算,你可以在这里代替getApproximateDistanceUnits函数链接

然后我使用这个函数来查看经纬度是否在半径内

function isMapPlacemarkInRadius(point1, point2, radi) {
    if (point1 && point2) {
        return getApproximateDistanceUnits(point1, point2) <= radi;
    } else {
        return 0;
    }
}

点可以定义为

 $$.getPoint = function(lati, longi) {
        var location = {
            x: 0,
            y: 0,
            getX: function() { return location.x; },
            getY: function() { return location.y; }
        };
        location.x = lati;
        location.y = longi;

        return location;
    };

然后你可以做你的事情,看看一个点是否在一个半径范围内,比如:

 //put it on the map if within the range of a specified radi assuming 100,000,000 units
        var iconpoint = Map.getPoint(pp.latitude, pp.longitude);
        var centerpoint = Map.getPoint(Settings.CenterLatitude, Settings.CenterLongitude);

        //approx ~200 units to show only half of the globe from the default center radius
        if (isMapPlacemarkInRadius(centerpoint, iconpoint, 120)) {
            addPlacemark(pp.latitude, pp.longitude, pp.name);
        }
        else {
            otherSidePlacemarks.push({
                latitude: pp.latitude,
                longitude: pp.longitude,
                name: pp.name
            });

        }