Обчисліть відстань між 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

Шукайте Haversine за допомогою 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 для кращої продуктивності.
Hlung

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

71
Так, або просто використовувати компілятор після 60-х.
праворуч

17
Не потрібно "оптимізувати" (M_PI / 180.0) до постійної величини, яку ніхто не розуміє без контексту. Компілятор обчислює ці фіксовані умови для вас!
Патрік Корнеліссен

2
@ TõnuSamuel Дуже дякую за ваш коментар. Я дійсно ціную це. Має сенс, що компілятор із включеною оптимізацією (-O) може попередньо обчислювати операції констант, роблячи ручне згортання марним. Я випробую це, коли встигну.
Hlung

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 алгоритму Хаверсіна на основі відповіді Романа Макарова на цю тему

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 переконайтеся, що ви правильно його використовуєте та не обмінюєте місцями lat / log при передачі їх будь-якому методу.
Пауло Мігель Альмейда

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

Якщо ваш Lat і long знаходяться в градусах, то розділіть на 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))

І заради повноти: Гаверсіне на вікі.


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. Зміна висоти перетворить плоскі спіралі на 3D-спіралі (які знову довші). Це дуже важливо для горбистих районів.

Нижче див. Функцію на З, яка враховує №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 повертає значення в метрах
Пшемек

Це правильно? GetDirection () не використовує "dlat".
gubby

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
}

Я викрив цю функцію, щоб можна було легко виробляти функції, які мають одне з двох розташувань, зафіксованим і для отримання відстані потрібна лише пара lat / lon.


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 при обчисленні $ відстані, щоб визначити відстань у милях
  4. Я використовую Гаверсіна, як і інші пости вказували, що формули Вінсенті набагато точніші

    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

Ось моя реалізація в Elixir

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

Версія Dart

Алгоритм Гаверсина.

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

Ось варіант Котліна:

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. Якщо ви скажете мені, що призводить до більш точних значень, я зміню свою відповідь.
Csaba Toth

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.