Рассчитать расстояние между 2 GPS-координатами


Ответы:


406

Рассчитайте расстояние между двумя координатами по широте и долготе , включая реализацию 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;
}

Вот несколько примеров использования:

distanceInKmBetweenEarthCoordinates(0,0,0,0)  // Distance between same 
                                              // points should be 0
0

distanceInKmBetweenEarthCoordinates(51.5, 0, 38.8, -77.1) // From London
                                                          // to Arlington
5918.185064088764

17
В случае , если это не очевидно, метод toRad () является настройка на номер прототипа , такие как: Number.prototype.toRad = function() { return this * (Math.PI / 180); }; . Или, как указано ниже, вы можете заменить (Math.PI/2)на 0.0174532925199433 (... независимо от точности, которую вы считаете необходимой) для повышения производительности.
Винни Келли

44
Если кто-то, особенно те из вас, кто не ищет комментариев в конце строки, смотрит на эту формулу и ищет единицу расстояния, то это единица измерения км. :)
Дилан Ноулз

1
@VinneyKelly Маленькая опечатка, но заменить (Math.PI / 180) нет (Math.PI / 2), спасибо за помощь всем
Патрик Мерфи

1
@ChristianKRider Посмотрите на первую строку. Подумайте о том, что Rобычно означает в математике, а затем найдите соответствующие, связанные с Землей величины, чтобы увидеть, совпадают ли числа.
Фонд Моника иск

3
Для имперских единиц (миль) вы могли бы измениться, earthRadiusKmк var earthRadiusMiles = 3959;вашему сведению.
chapeljuice

59

Ищите Гаверсин с Google; вот мое решение:

#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;
}

3
Вы можете заменить (M_PI / 180.0) на 0.0174532925199433 для лучшей производительности.
Хлунг

3
С точки зрения производительности: можно рассчитать sin (dlat / 2.0) только один раз, сохранить его в переменной a1, и вместо pow (, 2) гораздо лучше использовать a1 * a1. То же самое для другой пау (2).
pms

71
Да, или просто используйте компилятор пост-60-х годов.
правостороннее

17
Нет необходимости «оптимизировать» (M_PI / 180.0) константу, которую никто не понимает без контекста. Компилятор рассчитывает эти фиксированные условия для вас!
Патрик Корнелиссен

2
@ TõnuSamuel Большое спасибо за ваш комментарий. Я очень ценю это. Имеет смысл, что компилятор с включенной оптимизацией (-O) может предварительно вычислять операции констант, делая ручное свертывание бесполезным. Я проверю это, когда у меня будет время.
Хлунг

44

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

Вот это .NET Fiddle , так что вы можете проверить это с помощью своих собственных Lat / Longs.


1
Я также добавил проверочную скрипту .NET, чтобы люди могли легко проверить это.
Pure.Krome

7
.Net Framework имеет встроенный метод GeoCoordinate.GetDistanceTo. Сборка System.Device должна быть указана. Статья MSDN msdn.microsoft.com/en-us/library/…
fnx

27

Java-версия алгоритма Haversine, основанная на ответе Романа Макарова этой теме

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

}

@Radu убедитесь, что вы используете его правильно и не меняете латы / логи при передаче их любому методу.
Пауло Мигель Алмейда

1
Я получил достаточно близкий ответ, используя эту формулу. Я основал точность, используя этот веб-сайт: movable-type.co.uk/scripts/latlong.html, который дал мне 0.07149км, тогда как ваша формула дала мне 0.07156точность, которая составляет около 99%
Janac Meena

24

Это очень легко сделать с типом географии в 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

4326 - SRID для модели элипсоидальной Земли WGS84


19

Вот функция Haversine в Python, которую я использую:

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}

16

Это зависит от того, насколько точным он вам нужен, если вам нужна точная точность, лучше всего взглянуть на алгоритм с использованием эллипсоида, а не сферы, такой как алгоритм Винсенти, который точен до мм. http://en.wikipedia.org/wiki/Vincenty%27s_algorithm


11

Вот это в C # (широта и долгота в радианах):

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

Если ваши широта и долгота указаны в градусах, то разделите на 180 / PI, чтобы преобразовать в радианы.


1
Это вычисление «сферического закона косинусов», которое является наименее точным и наиболее подверженным ошибкам методом расчета расстояния большого круга.
Джон Мачин

11

Мне нужно было рассчитать много расстояний между точками для моего проекта, поэтому я пошел дальше и попытался оптимизировать код, который я нашел здесь. В среднем в разных браузерах моя новая реализация работает в 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
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))

И ради полноты: Haversine на вики.


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;

1
Пожалуйста, измените lat1 и lat2 на $ lat1 nad $ lat2.
вместо

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

Это вычисление «сферического закона косинусов», которое является наименее точным и наиболее подверженным ошибкам методом расчета расстояния большого круга.
Джон Мачин

5

Если вам нужно что-то более точное, взгляните на это .

Формулы Винсенти - это два связанных итеративных метода, используемых в геодезии для расчета расстояния между двумя точками на поверхности сфероида, разработанные Таддеем Винсенти (1975a). Они основаны на предположении, что фигура Земли представляет собой сжатый сфероид, и, следовательно, являются более точными, чем методы, такие как расстояние по большому кругу, которые предполагают сферическую Землю.

Первый (прямой) метод вычисляет местоположение точки, которая является заданным расстоянием и азимутом (направлением) от другой точки. Второй (обратный) метод вычисляет географическое расстояние и азимут между двумя заданными точками. Они широко используются в геодезии, потому что они с точностью до 0,5 мм (0,020 ″) на земном эллипсоиде.


5

I. Относительно метода "сухари"

  1. Радиус Земли различен на разных широтах. Это необходимо учитывать в алгоритме Хаверсайна.
  2. Рассмотрим изменение подшипника, при котором прямые линии превращаются в арки (которые длиннее)
  3. Принятие во внимание изменения скорости превратит арки в спирали (которые длиннее или короче арок)
  4. Изменение высоты превратит плоские спирали в трехмерные (которые снова длиннее). Это очень важно для холмистой местности.

Ниже смотрите функцию в 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;
}

II. Есть более простой способ, который дает довольно хорошие результаты.

По средней скорости.

Trip_distance = Trip_average_speed * Trip_time

Поскольку скорость GPS определяется эффектом Доплера и не имеет прямого отношения к [Lon, Lat], ее можно по меньшей мере рассматривать как вторичную (резервное копирование или коррекция), если не как метод расчета основного расстояния.


4

Если вы используете .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);

3

Этот код Lua адаптирован из материалов, найденных в Википедии и в инструменте Роберта Липа GPSbabel :

local EARTH_RAD = 6378137.0 
  -- earth's radius in meters (official geoid datum, not 20,000km / pi)

local radmiles = EARTH_RAD*100.0/2.54/12.0/5280.0;
  -- earth's radius in miles

local multipliers = {
  radians = 1, miles = radmiles, mi = radmiles, feet = radmiles * 5280,
  meters = EARTH_RAD, m = EARTH_RAD, km = EARTH_RAD / 1000, 
  degrees = 360 / (2 * math.pi), min = 60 * 360 / (2 * math.pi)
}

function gcdist(pt1, pt2, units) -- return distance in radians or given units
  --- this formula works best for points close together or antipodal
  --- rounding error strikes when distance is one-quarter Earth's circumference
  --- (ref: wikipedia Great-circle distance)
  if not pt1.radians then pt1 = rad(pt1) end
  if not pt2.radians then pt2 = rad(pt2) end
  local sdlat = sin((pt1.lat - pt2.lat) / 2.0);
  local sdlon = sin((pt1.lon - pt2.lon) / 2.0);
  local res = sqrt(sdlat * sdlat + cos(pt1.lat) * cos(pt2.lat) * sdlon * sdlon);
  res = res > 1 and 1 or res < -1 and -1 or res
  res = 2 * asin(res);
  if units then return res * assert(multipliers[units])
  else return res
  end
end

3
    private double deg2rad(double deg)
    {
        return (deg * Math.PI / 180.0);
    }

    private double rad2deg(double rad)
    {
        return (rad / Math.PI * 180.0);
    }

    private double GetDistance(double lat1, double lon1, double lat2, double lon2)
    {
        //code for Distance in Kilo Meter
        double theta = lon1 - lon2;
        double dist = Math.Sin(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) + Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(deg2rad(theta));
        dist = Math.Abs(Math.Round(rad2deg(Math.Acos(dist)) * 60 * 1.1515 * 1.609344 * 1000, 0));
        return (dist);
    }

    private double GetDirection(double lat1, double lon1, double lat2, double lon2)
    {
        //code for Direction in Degrees
        double dlat = deg2rad(lat1) - deg2rad(lat2);
        double dlon = deg2rad(lon1) - deg2rad(lon2);
        double y = Math.Sin(dlon) * Math.Cos(lat2);
        double x = Math.Cos(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) - Math.Sin(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(dlon);
        double direct = Math.Round(rad2deg(Math.Atan2(y, x)), 0);
        if (direct < 0)
            direct = direct + 360;
        return (direct);
    }

    private double GetSpeed(double lat1, double lon1, double lat2, double lon2, DateTime CurTime, DateTime PrevTime)
    {
        //code for speed in Kilo Meter/Hour
        TimeSpan TimeDifference = CurTime.Subtract(PrevTime);
        double TimeDifferenceInSeconds = Math.Round(TimeDifference.TotalSeconds, 0);
        double theta = lon1 - lon2;
        double dist = Math.Sin(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) + Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(deg2rad(theta));
        dist = rad2deg(Math.Acos(dist)) * 60 * 1.1515 * 1.609344;
        double Speed = Math.Abs(Math.Round((dist / Math.Abs(TimeDifferenceInSeconds)) * 60 * 60, 0));
        return (Speed);
    }

    private double GetDuration(DateTime CurTime, DateTime PrevTime)
    {
        //code for speed in Kilo Meter/Hour
        TimeSpan TimeDifference = CurTime.Subtract(PrevTime);
        double TimeDifferenceInSeconds = Math.Abs(Math.Round(TimeDifference.TotalSeconds, 0));
        return (TimeDifferenceInSeconds);
    }

1
Я думаю, что ваша функция GetDistance возвращает значение в метрах
Przemek

Это правильно? GetDirection () не использует «dlat».
грязный

3

Это версия от "Генри Вилинского", адаптированная для 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;

MySQLсказалSomething is wrong in your syntax near '' on line 8 // declare distance float;
Легионар

Это вычисление «сферического закона косинусов», которое является наименее точным и наиболее подверженным ошибкам методом вычисления расстояния большого круга
Джон Мачин

3

вот реализация 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
}

3

я взял лучший ответ и использовал его в программе Scala

import java.lang.Math.{atan2, cos, sin, sqrt}

def latLonDistance(lat1: Double, lon1: Double)(lat2: Double, lon2: Double): Double = {
    val earthRadiusKm = 6371
    val dLat = (lat2 - lat1).toRadians
    val dLon = (lon2 - lon1).toRadians
    val latRad1 = lat1.toRadians
    val latRad2 = lat2.toRadians

    val a = sin(dLat / 2) * sin(dLat / 2) + sin(dLon / 2) * sin(dLon / 2) * cos(latRad1) * cos(latRad2)
    val c = 2 * atan2(sqrt(a), sqrt(1 - a))
    earthRadiusKm * c
}

я каррировал функцию, чтобы иметь возможность легко создавать функции, которые имеют одно из двух фиксированных местоположений и требуют только пару широта / долгота для получения расстояния.


2

Я предполагаю, что вы хотите это по искривлению земли. Ваши две точки и центр Земли находятся на плоскости. Центр Земли является центром круга на этой плоскости, и две точки (примерно) находятся по периметру этого круга. Исходя из этого, вы можете рассчитать расстояние, выяснив, каков угол от одной точки к другой.

Если точки не одинаковы по высоте, или если вам нужно принять во внимание, что Земля не является идеальной сферой, становится немного сложнее.


2

Я недавно должен был сделать то же самое. Я нашел этот сайт очень полезным для объяснения сферической триггера с примерами, за которыми было легко следить.


2

Вы можете найти реализацию этого (с некоторыми хорошими объяснениями) в F # на fssnip

Вот важные части:


let GreatCircleDistance<[<Measure>] 'u> (R : float<'u>) (p1 : Location) (p2 : Location) =
    let degToRad (x : float<deg>) = System.Math.PI * x / 180.0<deg/rad>

    let sq x = x * x
    // take the sin of the half and square the result
    let sinSqHf (a : float<rad>) = (System.Math.Sin >> sq) (a / 2.0<rad>)
    let cos (a : float<deg>) = System.Math.Cos (degToRad a / 1.0<rad>)

    let dLat = (p2.Latitude - p1.Latitude) |> degToRad
    let dLon = (p2.Longitude - p1.Longitude) |> degToRad

    let a = sinSqHf dLat + cos p1.Latitude * cos p2.Latitude * sinSqHf dLon
    let c = 2.0 * System.Math.Atan2(System.Math.Sqrt(a), System.Math.Sqrt(1.0-a))

    R * c

2

Мне нужно было реализовать это в PowerShell, надеюсь, это поможет кому-то еще. Некоторые заметки об этом методе

  1. Не разбивайте ни одну из строк, иначе расчет будет неверным
  2. Для расчета в КМ снимите * 1000 в расчете на расстояние $
  3. Измените $ earthsRadius = 3963.19059 и удалите * 1000 в расчете $ distance для расчета расстояния в милях
  4. Я использую Haversine, поскольку другие должности указали, что формулы Винсенти намного более точны

    Function MetresDistanceBetweenTwoGPSCoordinates($latitude1, $longitude1, $latitude2, $longitude2)  
    {  
      $Rad = ([math]::PI / 180);  
    
      $earthsRadius = 6378.1370 # Earth's Radius in KM  
      $dLat = ($latitude2 - $latitude1) * $Rad  
      $dLon = ($longitude2 - $longitude1) * $Rad  
      $latitude1 = $latitude1 * $Rad  
      $latitude2 = $latitude2 * $Rad  
    
      $a = [math]::Sin($dLat / 2) * [math]::Sin($dLat / 2) + [math]::Sin($dLon / 2) * [math]::Sin($dLon / 2) * [math]::Cos($latitude1) * [math]::Cos($latitude2)  
      $c = 2 * [math]::ATan2([math]::Sqrt($a), [math]::Sqrt(1-$a))  
    
      $distance = [math]::Round($earthsRadius * $c * 1000, 0) #Multiple by 1000 to get metres  
    
      Return $distance  
    }
    

2

Scala версия

  def deg2rad(deg: Double) = deg * Math.PI / 180.0

  def rad2deg(rad: Double) = rad / Math.PI * 180.0

  def getDistanceMeters(lat1: Double, lon1: Double, lat2: Double, lon2: Double) = {
    val theta = lon1 - lon2
    val dist = Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) + Math.cos(deg2rad(lat1)) *
      Math.cos(deg2rad(lat2)) * Math.cos(deg2rad(theta))
    Math.abs(
      Math.round(
        rad2deg(Math.acos(dist)) * 60 * 1.1515 * 1.609344 * 1000)
    )
  }

1

// Может быть, ошибка опечатки? Я полагаю, у
нас есть неиспользуемая переменная dlon в GetDirection

double y = Math.Sin(dlon) * Math.Cos(lat2);
// cannot use degrees in Cos ?

должно быть

double y = Math.Sin(dlon) * Math.Cos(dlat);

1
Это не ответ, это в лучшем случае комментарий.
Кевин

1

Вот моя реализация в эликсире

defmodule Geo do
  @earth_radius_km 6371
  @earth_radius_sm 3958.748
  @earth_radius_nm 3440.065
  @feet_per_sm 5280

  @d2r :math.pi / 180

  def deg_to_rad(deg), do: deg * @d2r

  def great_circle_distance(p1, p2, :km), do: haversine(p1, p2) * @earth_radius_km
  def great_circle_distance(p1, p2, :sm), do: haversine(p1, p2) * @earth_radius_sm
  def great_circle_distance(p1, p2, :nm), do: haversine(p1, p2) * @earth_radius_nm
  def great_circle_distance(p1, p2, :m), do: great_circle_distance(p1, p2, :km) * 1000
  def great_circle_distance(p1, p2, :ft), do: great_circle_distance(p1, p2, :sm) * @feet_per_sm

  @doc """
  Calculate the [Haversine](https://en.wikipedia.org/wiki/Haversine_formula)
  distance between two coordinates. Result is in radians. This result can be
  multiplied by the sphere's radius in any unit to get the distance in that unit.
  For example, multiple the result of this function by the Earth's radius in
  kilometres and you get the distance between the two given points in kilometres.
  """
  def haversine({lat1, lon1}, {lat2, lon2}) do
    dlat = deg_to_rad(lat2 - lat1)
    dlon = deg_to_rad(lon2 - lon1)

    radlat1 = deg_to_rad(lat1)
    radlat2 = deg_to_rad(lat2)

    a = :math.pow(:math.sin(dlat / 2), 2) +
        :math.pow(:math.sin(dlon / 2), 2) *
        :math.cos(radlat1) * :math.cos(radlat2)

    2 * :math.atan2(:math.sqrt(a), :math.sqrt(1 - a))
  end
end

1

Дартс версия

Алгоритм Haversine.

import 'dart:math';

class GeoUtils {

  static double _degreesToRadians(degrees) {
    return degrees * pi / 180;
  }

  static double 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 = sin(dLat/2) * sin(dLat/2) +
        sin(dLon/2) * sin(dLon/2) * cos(lat1) * cos(lat2);
    var c = 2 * atan2(sqrt(a), sqrt(1-a));
    return earthRadiusKm * c;
  }
}

0

Я думаю, что версия алгоритма в R все еще отсутствует:

gpsdistance<-function(lat1,lon1,lat2,lon2){

# internal function to change deg to rad

degreesToRadians<- function (degrees) {
return (degrees * pi / 180)
}

R<-6371e3  #radius of Earth in meters

phi1<-degreesToRadians(lat1) # latitude 1
phi2<-degreesToRadians(lat2) # latitude 2
lambda1<-degreesToRadians(lon1) # longitude 1
lambda2<-degreesToRadians(lon2) # longitude 2

delta_phi<-phi1-phi2 # latitude-distance
delta_lambda<-lambda1-lambda2 # longitude-distance

a<-sin(delta_phi/2)*sin(delta_phi/2)+
cos(phi1)*cos(phi2)*sin(delta_lambda/2)*
sin(delta_lambda/2)

cc<-2*atan2(sqrt(a),sqrt(1-a))

distance<- R * cc

return(distance)  # in meters
}

0

Вот вариант Kotlin:

import kotlin.math.*

class HaversineAlgorithm {

    companion object {
        private const val MEAN_EARTH_RADIUS = 6371.0
        private const val D2R = Math.PI / 180.0
    }

    private fun haversineInKm(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
        val lonDiff = (lon2 - lon1) * D2R
        val latDiff = (lat2 - lat1) * D2R
        val latSin = sin(latDiff / 2.0)
        val lonSin = sin(lonDiff / 2.0)
        val a = latSin * latSin + (cos(lat1 * D2R) * cos(lat2 * D2R) * lonSin * lonSin)
        val c = 2.0 * atan2(sqrt(a), sqrt(1.0 - a))
        return EQATORIAL_EARTH_RADIUS * c
    }
}

Почему вы использовали экваториальный радиус вместо среднего радиуса Земли?
user13044086

@ user13044086 Хороший вопрос. Это потому, что я получил это из Java-версии Пауло Мигеля Алмейды. Похоже, версия C # также использует это расстояние. Другие версии здесь имеют 6371, но тогда вы должны понимать, что все эти алгоритмы могут не совсем точно обрабатывать форму геоида Земли. Не стесняйтесь изменить это и использовать 6371. Если вы скажете мне, что это приводит к более точным значениям, я изменю свой ответ.
Чаба Тот

1
6371.008 обычно используется, потому что минимизирует относительную ошибку формулы, как описано в примечаниях на странице movable-type.co.uk/scripts/latlong.html#ellipsoid
user13044086

Справедливо! Я отредактирую свой ответ завтра
Csaba Toth

@ user13044086 Спасибо за ссылку, я отредактировал свой ответ некоторое время назад, основываясь на этом
Csaba Toth
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.