计算两个经度点之间的距离?(Haversine公式)


905

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

为了澄清起见,我想要以千米为单位的距离;这些要点使用WGS84系统,我想了解可用方法的相对准确性。


为了获得更好的精度-见stackoverflow.com/questions/1420045/...
利奥尔高根

3
请注意,您不能在诸如WGS 84的旋转椭球上应用Haversine公式。您只能在具有半径的球体上应用此方法。
Mike T

3
这里的大多数答案都使用简单的球三角函数,因此与GPS系统中使用的WGS84椭球距离相比,结果相当粗糙。某些答案确实参考了Vincenty的椭球公式,但该算法是为1960年代的时代台式计算器设计的,具有稳定性和准确性问题。我们现在有更好的硬件和软件。请参阅GeographicLib以获取具有各种语言实现的高质量库。
下午18年

@MikeT-是的,尽管这里的许多答案在短距离内似乎都很有用:如果您从WGS 84上取得经/纬度,并像将其作为球体上的点那样应用Haversine ,您不会得到答案的错误仅是由于地球的扁平系数,那么也许在一个更精确的公式的1%之内?请注意,距离很小,例如在一个城镇内。
制造商史蒂夫

1
对于这些平台:Mono / .NET 4.5 / .NET Core / Windows Phone 8.x /通用Windows平台/ Xamarin iOS / Xamarin Android,请参见stackoverflow.com/a/54296314/2736742
A. Morel,

Answers:


1145

链接可能会对您有所帮助,因为它详细说明了使用Haversine公式计算距离的方法。

摘抄:

该脚本(使用Java语言)使用“ Haversine”公式计算两点之间的大圆距离-即地球表面上的最短距离。

function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) {
  var R = 6371; // Radius of the earth in km
  var dLat = deg2rad(lat2-lat1);  // deg2rad below
  var dLon = deg2rad(lon2-lon1); 
  var a = 
    Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(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; // Distance in km
  return d;
}

function deg2rad(deg) {
  return deg * (Math.PI/180)
}

51
此计算/方法是否将地球视为椭球体(不是理想球体)?最初的问题要求在WGS84地球仪上两点之间的距离。不确定使用完美球体会导致多少误差,但是我怀疑误差可能会很大,具体取决于点在地球上的位置,因此应该牢记这种区别。
redcalx

15
Haversine公式不能说明地球是一个球体,因此您会由于这个事实而引入一些错误。不能保证其正确率超过0.5%。但是,这可能是也可能不是可接受的错误级别。
布兰登

24
有什么理由要Math.atan2(Math.sqrt(a), Math.sqrt(1-a))代替Math.asin(Math.sqrt(h)),这将是Wikipedia文章使用的公式的直接实现?它更有效和/或更数值稳定吗?
musiphil

16
@UsmanMutawakil好吧,您获得的38英里就是道路上的距离。该算法计算地球表面上的直线距离。Google Maps有一个距离工具(左下角为“实验室”),其作用相同,可以用来进行比较。
Pascal

4
@ Forte_201092:因为这是没有必要-因为(sin(x))²平等(sin(-x))²
让Hominal

359

我需要为我的项目计算很多点之间的距离,所以我继续尝试优化代码,我在这里找到了。平均而言,在不同的浏览器中,我的新实现比最喜欢的答案运行速度快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...

为了完整起见:Wiki上的Haversine


13
@AngularM,如果您要走一些道路而不是一条直线,谷歌很可能会计算距离。
萨尔瓦多·达利

3
Google计算行驶距离,这计算“乌鸦飞翔”
爱好者

4
@Ouadie会提高速度吗?最有可能没有,但我会最终有一个很大的“你的东西不工作”为谁copypaste它在旧的浏览器的人
达利

4
是的,但是// 2 * R; R = 6371 km代表什么呢?目前的方法提供的答案是公里还是英里?需要更好的文档。谢谢
Khalil Khalaf

20
@KhalilKhalaf您是在开玩笑还是想在这里拖钓?km代表公里。您认为R代表什么(特别是如果我们谈论sheepe)?如果您已经看到公里,请猜猜答案将以什么单位表示。您在这里寻找什么样的文档:那里实际上有4行。
萨尔瓦多·达利

69

这是一个C#实现:

static class DistanceAlgorithm
{
    const double PIx = 3.141592653589793;
    const double RADIUS = 6378.16;

    /// <summary>
    /// Convert degrees to Radians
    /// </summary>
    /// <param name="x">Degrees</param>
    /// <returns>The equivalent in radians</returns>
    public static double Radians(double x)
    {
        return x * PIx / 180;
    }

    /// <summary>
    /// Calculate the distance between two places.
    /// </summary>
    /// <param name="lon1"></param>
    /// <param name="lat1"></param>
    /// <param name="lon2"></param>
    /// <param name="lat2"></param>
    /// <returns></returns>
    public static double DistanceBetweenPlaces(
        double lon1,
        double lat1,
        double lon2,
        double lat2)
    {
        double dlon = Radians(lon2 - lon1);
        double dlat = Radians(lat2 - lat1);

        double a = (Math.Sin(dlat / 2) * Math.Sin(dlat / 2)) + Math.Cos(Radians(lat1)) * Math.Cos(Radians(lat2)) * (Math.Sin(dlon / 2) * Math.Sin(dlon / 2));
        double angle = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
        return angle * RADIUS;
    }

}

14
您使用的是赤道半径,但您应该使用的平均半径为6371公里
Philippe Leybaert 2009年

7
如果不是这是double dlon = Radians(lon2 - lon1);double dlat = Radians(lat2 - lat1);
克里斯Marisic

我同意克里斯·马里西奇(Chris Marisic)的观点。我使用原始代码,计算错误。我添加了将增量转换为弧度的调用,并且现在可以正常使用。我提交了修改,正在等待同行评审。
布莱恩·贝亚德

我提交了另一个修改,因为lat1和lat2也需要转换为弧度。我还修改了分配公式,使其与此处找到的公式和代码匹配:movable-type.co.uk/scripts/latlong.html
Bryan Bedard

RADIUS与其他答案一样,值是否需要为6371?
克里斯·海斯

66

这是Haversine公式的Java实现。

public final static double AVERAGE_RADIUS_OF_EARTH_KM = 6371;
public int calculateDistanceInKilometer(double userLat, double userLng,
  double venueLat, double venueLng) {

    double latDistance = Math.toRadians(userLat - venueLat);
    double lngDistance = Math.toRadians(userLng - venueLng);

    double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
      + Math.cos(Math.toRadians(userLat)) * Math.cos(Math.toRadians(venueLat))
      * Math.sin(lngDistance / 2) * Math.sin(lngDistance / 2);

    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

    return (int) (Math.round(AVERAGE_RADIUS_OF_EARTH_KM * c));
}

请注意,这里我们将答案四舍五入到最接近的公里。


2
如果我们要计算两点之间的距离(以米为单位),哪种方法更准确?要使用6371000为地球半径?(平均地球半径为6371000米)还是将千米转换为您的功能?
微型版

如果您想要里程,则将结果乘以0.621371
lasec0203 '19

42

非常感谢所有这些。我在我的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(),因为我使用的距离非常小,以至于他们不需要它。

直到我在Radians中传递值之前,它给出的答案都是不正确的-现在,它与从Apple的Map应用程序中获得的值几乎相同:-)

额外更新:

如果您使用的是iOS4或更高版本,则Apple提供了一些方法来执行此操作,因此可以使用以下方法实现相同的功能:

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

    MKMapPoint  start, finish;


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

    return MKMetersBetweenMapPoints(start, finish) / 1000;
}


我认为圆括号pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))不正确。删除这些内容,结果与我在此页面上使用其他实现或从头开始从Wikipedia实现Haversine公式时得到的结果匹配。
zanedp

使用纽约的坐标(40.7127837,-74.0059413)和洛杉矶的坐标(34.052234,-118.243685)(),得到的总和约为3869.75。没有它们,我会得到3935.75,这几乎是网络搜索的结果。
zanedp

40

这是一个简单的PHP函数,可以给出非常合理的近似值(误差范围在+/- 1%以下)。

<?php
function distance($lat1, $lon1, $lat2, $lon2) {

    $pi80 = M_PI / 180;
    $lat1 *= $pi80;
    $lon1 *= $pi80;
    $lat2 *= $pi80;
    $lon2 *= $pi80;

    $r = 6372.797; // mean radius of Earth in km
    $dlat = $lat2 - $lat1;
    $dlon = $lon2 - $lon1;
    $a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlon / 2) * sin($dlon / 2);
    $c = 2 * atan2(sqrt($a), sqrt(1 - $a));
    $km = $r * $c;

    //echo '<br/>'.$km;
    return $km;
}
?>

如前所述;地球不是一个球体。就像马克·麦格威尔(Mark McGwire)决定练习的古老棒球一样,它充满了凹痕和颠簸。更简单的计算(像这样)将其视为一个球体。

根据您在此不规则卵形上的位置以及点之间的距离(距离越近,绝对误差范围越小),不同的方法可能或多或少精确。您的期望越精确,数学就越复杂。

有关更多信息:Wikipedia地理距离


4
这样完美!我刚刚添加了$ distance_miles = $ km * 0.621371; 这就是我所需的大约英里数的全部距离!谢谢托尼。

31

我在这里发布我的工作示例。

列出表中指定点之间的距离(我们使用随机点-lat:45.20327,long:23.7806)且小于50 KM(经度和纬度)的所有点(在MySQL中,表字段为coord_lat和coord_long):

列出所有DISTANCE <50(以公里为单位)(考虑到地球半径6371 KM):

SELECT denumire, (6371 * acos( cos( radians(45.20327) ) * cos( radians( coord_lat ) ) * cos( radians( 23.7806 ) - radians(coord_long) ) + sin( radians(45.20327) ) * sin( radians(coord_lat) ) )) AS distanta 
FROM obiective 
WHERE coord_lat<>'' 
    AND coord_long<>'' 
HAVING distanta<50 
ORDER BY distanta desc

上面的示例已在MySQL 5.0.95和5.5.16(Linux)中进行了测试。


我认为一种好的方法可能是使用近似值对结果进行预过滤,因此繁重的公式仅适用于某些情况。如果您有其他条件,则特别有用。我正在使用它作为初始的aprox:stackoverflow.com/questions/1253499/…–
Pato

28

在其他答案中的实现 不见了。

distm使用geosphere包中的函数,计算两点之间的距离非常简单:

distm(p1, p2, fun = distHaversine)

哪里:

p1 = longitude/latitude for point(s)
p2 = longitude/latitude for point(s)
# type of distance calculation
fun = distCosine / distHaversine / distVincentySphere / distVincentyEllipsoid 

由于地球不是完美的球形,因此椭球Vincenty公式可能是计算距离的最佳方法。因此,geosphere您可以在软件包中使用:

distm(p1, p2, fun = distVincentyEllipsoid)

当然,您不必一定要使用geospherepackage,您还可以R使用以下函数计算基本距离:

hav.dist <- function(long1, lat1, long2, lat2) {
  R <- 6371
  diff.long <- (long2 - long1)
  diff.lat <- (lat2 - lat1)
  a <- sin(diff.lat/2)^2 + cos(lat1) * cos(lat2) * sin(diff.long/2)^2
  b <- 2 * asin(pmin(1, sqrt(a))) 
  d = R * b
  return(d)
}

为了确保我清楚您的意思:文章末尾给出的代码:这是Vincenty公式的实现吗?据您所知,它应该给出与在地球圈中调用Vincenty相同的答案?[我没有地理圈或其他图书馆;只是寻找一些包含在跨平台应用程序中的代码。我当然会用一个已知的好的计算器验证一些测试用例。]
ToolmakerSteve

1
@ToolmakerSteve我答案结尾的功能是Haversine方法的实现
Jaap

嗨,@ Jaap,我能问一下公式的计量单位是多少吗?以米为单位吗?
杰克逊

11

在大多数情况下,haversine绝对是一个很好的公式,其他答案已经包含了它,因此我不会占用空间。但是需要注意的是,无论使用什么公式(不仅是一个公式),都必须注意。由于可能的精确度范围以及所需的计算时间很大。公式的选择比简单的没有头脑的答案需要更多的思考。

这是美国国家航空航天局(NASA)某人的帖子,是我在讨论这些选项时发现的最好的帖子

http://www.cs.nyu.edu/visual/home/proj/tiger/gisfaq.html

例如,如果您只是按半径100英里内的距离对行进行排序。平地配方将比haversine快得多。

HalfPi = 1.5707963;
R = 3956; /* the radius gives you the measurement unit*/

a = HalfPi - latoriginrad;
b = HalfPi - latdestrad;
u = a * a + b * b;
v = - 2 * a * b * cos(longdestrad - longoriginrad);
c = sqrt(abs(u + v));
return R * c;

注意,只有一个余弦和一个平方根。与Haversine公式相比,它们中的9分。


这是一个很好的可能性。请注意,讨论中建议的最大距离是12英里,而不是100英里,即使如此,根据地球仪的位置,误差可能会上升到30米(100英尺)。
Eric Wu

7

您可以使用CLLocationDistance中的构建来计算以下内容:

CLLocation *location1 = [[CLLocation alloc] initWithLatitude:latitude1 longitude:longitude1];
CLLocation *location2 = [[CLLocation alloc] initWithLatitude:latitude2 longitude:longitude2];
[self distanceInMetersFromLocation:location1 toLocation:location2]

- (int)distanceInMetersFromLocation:(CLLocation*)location1 toLocation:(CLLocation*)location2 {
    CLLocationDistance distanceInMeters = [location1 distanceFromLocation:location2];
    return distanceInMeters;
}

在您的情况下,如果您想将公里数除以1000。


7

我不喜欢再添加一个答案,但是Google Maps API v.3具有球形几何图形(以及更多)。将WGS84转换为十进制度后,您可以执行以下操作:

<script src="http://maps.google.com/maps/api/js?sensor=false&libraries=geometry" type="text/javascript"></script>  

distance = google.maps.geometry.spherical.computeDistanceBetween(
    new google.maps.LatLng(fromLat, fromLng), 
    new google.maps.LatLng(toLat, toLng));

关于Google的计算准确度,甚至使用的模型,都一言不发(尽管它的确说的是“球形”而不是“ geoid”。顺便说一句,“直线”的距离显然会与人在飞机上行驶的距离有所不同)。每个人似乎都在推测地球表面。


距离以米为单位。备选地可以使用computeLength()
electrobabe

7

Python的实现Origin是连续的美国的中心。

from haversine import haversine
origin = (39.50, 98.35)
paris = (48.8567, 2.3508)
haversine(origin, paris, miles=True)

要获得以千米为单位的答案,只需将miles = false设置为即可。


1
您正在导入完成所有工作的非标准软件包。我不知道这是否有用。
Teepeemm,2015年

该软件包与numpy和scikit-learn一起作为python 3软件包包含在PyPI,Python软件包索引中。不知道为什么将一个包装。它们往往很有用。作为开源,也可以检查其中包含的方法。我认为许多人会觉得此软件包很有用,因此尽管投票不足,我仍将离开该职位。干杯。:)
invoketheshell

7

可能有一个更简单,更正确的解决方案:赤道的地球周长为40,000Km,格林威治(或任何经度)周期的地球周长约为37,000。从而:

pythagoras = function (lat1, lon1, lat2, lon2) {
   function sqr(x) {return x * x;}
   function cosDeg(x) {return Math.cos(x * Math.PI / 180.0);}

   var earthCyclePerimeter = 40000000.0 * cosDeg((lat1 + lat2) / 2.0);
   var dx = (lon1 - lon2) * earthCyclePerimeter / 360.0;
   var dy = 37000000.0 * (lat1 - lat2) / 360.0;

   return Math.sqrt(sqr(dx) + sqr(dy));
};

我同意应该对其进行微调,因为我自己说这是一个椭球,因此要乘以余弦的半径会有所不同。但这更加准确。与Google Maps相比,它确实减少了错误。


该函数返回距离以公里为单位吗?
Wikki

只是因为赤道和经度周期的单位是Km。对于英里,只需将40000和37000除以1.6。感到怪异,您可以将其转换为Ris,乘以7或转换为parasang,除以2.2 ;-)
Meymann

这似乎是这里提供的最佳答案。我希望使用它,但我只是想知道是否有一种方法可以验证此算法的正确性。我测试了f(50,5,58,3)。它而赋予832公里,movable-type.co.uk/scripts/latlong.html使用“半正矢”公式给出899公里。有这么大的区别吗?
Chong Lip Phang

而且,我认为上述代码返回的值以m为单位,而不是km。
Chong Lip Phang

@ChongLipPhang-注意:勾股定理仅是小区域的合理近似,因为该定理假设地球是平坦的。在极端情况下,请从赤道开始,然后向东移动90度,向北移动90度。当然,最终结果是北极,与向东0度和向北90度移动相同;因此,在第一种情况下,执行sqrt(sqr(dx)+ sqr(dy))将非常困难。〜sqrt(10km sqr + 10km sqr)〜= 14.4 km vs正确距离〜10km。
制造商史蒂夫

7

以上所有答案均假设地球是一个球体。但是,更精确的近似是扁球形。

a= 6378.137#equitorial radius in km
b= 6356.752#polar radius in km

def Distance(lat1, lons1, lat2, lons2):
    lat1=math.radians(lat1)
    lons1=math.radians(lons1)
    R1=(((((a**2)*math.cos(lat1))**2)+(((b**2)*math.sin(lat1))**2))/((a*math.cos(lat1))**2+(b*math.sin(lat1))**2))**0.5 #radius of earth at lat1
    x1=R*math.cos(lat1)*math.cos(lons1)
    y1=R*math.cos(lat1)*math.sin(lons1)
    z1=R*math.sin(lat1)

    lat2=math.radians(lat2)
    lons2=math.radians(lons2)
    R1=(((((a**2)*math.cos(lat2))**2)+(((b**2)*math.sin(lat2))**2))/((a*math.cos(lat2))**2+(b*math.sin(lat2))**2))**0.5 #radius of earth at lat2
    x2=R*math.cos(lat2)*math.cos(lons2)
    y2=R*math.cos(lat2)*math.sin(lons2)
    z2=R*math.sin(lat2)

    return ((x1-x2)**2+(y1-y2)**2+(z1-z2)**2)**0.5

6

这是SQL实现,用于以km为单位计算距离,

SELECT UserId, ( 3959 * acos( cos( radians( your latitude here ) ) * cos( radians(latitude) ) * 
cos( radians(longitude) - radians( your longitude here ) ) + sin( radians( your latitude here ) ) * 
sin( radians(latitude) ) ) ) AS distance FROM user HAVING
distance < 5  ORDER BY distance LIMIT 0 , 5;

有关通过编程语言实现的更多详细信息,您可以浏览此处给出的php脚本


5

这是Haversine公式的打字稿实现

static getDistanceFromLatLonInKm(lat1: number, lon1: number, lat2: number, lon2: number): number {
    var deg2Rad = deg => {
        return deg * Math.PI / 180;
    }

    var r = 6371; // Radius of the earth in km
    var dLat = deg2Rad(lat2 - lat1);   
    var dLon = deg2Rad(lon2 - lon1);
    var a =
        Math.sin(dLat / 2) * Math.sin(dLat / 2) +
        Math.cos(deg2Rad(lat1)) * Math.cos(deg2Rad(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; // Distance in km
    return d;
}

5

如前所述,准确的计算应考虑到地球不是一个完美的球体。以下是此处提供的各种算法的一些比较:

geoDistance(50,5,58,3)
Haversine: 899 km
Maymenn: 833 km
Keerthana: 897 km
google.maps.geometry.spherical.computeDistanceBetween(): 900 km

geoDistance(50,5,-58,-3)
Haversine: 12030 km
Maymenn: 11135 km
Keerthana: 10310 km
google.maps.geometry.spherical.computeDistanceBetween(): 12044 km

geoDistance(.05,.005,.058,.003)
Haversine: 0.9169 km
Maymenn: 0.851723 km
Keerthana: 0.917964 km
google.maps.geometry.spherical.computeDistanceBetween(): 0.917964 km

geoDistance(.05,80,.058,80.3)
Haversine: 33.37 km
Maymenn: 33.34 km
Keerthana: 33.40767 km
google.maps.geometry.spherical.computeDistanceBetween(): 33.40770 km

在很小的距离上,Kerthana的算法似乎确实与Google Maps的算法一致。Google Maps似乎没有遵循任何简单的算法,这表明这可能是最准确的方法。

无论如何,这是Keerthana算法的Javascript实现:

function geoDistance(lat1, lng1, lat2, lng2){
    const a = 6378.137; // equitorial radius in km
    const b = 6356.752; // polar radius in km

    var sq = x => (x*x);
    var sqr = x => Math.sqrt(x);
    var cos = x => Math.cos(x);
    var sin = x => Math.sin(x);
    var radius = lat => sqr((sq(a*a*cos(lat))+sq(b*b*sin(lat)))/(sq(a*cos(lat))+sq(b*sin(lat))));

    lat1 = lat1 * Math.PI / 180;
    lng1 = lng1 * Math.PI / 180;
    lat2 = lat2 * Math.PI / 180;
    lng2 = lng2 * Math.PI / 180;

    var R1 = radius(lat1);
    var x1 = R1*cos(lat1)*cos(lng1);
    var y1 = R1*cos(lat1)*sin(lng1);
    var z1 = R1*sin(lat1);

    var R2 = radius(lat2);
    var x2 = R2*cos(lat2)*cos(lng2);
    var y2 = R2*cos(lat2)*sin(lng2);
    var z2 = R2*sin(lat2);

    return sqr(sq(x1-x2)+sq(y1-y2)+sq(z1-z2));
}

4

此脚本(在PHP中)计算两点之间的距离。

public static function getDistanceOfTwoPoints($source, $dest, $unit='K') {
        $lat1 = $source[0];
        $lon1 = $source[1];
        $lat2 = $dest[0];
        $lon2 = $dest[1];

        $theta = $lon1 - $lon2;
        $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +  cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
        $dist = acos($dist);
        $dist = rad2deg($dist);
        $miles = $dist * 60 * 1.1515;
        $unit = strtoupper($unit);

        if ($unit == "K") {
            return ($miles * 1.609344);
        }
        else if ($unit == "M")
        {
            return ($miles * 1.609344 * 1000);
        }
        else if ($unit == "N") {
            return ($miles * 0.8684);
        } 
        else {
            return $miles;
        }
    }

4

Haversine公式中的 Java实现

double calculateDistance(double latPoint1, double lngPoint1, 
                         double latPoint2, double lngPoint2) {
    if(latPoint1 == latPoint2 && lngPoint1 == lngPoint2) {
        return 0d;
    }

    final double EARTH_RADIUS = 6371.0; //km value;

    //converting to radians
    latPoint1 = Math.toRadians(latPoint1);
    lngPoint1 = Math.toRadians(lngPoint1);
    latPoint2 = Math.toRadians(latPoint2);
    lngPoint2 = Math.toRadians(lngPoint2);

    double distance = Math.pow(Math.sin((latPoint2 - latPoint1) / 2.0), 2) 
            + Math.cos(latPoint1) * Math.cos(latPoint2)
            * Math.pow(Math.sin((lngPoint2 - lngPoint1) / 2.0), 2);
    distance = 2.0 * EARTH_RADIUS * Math.asin(Math.sqrt(distance));

    return distance; //km value
}

3

要计算球体上两个点之间的距离,您需要进行“ 大圆”计算

如果需要将距离重新投影到平面上,有许多C / C ++库可帮助MapTools进行地图投影。为此,您将需要各种坐标系的投影字符串。

您可能还会发现MapWindow是可视化这些点的有用工具。作为其开源,它也是如何使用proj.dll库的有用指南,该库似乎是核心的开源投影库。


3

如果有人需要,这是移植到Java的可接受的答案实现。

package com.project529.garage.util;


/**
 * Mean radius.
 */
private static double EARTH_RADIUS = 6371;

/**
 * Returns the distance between two sets of latitudes and longitudes in meters.
 * <p/>
 * Based from the following JavaScript SO answer:
 * http://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula,
 * which is based on https://en.wikipedia.org/wiki/Haversine_formula (error rate: ~0.55%).
 */
public double getDistanceBetween(double lat1, double lon1, double lat2, double lon2) {
    double dLat = toRadians(lat2 - lat1);
    double dLon = toRadians(lon2 - lon1);

    double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
            Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) *
                    Math.sin(dLon / 2) * Math.sin(dLon / 2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    double d = EARTH_RADIUS * c;

    return d;
}

public double toRadians(double degrees) {
    return degrees * (Math.PI / 180);
}

2

这是VB.NET的实现,此实现将根据您传递的Enum值为您提供KM或Miles的结果。

Public Enum DistanceType
    Miles
    KiloMeters
End Enum

Public Structure Position
    Public Latitude As Double
    Public Longitude As Double
End Structure

Public Class Haversine

    Public Function Distance(Pos1 As Position,
                             Pos2 As Position,
                             DistType As DistanceType) As Double

        Dim R As Double = If((DistType = DistanceType.Miles), 3960, 6371)

        Dim dLat As Double = Me.toRadian(Pos2.Latitude - Pos1.Latitude)

        Dim dLon As Double = Me.toRadian(Pos2.Longitude - Pos1.Longitude)

        Dim a As Double = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(Me.toRadian(Pos1.Latitude)) * Math.Cos(Me.toRadian(Pos2.Latitude)) * Math.Sin(dLon / 2) * Math.Sin(dLon / 2)

        Dim c As Double = 2 * Math.Asin(Math.Min(1, Math.Sqrt(a)))

        Dim result As Double = R * c

        Return result

    End Function

    Private Function toRadian(val As Double) As Double

        Return (Math.PI / 180) * val

    End Function

End Class

在计算“ a”时,您是否错误地编写了Math.Sin(dLat ..)两次?
Marco Ottina

2

我通过简化公式来简化计算。

在Ruby中:

include Math
earth_radius_mi = 3959
radians = lambda { |deg| deg * PI / 180 }
coord_radians = lambda { |c| { :lat => radians[c[:lat]], :lng => radians[c[:lng]] } }

# from/to = { :lat => (latitude_in_degrees), :lng => (longitude_in_degrees) }
def haversine_distance(from, to)
  from, to = coord_radians[from], coord_radians[to]
  cosines_product = cos(to[:lat]) * cos(from[:lat]) * cos(from[:lng] - to[:lng])
  sines_product = sin(to[:lat]) * sin(from[:lat])
  return earth_radius_mi * acos(cosines_product + sines_product)
end

2
function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2,units) {
  var R = 6371; // Radius of the earth in km
  var dLat = deg2rad(lat2-lat1);  // deg2rad below
  var dLon = deg2rad(lon2-lon1); 
  var a = 
    Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(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; 
  var miles = d / 1.609344; 

if ( units == 'km' ) {  
return d; 
 } else {
return miles;
}}

Chuck的解决方案也适用于英里数。


2

这是我通过一些搜索后通过十进制度数计算距离的java实现。我使用的是世界平均半径(来自维基百科),以公里为单位。如果您想要结果英里,则使用以英里为单位的世界半径。

public static double distanceLatLong2(double lat1, double lng1, double lat2, double lng2) 
{
  double earthRadius = 6371.0d; // KM: use mile here if you want mile result

  double dLat = toRadian(lat2 - lat1);
  double dLng = toRadian(lng2 - lng1);

  double a = Math.pow(Math.sin(dLat/2), 2)  + 
          Math.cos(toRadian(lat1)) * Math.cos(toRadian(lat2)) * 
          Math.pow(Math.sin(dLng/2), 2);

  double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

  return earthRadius * c; // returns result kilometers
}

public static double toRadian(double degrees) 
{
  return (degrees * Math.PI) / 180.0d;
}

2

在Mysql中,使用以下函数将参数传递为 POINT(LONG,LAT)

CREATE FUNCTION `distance`(a POINT, b POINT)
 RETURNS double
    DETERMINISTIC
BEGIN

RETURN

GLength( LineString(( PointFromWKB(a)), (PointFromWKB(b)))) * 100000; -- To Make the distance in meters

END;

2
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(position1.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}
));

2

这是postgres sql中的示例(以km为单位,对于Miles版本,将1.609344替换为0.8684版本)

CREATE OR REPLACE FUNCTION public.geodistance(alat float, alng float, blat  

float, blng  float)
  RETURNS float AS
$BODY$
DECLARE
    v_distance float;
BEGIN

    v_distance = asin( sqrt(
            sin(radians(blat-alat)/2)^2 
                + (
                    (sin(radians(blng-alng)/2)^2) *
                    cos(radians(alat)) *
                    cos(radians(blat))
                )
          )
        ) * cast('7926.3352' as float) * cast('1.609344' as float) ;


    RETURN v_distance;
END 
$BODY$
language plpgsql VOLATILE SECURITY DEFINER;
alter function geodistance(alat float, alng float, blat float, blng float)
owner to postgres;

2

这是另一个转换为Ruby代码的代码:

include Math
#Note: from/to = [lat, long]

def get_distance_in_km(from, to)
  radians = lambda { |deg| deg * Math.PI / 180 }
  radius = 6371 # Radius of the earth in kilometer
  dLat = radians[to[0]-from[0]]
  dLon = radians[to[1]-from[1]]

  cosines_product = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(radians[from[0]]) * Math.cos(radians[to[1]]) * Math.sin(dLon/2) * Math.sin(dLon/2)

  c = 2 * Math.atan2(Math.sqrt(cosines_product), Math.sqrt(1-cosines_product)) 
  return radius * c # Distance in kilometer
end

1

这里有一个很好的示例,可以使用PHP http://www.geodatasource.com/developers/php计算距离:

 function distance($lat1, $lon1, $lat2, $lon2, $unit) {

     $theta = $lon1 - $lon2;
     $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +  cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
     $dist = acos($dist);
     $dist = rad2deg($dist);
     $miles = $dist * 60 * 1.1515;
     $unit = strtoupper($unit);

     if ($unit == "K") {
         return ($miles * 1.609344);
     } else if ($unit == "N") {
          return ($miles * 0.8684);
     } else {
          return $miles;
     }
 }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.