计算2个GPS坐标之间的距离

nic*_*tro 344 math geometry geography geolocation latitude-longitude

如何计算两个GPS坐标之间的距离(使用纬度和经度)?

cle*_*tus 378

按纬度和经度计算两个坐标之间的距离,包括Javascript实现.

西部南部地区都是负面的.记住分数和秒数都在60以上,所以S31 30'是-31.50度.

不要忘记将度数转换为弧度.许多语言都有这个功能.或者它的简单计算:radians = degrees * PI / 180.

function degreesToRadians(degrees) {
  return degrees * Math.PI / 180;
}

function distanceInKmBetweenEarthCoordinates(lat1, lon1, lat2, lon2) {
  var earthRadiusKm = 6371;

  var dLat = degreesToRadians(lat2-lat1);
  var dLon = degreesToRadians(lon2-lon1);

  lat1 = degreesToRadians(lat1);
  lat2 = degreesToRadians(lat2);

  var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
          Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2); 
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
  return earthRadiusKm * c;
}
Run Code Online (Sandbox Code Playgroud)

以下是一些用法示例:

distanceInKmBetweenCoordinates(0,0,0,0)//相同点之间的距离应为0 0 distanceInKmBetweenCoordinates(51.5,0,38.8,-77.1)//从伦敦到阿灵顿5918.185064088764

  • 如果有人,特别是那些不寻找行尾注释的人,正盯着这个公式并寻找一个距离单位,则单位为km.:) (43认同)
  • 如果不明显,则toRad()方法是对**Number**原型的自定义,例如:`Number.prototype.toRad = function(){return this*(Math.PI/180); }; `.或者,如下所示,您可以将"(Math.PI/2)"替换为0.0174532925199433(......您认为必要的精度)以提高性能. (17认同)
  • 对于英制单位(英里),你可以将`earthRadiusKm`改为`var earthRadiusMiles = 3959;`,fyi. (3认同)

小智 59

寻找与谷歌的bersine; 这是我的解决方案:

#include <math.h>
#include "haversine.h"

#define d2r (M_PI / 180.0)

//calculate haversine distance for linear distance
double haversine_km(double lat1, double long1, double lat2, double long2)
{
    double dlong = (long2 - long1) * d2r;
    double dlat = (lat2 - lat1) * d2r;
    double a = pow(sin(dlat/2.0), 2) + cos(lat1*d2r) * cos(lat2*d2r) * pow(sin(dlong/2.0), 2);
    double c = 2 * atan2(sqrt(a), sqrt(1-a));
    double d = 6367 * c;

    return d;
}

double haversine_mi(double lat1, double long1, double lat2, double long2)
{
    double dlong = (long2 - long1) * d2r;
    double dlat = (lat2 - lat1) * d2r;
    double a = pow(sin(dlat/2.0), 2) + cos(lat1*d2r) * cos(lat2*d2r) * pow(sin(dlong/2.0), 2);
    double c = 2 * atan2(sqrt(a), sqrt(1-a));
    double d = 3956 * c; 

    return d;
}
Run Code Online (Sandbox Code Playgroud)

  • 是的,或者只是使用60后的编译器. (68认同)
  • 没有必要"优化"(M_PI/180.0)到没有上下文就没有人理解的常数.编译器为您计算这些固定的术语! (15认同)
  • 您可以使用0.0174532925199433替换(M_PI/180.0)以获得更好的性能. (3认同)
  • 在性能方面:人们可以只计算一次sin(dlat/2.0),将其存储在变量a1中,而不是pow(,2),使用a1*a1会更好.对于其他战俘(,2)也是如此. (3认同)
  • @TõnuSamuel非常感谢你的评论.对此,我真的非常感激.有意义的是,启用了优化的编译器(-O)可以预先计算常量的操作,使手动折叠无效.我有空的时候会测试一下. (2认同)

小智 42

C#版本的Haversine

double _eQuatorialEarthRadius = 6378.1370D;
double _d2r = (Math.PI / 180D);

private int HaversineInM(double lat1, double long1, double lat2, double long2)
{
    return (int)(1000D * HaversineInKM(lat1, long1, lat2, long2));
}

private double HaversineInKM(double lat1, double long1, double lat2, double long2)
{
    double dlong = (long2 - long1) * _d2r;
    double dlat = (lat2 - lat1) * _d2r;
    double a = Math.Pow(Math.Sin(dlat / 2D), 2D) + Math.Cos(lat1 * _d2r) * Math.Cos(lat2 * _d2r) * Math.Pow(Math.Sin(dlong / 2D), 2D);
    double c = 2D * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1D - a));
    double d = _eQuatorialEarthRadius * c;

    return d;
}
Run Code Online (Sandbox Code Playgroud)

这是一个.NET小提琴,所以你可以用你自己的Lat/Longs来测试它.

  • .Net Framework有一个构建方法GeoCoordinate.GetDistanceTo.必须引用程序集System.Device.MSDN文章https://msdn.microsoft.com/en-us/library/system.device.location.geocoordinate.getdistanceto%28v=vs.110%29.aspx (6认同)

Pau*_*ida 25

Java版本的Haversine算法基于Roman Makarov对此主题的回复

public class HaversineAlgorithm {

    static final double _eQuatorialEarthRadius = 6378.1370D;
    static final double _d2r = (Math.PI / 180D);

    public static int HaversineInM(double lat1, double long1, double lat2, double long2) {
        return (int) (1000D * HaversineInKM(lat1, long1, lat2, long2));
    }

    public static double HaversineInKM(double lat1, double long1, double lat2, double long2) {
        double dlong = (long2 - long1) * _d2r;
        double dlat = (lat2 - lat1) * _d2r;
        double a = Math.pow(Math.sin(dlat / 2D), 2D) + Math.cos(lat1 * _d2r) * Math.cos(lat2 * _d2r)
                * Math.pow(Math.sin(dlong / 2D), 2D);
        double c = 2D * Math.atan2(Math.sqrt(a), Math.sqrt(1D - a));
        double d = _eQuatorialEarthRadius * c;

        return d;
    }

}
Run Code Online (Sandbox Code Playgroud)


小智 23

这在SQL Server 2008中非常容易使用地理类型.

SELECT geography::Point(lat1, lon1, 4326).STDistance(geography::Point(lat2, lon2, 4326))
-- computes distance in meters using eliptical model, accurate to the mm
Run Code Online (Sandbox Code Playgroud)

4326是WGS84 elipsoidal Earth模型的SRID


sea*_*anb 15

这取决于你需要它的准确度,如果你需要精确定位,最好用一个椭球来看一个算法,而不是像Vincenty算法这样精确到mm的球体.http://en.wikipedia.org/wiki/Vincenty%27s_algorithm

  • @NicoHaase 公平电话会议,如果可能有点即兴的话,已经是 12 年前的事了,当时的情况略有不同。 (3认同)
  • 请将所有信息放入您的答案中,而不是链接到外部资源 (2认同)

小智 15

这是我使用的Python中的Haversine函数:

from math import pi,sqrt,sin,cos,atan2

def haversine(pos1, pos2):
    lat1 = float(pos1['lat'])
    long1 = float(pos1['long'])
    lat2 = float(pos2['lat'])
    long2 = float(pos2['long'])

    degree_to_rad = float(pi / 180.0)

    d_lat = (lat2 - lat1) * degree_to_rad
    d_long = (long2 - long1) * degree_to_rad

    a = pow(sin(d_lat / 2), 2) + cos(lat1 * degree_to_rad) * cos(lat2 * degree_to_rad) * pow(sin(d_long / 2), 2)
    c = 2 * atan2(sqrt(a), sqrt(1 - a))
    km = 6367 * c
    mi = 3956 * c

    return {"km":km, "miles":mi}
Run Code Online (Sandbox Code Playgroud)

  • 在PyPI中有一个包:https://pypi.python.org/pypi/haversine (3认同)

Mik*_*ain 11

这里是C#(lat和long弧度):

double CalculateGreatCircleDistance(double lat1, double long1, double lat2, double long2, double radius)
{
    return radius * Math.Acos(
        Math.Sin(lat1) * Math.Sin(lat2)
        + Math.Cos(lat1) * Math.Cos(lat2) * Math.Cos(long2 - long1));
}
Run Code Online (Sandbox Code Playgroud)

如果lat和long以度为单位,则除以180/PI转换为弧度.


qua*_*ape 11

PHP版本:

(deg2rad()如果您的坐标已经是弧度,请删除所有.)

$R = 6371; // km
$dLat = deg2rad($lat2-$lat1);
$dLon = deg2rad($lon2-$lon1);
$lat1 = deg2rad($lat1);
$lat2 = deg2rad($lat2);

$a = sin($dLat/2) * sin($dLat/2) +
     sin($dLon/2) * sin($dLon/2) * cos($lat1) * cos($lat2); 

$c = 2 * atan2(sqrt($a), sqrt(1-$a)); 
$d = $R * $c;
Run Code Online (Sandbox Code Playgroud)


Sal*_*ali 10

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

您可以使用我的jsPerf并在此处查看结果.

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

from math import cos, asin, sqrt
def distance(lat1, lon1, lat2, lon2):
    p = 0.017453292519943295
    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))
Run Code Online (Sandbox Code Playgroud)

并且为了完整起见:在wiki上的Haversine.


小智 7

一个T-SQL函数,用于按中心距离选择记录

Create Function  [dbo].[DistanceInMiles] 
 (  @fromLatitude float ,
    @fromLongitude float ,
    @toLatitude float, 
    @toLongitude float
  )
   returns float
AS 
BEGIN
declare @distance float

select @distance = cast((3963 * ACOS(round(COS(RADIANS(90-@fromLatitude))*COS(RADIANS(90-@toLatitude))+ 
SIN(RADIANS(90-@fromLatitude))*SIN(RADIANS(90-@toLatitude))*COS(RADIANS(@fromLongitude-@toLongitude)),15)) 
)as float) 
  return  round(@distance,1)
END
Run Code Online (Sandbox Code Playgroud)


小智 7

一、关于“面包屑”方法

  1. 地球半径在不同纬度上是不同的。在Haversine算法中必须考虑到这一点。
  2. 考虑轴承变化,将直线变成拱形(更长)
  3. 考虑速度变化会将拱形变成螺旋形(比拱形更长或更短)
  4. 高度变化会将扁平螺旋变成 3D 螺旋(再次变长)。这对于丘陵地区非常重要。

下面请看 C 中考虑 #1 和 #2 的函数:

double   calcDistanceByHaversine(double rLat1, double rLon1, double rHeading1,
       double rLat2, double rLon2, double rHeading2){
  double rDLatRad = 0.0;
  double rDLonRad = 0.0;
  double rLat1Rad = 0.0;
  double rLat2Rad = 0.0;
  double a = 0.0;
  double c = 0.0;
  double rResult = 0.0;
  double rEarthRadius = 0.0;
  double rDHeading = 0.0;
  double rDHeadingRad = 0.0;

  if ((rLat1 < -90.0) || (rLat1 > 90.0) || (rLat2 < -90.0) || (rLat2 > 90.0)
              || (rLon1 < -180.0) || (rLon1 > 180.0) || (rLon2 < -180.0)
              || (rLon2 > 180.0)) {
        return -1;
  };

  rDLatRad = (rLat2 - rLat1) * DEGREE_TO_RADIANS;
  rDLonRad = (rLon2 - rLon1) * DEGREE_TO_RADIANS;
  rLat1Rad = rLat1 * DEGREE_TO_RADIANS;
  rLat2Rad = rLat2 * DEGREE_TO_RADIANS;

  a = sin(rDLatRad / 2) * sin(rDLatRad / 2) + sin(rDLonRad / 2) * sin(
              rDLonRad / 2) * cos(rLat1Rad) * cos(rLat2Rad);

  if (a == 0.0) {
        return 0.0;
  }

  c = 2 * atan2(sqrt(a), sqrt(1 - a));
  rEarthRadius = 6378.1370 - (21.3847 * 90.0 / ((fabs(rLat1) + fabs(rLat2))
              / 2.0));
  rResult = rEarthRadius * c;

  // Chord to Arc Correction based on Heading changes. Important for routes with many turns and U-turns

  if ((rHeading1 >= 0.0) && (rHeading1 < 360.0) && (rHeading2 >= 0.0)
              && (rHeading2 < 360.0)) {
        rDHeading = fabs(rHeading1 - rHeading2);
        if (rDHeading > 180.0) {
              rDHeading -= 180.0;
        }
        rDHeadingRad = rDHeading * DEGREE_TO_RADIANS;
        if (rDHeading > 5.0) {
              rResult = rResult * (rDHeadingRad / (2.0 * sin(rDHeadingRad / 2)));
        } else {
              rResult = rResult / cos(rDHeadingRad);
        }
  }
  return rResult;
}
Run Code Online (Sandbox Code Playgroud)

二、有一种更简单的方法可以得到很好的结果。

按平均速度。

Trip_distance = Trip_average_speed * Trip_time

由于 GPS 速度是通过多普勒效应检测的,并且与 [Lon,Lat] 没有直接关系,因此如果不是主要的距离计算方法,它至少可以被视为次要的(备份或校正)。


Tim*_*dge 6

如果您使用的是 .NET,请不要重新使用轮子。请参阅System.Device.Location。在另一个答案的评论中归功于 fnx 。

using System.Device.Location;

double lat1 = 45.421527862548828D;
double long1 = -75.697189331054688D;
double lat2 = 53.64135D;
double long2 = -113.59273D;

GeoCoordinate geo1 = new GeoCoordinate(lat1, long1);
GeoCoordinate geo2 = new GeoCoordinate(lat2, long2);

double distance = geo1.GetDistanceTo(geo2);
Run Code Online (Sandbox Code Playgroud)


Sai*_* Li 6

这是答案中的 Swift 实现

func degreesToRadians(degrees: Double) -> Double {
    return degrees * Double.pi / 180
}

func distanceInKmBetweenEarthCoordinates(lat1: Double, lon1: Double, lat2: Double, lon2: Double) -> Double {

    let earthRadiusKm: Double = 6371

    let dLat = degreesToRadians(degrees: lat2 - lat1)
    let dLon = degreesToRadians(degrees: lon2 - lon1)

    let lat1 = degreesToRadians(degrees: lat1)
    let lat2 = degreesToRadians(degrees: lat2)

    let a = sin(dLat/2) * sin(dLat/2) +
    sin(dLon/2) * sin(dLon/2) * cos(lat1) * cos(lat2)
    let c = 2 * atan2(sqrt(a), sqrt(1 - a))
    return earthRadiusKm * c
}
Run Code Online (Sandbox Code Playgroud)


小智 5

如果你需要更准确的东西,那么看看这个.

Vincenty的公式是用于大地测量的两种相关迭代方法,用于计算由Thaddeus Vincenty(1975a)开发的球体表面上两点之间的距离.它们是基于地球的形状是扁球体的假设,因此比假定球形地球的大圆距离等方法更精确.

第一个(直接)方法计算距离另一个点的给定距离和方位角(方向)的点的位置.第二种(逆)方法计算两个给定点之间的地理距离和方位角.它们已被广泛用于大地测量,因为它们精确到地球椭球上0.5毫米(0.020英寸)以内.


Max*_*axs 5

这是来自“Henry Vilinskiy”的版本,适用于 MySQL 和公里:

CREATE FUNCTION `CalculateDistanceInKm`(
  fromLatitude float,
  fromLongitude float,
  toLatitude float, 
  toLongitude float
) RETURNS float
BEGIN
  declare distance float;

  select 
    6367 * ACOS(
            round(
              COS(RADIANS(90-fromLatitude)) *
                COS(RADIANS(90-toLatitude)) +
                SIN(RADIANS(90-fromLatitude)) *
                SIN(RADIANS(90-toLatitude)) *
                COS(RADIANS(fromLongitude-toLongitude))
              ,15)
            )
    into distance;

  return  round(distance,3);
END;
Run Code Online (Sandbox Code Playgroud)