【问题标题】:Calculate distance between two latitude-longitude points? (Haversine formula)计算两个经纬度点之间的距离? (Haversine 公式)
【发布时间】:2010-09-06 21:00:41
【问题描述】:

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

为了澄清,我想以公里为单位;这些点使用 WGS84 系统,我想了解可用方法的相对准确性。

【问题讨论】:

  • 为了更准确 - 请参阅stackoverflow.com/questions/1420045/…
  • 请注意,您不能在像 WGS 84 这样的旋转椭球体上应用半正弦公式。您只能在具有半径的球体上应用此方法。
  • 这里的大部分答案都是使用简单的球面三角法,因此与 GPS 系统中使用的 WGS84 椭球距离相比,结果相当粗糙。一些答案确实参考了 Vincenty 的椭球公式,但该算法是为 1960 年代的台式计算器设计的,并且存在稳定性和准确性问题;我们现在有更好的硬件和软件。请参阅GeographicLib 了解具有多种语言实现的高质量库。
  • @MikeT - 虽然这里的许多答案似乎在小距离内很有用:如果您从 WGS 84 获取纬度/经度,并应用Haversine 就好像那些 是球体上的点,你没有得到答案,其错误只是由于地球的扁平化因素,所以可能在更准确公式的 1% 以内?需要注意的是,这些距离很短,比如在一个城镇内。
  • 对于这些平台:Mono/.NET 4.5/.NET Core/Windows Phone 8.x/通用 Windows 平台/Xamarin iOS/Xamarin Android 参见stackoverflow.com/a/54296314/2736742

标签: algorithm math maps latitude-longitude haversine


【解决方案1】:

这个link 可能对您有帮助,因为它详细说明了使用Haversine formula 来计算距离。

摘录:

这个脚本 [in Javascript] 计算两点之间的大圆距离 - 也就是说,地球表面的最短距离——使用 “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)
}

【讨论】:

  • 这个计算/方法是否说明地球是一个球体(不是一个完美的球体)?最初的问题要求 WGS84 地球上各点之间的距离。不确定使用完美球体会带来多少误差,但我怀疑它可能会很大,具体取决于地球上点的位置,因此值得牢记区别。
  • Haversine 公式没有说明地球是一个椭球体,因此您会因为这个事实而引入一些错误。它不能保证正确到优于 0.5%。不过,这可能是也可能不是可接受的错误级别。
  • 是否有任何理由使用Math.atan2(Math.sqrt(a), Math.sqrt(1-a)) 而不是Math.asin(Math.sqrt(h)),这将是维基百科文章使用的公式的直接实现?它是否更有效和/或更稳定?
  • @UsmanMutawakil 好吧,您获得的 38 英里是在路上的距离。该算法计算地球表面上的直线距离。谷歌地图有一个距离工具(左下角,“实验室”)可以做同样的事情,用它来比较。
  • @Forte_201092:因为这没有必要——因为(sin(x))² 等于(sin(-x))²
【解决方案2】:

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

最近我需要在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 维基百科。

【讨论】:

  • @AngularM 如果你要走一些路而不是直线,谷歌很可能会计算距离。
  • Google 计算行驶距离,这计算“如乌鸦飞”
  • @Ouadie 会提高速度吗?很可能没有,但对于那些在旧浏览器中复制粘贴的人来说,我最终会得到很多“你的东西不起作用”
  • 好吧,但是// 2 * R; R = 6371 km 代表什么?当前的方法以公里或英里为单位提供答案?需要更好的文档。谢谢
  • @KhalilKhalaf 你是在开玩笑还是想在这里拖钓? km代表公里。你认为 R 代表什么(尤其是当我们谈论一个 shpere 时)?如果您已经看到公里,猜猜答案是什么单位。您在这里寻找什么样的文档:实际上有 4 行。
【解决方案3】:

这是一个 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;
    }

}

【讨论】:

  • 您使用的是赤道半径,但您应该使用平均半径,即 6371 公里
  • 不应该是double dlon = Radians(lon2 - lon1);double dlat = Radians(lat2 - lat1);
  • 我同意克里斯·马里西奇的观点。我使用了原始代码,计算错误。我添加了将增量转换为弧度的调用,它现在可以正常工作。我提交了修改,正在等待同行评审。
  • 我提交了另一个编辑,因为 lat1 & lat2 也需要转换为弧度。我还将赋值公式修改为 a 以匹配此处找到的公式和代码:movable-type.co.uk/scripts/latlong.html
  • RADIUS 的值是否需要像其他答案一样为 6371?
【解决方案4】:

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

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

【讨论】:

  • 如果我们想以米为单位计算两点之间的距离,更准确的方法是什么?使用6371000 作为地球的半径? (平均地球半径为 6371000 米)或将公里转换为米?
  • 如果你想要里程,将结果乘以0.621371
【解决方案5】:

非常感谢这一切。我在我的 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;
}

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

在我传入 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 公式。
  • 使用纽约市的坐标 (40.7127837, -74.0059413) 和洛杉矶的坐标 (34.052234, -118.243685),() 在该总和附近,我得到 3869.75。没有它们,我得到 3935.75,这几乎就是网络搜索出现的结果。
【解决方案6】:

这是一个简单的 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;
}
?>

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

根据您在这个不规则卵形上的位置以及您的点相距多远(它们越接近绝对误差范围越小),不同的方法可能或多或少地精确。您的期望越精确,数学就越复杂。

欲了解更多信息:wikipedia geographic distance

【讨论】:

  • 这很完美!我刚刚添加了 $distance_miles = $km * 0.621371;这就是我所需要的以英里为单位的近似距离!谢谢托尼。
【解决方案7】:

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

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

列出所有 DISTANCE

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/…
【解决方案8】:

在其他答案中, 中的实现缺失。

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

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 formula for ellipsoids 可能是计算距离的最佳方法。因此,在您使用的 geosphere 包中:

distm(p1, p2, fun = distVincentyEllipsoid)

当然你不一定非得使用geosphere包,你也可以用一个函数计算以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)
}

【讨论】:

  • 为了确保我清楚您所说的内容:您在帖子末尾给出的代码:这是文森蒂公式的实现吗?据您所知,它应该给出与在地圈中调用文森蒂相同的答案? [我没有geosphere或其他图书馆;只是在寻找一些代码以包含在跨平台应用程序中。我当然会根据已知的好计算器验证一些测试用例。]
  • @ToolmakerSteve 我答案末尾的函数是Haversine方法的实现
  • 嗨@Jaap 我能问一下公式的计量单位是什么吗?单位是米吗?
  • @Jaap 我喜欢“椭圆体的文森蒂公式”的解释,我测试它非常准确。 @Jackson distm(p1, p2, fun = distVincentyEllipsoid) 以米为单位给出输出,您必须将其除以 1000 才能获得以公里为单位的值。
【解决方案9】:

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

这个来自美国宇航局的人的帖子,是我在讨论选项时发现的最好的帖子

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

例如,如果您只是按半径 100 英里内的距离对行进行排序。 flat earth 公式会比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 英尺),具体取决于在地球的位置上。
【解决方案10】:

可能有一个更简单、更正确的解决方案:赤道处地球周长为 40,000 公里,格林威治(或任何经度)周期约为 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));
};

我同意它应该进行微调,因为我自己说过它是一个椭球体,因此要乘以余弦的半径会有所不同。但它更准确一些。与谷歌地图相比,确实大大减少了误差。

【讨论】:

  • 这个函数返回的距离是公里吗?
  • 是的,因为赤道和经度周期是以公里为单位的。对于英里,只需将 40000 和 37000 除以 1.6。感觉怪怪的,您可以将其转换为 Ris,乘以大约 7 或转换为 parasang,除以 2.2 ;-)
  • 这似乎是这里提供的最佳答案。我希望使用它,但我只是想知道是否有办法验证该算法的正确性。我测试了 f(50,5,58,3)。它给出了 832 公里,而 movable-type.co.uk/scripts/latlong.html 使用“haversine”公式给出了 899 公里。有这么大的区别吗?
  • 另外,我认为上面代码返回的值是m,而不是km。
  • 这个公式中的数字不准确。通过两极的周长是 6356.752 NASA * 2 Pi = 39940.651 公里。不是 37000。因此,正如 Chong 所见,纬度变化的答案很低。将“37000000.0”替换为“39940651.0”。有了这个修正,我的猜测是精确到 100 分之一,距离可达 1 度。 (未验证。)
【解决方案11】:

以上所有答案都假设地球是一个球体。然而,更准确的近似值是扁椭球体。

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

【讨论】:

  • 您可以在公式中添加来源吗?
【解决方案12】:

pip install haversine

Python 实现

原产地是美国本土的中心。

from haversine import haversine, Unit
origin = (39.50, 98.35)
paris = (48.8567, 2.3508)
haversine(origin, paris, unit=Unit.MILES)

要以公里为单位获得答案,只需设置unit=Unit.KILOMETERS(这是默认设置)。

【讨论】:

  • 您正在导入一个完成所有工作的非标准包。我不知道这是否有用。
  • 该包在 PyPI,Python 包索引中,作为 python 3 包以及 numpy 和 scikit-learn。不知道为什么将一个与包裹并列。它们往往非常有用。作为开源,人们还可以检查其中包含的方法。我想很多人会发现这个包很有用,所以尽管投了反对票,我还是会离开这个帖子。干杯。 :)
  • 它看起来很有用,但我想包含确切的 pip 命令来安装这个包。
【解决方案13】:

我不喜欢添加另一个答案,但 Google 地图 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 的计算有多准确甚至使用什么模型的消息(尽管它确实说“球形”而不是“大地水准面”。顺便说一下,“直线”距离显然与距离不同,如果一个在地球表面旅行,这似乎是每个人都在猜测的。

【讨论】:

  • 距离以米为单位。或者可以使用 computeLength()
【解决方案14】:

您可以使用 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。

【讨论】:

    【解决方案15】:

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

    【讨论】:

      【解决方案16】:

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

      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
      

      在小距离内,Keerthana 的算法似乎与 Google 地图的算法一致。谷歌地图似乎没有遵循任何简单的算法,这表明它可能是这里最准确的方法。

      不管怎样,这里是 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));
      }
      

      【讨论】:

        【解决方案17】:

        这里是计算距离的 SQL 实现,以公里为单位,

        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;
        

        关于编程语言实现的更多细节,你可以通过here给出的php脚本来查看

        【讨论】:

          【解决方案18】:

          此脚本 [in 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;
                  }
              }
          

          【讨论】:

            【解决方案19】:

            根据Haversine formula中的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
            }
            

            【讨论】:

              【解决方案20】:

              要计算球体上两点之间的距离,您需要执行Great Circle calculation

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

              您还可以找到MapWindow 一个有用的工具来可视化这些点。此外,作为它的开源,它是如何使用 proj.dll 库的有用指南,它似乎是核心开源投影库。

              【讨论】:

                【解决方案21】:

                这是我在经过一些搜索后通过十进制度计算距离的 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;
                }
                

                【讨论】:

                  【解决方案22】:

                  这是移植到 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);
                  }
                  

                  【讨论】:

                    【解决方案23】:

                    这是 postgres sql 中的一个示例(以公里为单位,对于英里版本,将 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;
                    

                    【讨论】:

                      【解决方案24】:

                      对于那些寻找基于 WGS-84 和 GRS-80 标准的 Excel 公式的人:

                      =ACOS(COS(RADIANS(90-Lat1))*COS(RADIANS(90-Lat2))+SIN(RADIANS(90-Lat1))*SIN(RADIANS(90-Lat2))*COS(RADIANS(Long1-Long2)))*6371
                      

                      Source

                      【讨论】:

                        【解决方案25】:

                        我在 R 中创建了一个自定义函数,以使用 R 基础包中提供的函数计算两个空间点之间的半正弦距离(km)。

                        custom_hav_dist <- function(lat1, lon1, lat2, lon2) {
                        R <- 6371
                        Radian_factor <- 0.0174533
                        lat_1 <- (90-lat1)*Radian_factor
                        lat_2 <- (90-lat2)*Radian_factor
                        diff_long <-(lon1-lon2)*Radian_factor
                        
                        distance_in_km <- 6371*acos((cos(lat_1)*cos(lat_2))+ 
                                         (sin(lat_1)*sin(lat_2)*cos(diff_long)))
                        rm(lat1, lon1, lat2, lon2)
                        return(distance_in_km)
                        }
                        

                        样本输出

                        custom_hav_dist(50.31,19.08,54.14,19.39)
                        [1] 426.3987
                        

                        PS:要以英里为单位计算距离,请将函数 (6371) 中的 R 替换为 3958.756(对于海里,请使用 3440.065)。

                        【讨论】:

                        • 如何计算速度?
                        • 代码是关于计算两个地球静止空间点之间的距离。不明白为什么这里需要速度计算??
                        • 其实,如果给出时间戳,我们可以计算速度,因为距离是使用公式计算的。但是如果有一分钟的间隔时间戳,并且我们想了解每 5 分钟间隔(任何车辆移动)的速度,我想知道该怎么做?
                        • 您可以进一步添加代码来计算速度,但在我的用例中,没有必要因此没有计算速度。很想听听您对此有何看法
                        【解决方案26】:

                        这里有一个很好的例子来用 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;
                             }
                         }
                        

                        【讨论】:

                          【解决方案27】:

                          这是 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 ..) 写错了两次?
                          【解决方案28】:

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

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

                          【讨论】:

                            【解决方案29】:
                            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 的解决方案,对里程也有效。

                            【讨论】:

                              【解决方案30】:

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

                              【讨论】:

                                猜你喜欢
                                • 1970-01-01
                                • 2012-10-13
                                • 2011-04-11
                                • 2018-06-11
                                • 1970-01-01
                                • 1970-01-01
                                • 2012-01-26
                                • 1970-01-01
                                相关资源
                                最近更新 更多