Calculer la distance entre 2 coordonnées GPS


Réponses:


406

Calculez la distance entre deux coordonnées par latitude et longitude , y compris une implémentation Javascript.

Les emplacements ouest et sud sont négatifs. N'oubliez pas que les minutes et les secondes sont sur 60, donc S31 30 'est de -31,50 degrés.

N'oubliez pas de convertir les degrés en radians . De nombreuses langues ont cette fonction. Ou son calcul de simple: 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;
}

Voici quelques exemples d'utilisation:

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
Dans le cas où il est pas évident, la méthode toRad () est une personnalisation au numéro prototype tel que: Number.prototype.toRad = function() { return this * (Math.PI / 180); }; . Ou, comme indiqué ci-dessous, vous pouvez remplacer (Math.PI/2)par 0,0174532925199433 (... quelle que soit la précision que vous jugez nécessaire) pour des performances accrues.
Vinney Kelly

44
Si quelqu'un, en particulier ceux d'entre vous qui ne recherchent pas de commentaires de fin de ligne, regarde cette formule et recherche une unité de distance, l'unité est le km. :)
Dylan Knowles

1
@VinneyKelly Petite faute de frappe mais remplacer (Math.PI / 180) pas (Math.PI / 2), merci pour l'aide de tout le monde
Patrick Murphy

1
@ChristianKRider Regardez la première ligne. Réfléchissez à ce Rque signifie normalement en mathématiques, puis recherchez les quantités pertinentes liées à la Terre pour voir si les chiffres correspondent.
Fund Monica's Lawsuit

3
Pour les unités impériales (miles), vous pouvez changer earthRadiusKmpour être var earthRadiusMiles = 3959;fyi.
chapeljuice

59

Recherchez haversine avec Google; voici ma solution:

#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
Vous pouvez remplacer (M_PI / 180.0) par 0.0174532925199433 pour de meilleures performances.
Hlung

3
En termes de performances: on pourrait calculer sin (dlat / 2.0) une seule fois, le stocker dans la variable a1, et au lieu de pow (, 2) il est BEAUCOUP mieux d'utiliser a1 * a1. De même pour l'autre pow (, 2).
pms

71
Ouais, ou utilisez simplement un compilateur post-60.
droite le

17
Il n'est pas nécessaire "d'optimiser" (M_PI / 180.0) à une constante que personne ne comprend sans contexte. Le compilateur calcule ces termes fixes pour vous!
Patrick Cornelissen

2
@ TõnuSamuel Merci beaucoup pour votre commentaire. J'apprécie vraiment cela. Il est logique que le compilateur avec l'optimisation activée (-O) puisse pré-calculer les opérations des constantes, ce qui rend inutile l'effondrement manuel. Je le testerai quand j'aurai le temps.
Hlung

44

Version C # de 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;
}

Voici un violon .NET de cela , afin que vous puissiez le tester avec votre propre Lat / Longs.


1
J'ai également ajouté un violon .NET checky pour que les gens puissent facilement tester cela.
Pure.Krome

7
le .Net Framework a une méthode intégrée GeoCoordinate.GetDistanceTo. L'assembly System.Device doit être référencé. Article MSDN msdn.microsoft.com/en-us/library/…
fnx

27

Version Java de Haversine Algorithm basée sur la réponse de Roman Makarov à ce fil

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 assurez-vous de l'utiliser correctement et de ne pas échanger les emplacements lat / log lorsque vous les passez à une méthode.
Paulo Miguel Almeida

1
J'ai obtenu une réponse raisonnablement proche en utilisant cette formule. J'ai basé la précision en utilisant ce site Web: movable-type.co.uk/scripts/latlong.html qui m'a donné 0.07149km alors que votre formule m'a donné 0.07156qui est une précision d'environ 99%
Janac Meena

24

C'est très facile à faire avec le type géographique dans 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 est SRID pour le modèle de terre élipsoïdale WGS84


19

Voici une fonction Haversine en Python que j'utilise:

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

Cela dépend de la précision dont vous avez besoin, si vous avez besoin d'une précision précise, il est préférable de regarder un algorithme qui utilise un ellipsoïde plutôt qu'une sphère, comme l'algorithme de Vincenty, qui est précis au mm. http://en.wikipedia.org/wiki/Vincenty%27s_algorithm


11

Le voici en C # (lat et long en radians):

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

Si votre lat et long sont en degrés, divisez par 180 / PI pour convertir en radians.


1
C'est le calcul de la "loi sphérique des cosinus" qui est la méthode de calcul la moins précise et la plus sujette aux erreurs pour une grande distance de cercle.
John Machin

11

J'avais besoin de calculer beaucoup de distances entre les points pour mon projet, alors j'ai continué et j'ai essayé d'optimiser le code, j'ai trouvé ici. En moyenne, dans différents navigateurs, ma nouvelle implémentation s'exécute 2 fois plus vite que la réponse la plus votée.

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
}

Vous pouvez jouer avec mon jsPerf et voir les résultats ici .

Récemment, j'ai dû faire la même chose en python, voici donc une implémentation de 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))

Et pour être complet: Haversine sur wiki.


11

Version PHP:

(Supprimez tout deg2rad()si vos coordonnées sont déjà en radians.)

$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
Veuillez changer lat1 et lat2 en $ lat1 nad $ lat2.
au lieu de

7

Une fonction T-SQL, que j'utilise pour sélectionner des enregistrements par distance pour un centre

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

C'est le calcul de la "loi sphérique des cosinus" qui est la méthode de calcul la moins précise et la plus sujette aux erreurs pour une grande distance de cercle.
John Machin

5

Si vous avez besoin de quelque chose de plus précis, jetez un œil à cela .

Les formules de Vincenty sont deux méthodes itératives connexes utilisées en géodésie pour calculer la distance entre deux points à la surface d'un sphéroïde, développées par Thaddeus Vincenty (1975a) .Elles sont basées sur l'hypothèse que la figure de la Terre est un sphéroïde oblat, et donc sont plus précises que des méthodes telles que la distance du grand cercle qui supposent une Terre sphérique.

La première méthode (directe) calcule l'emplacement d'un point qui est une distance et un azimut (direction) donnés d'un autre point. La deuxième méthode (inverse) calcule la distance géographique et l'azimut entre deux points donnés. Ils ont été largement utilisés en géodésie car ils sont précis à 0,5 mm près (0,020 ″) sur l'ellipsoïde terrestre.


5

I. Concernant la méthode "chapelure"

  1. Le rayon de la Terre est différent sur différents lat. Cela doit être pris en considération dans l'algorithme Haversine.
  2. Envisagez un changement de relèvement, qui transforme les lignes droites en arcs (qui sont plus longs)
  3. La prise en compte du changement de vitesse transformera les arcs en spirales (qui sont plus longues ou plus courtes que les arches)
  4. Le changement d'altitude transformera les spirales plates en spirales 3D (qui sont encore plus longues). Ceci est très important pour les zones vallonnées.

Voir ci-dessous la fonction en C qui prend en compte # 1 et # 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. Il existe un moyen plus simple qui donne de très bons résultats.

Par vitesse moyenne.

Trip_distance = Trip_average_speed * Trip_time

Puisque la vitesse GPS est détectée par effet Doppler et n'est pas directement liée à [Lon, Lat], elle peut au moins être considérée comme secondaire (sauvegarde ou correction) sinon comme méthode principale de calcul de la distance.


4

Si vous utilisez .NET, ne faites pas revivre la roue. Voir System.Device.Location . Nous remercions fnx dans les commentaires d' une autre réponse .

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

Ce code Lua est adapté de choses trouvées sur Wikipedia et dans l' outil GPSbabel de Robert Lipe :

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
Je pense que votre fonction GetDistance renvoie une valeur en mètres
Przemek

Est-ce correct? GetDirection () n'utilise pas 'dlat'.
gubby

3

Ceci est une version de "Henry Vilinskiy" adaptée pour MySQL et Kilomètres:

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;

MySQLa déclaréSomething is wrong in your syntax near '' on line 8 // declare distance float;
Legionar

Il s'agit du calcul de la "loi sphérique des cosinus" qui est la méthode de calcul la moins précise et la plus sujette aux erreurs d'une distance de grand cercle
John Machin

3

voici l'implémentation Swift de la réponse

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

j'ai pris la meilleure réponse et l'ai utilisée dans un programme 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
}

j'ai curry la fonction afin de pouvoir produire facilement des fonctions qui ont l'un des deux emplacements fixes et qui ne nécessitent qu'une paire de lat / lon pour produire la distance.


2

Je suppose que vous le souhaitez le long de la courbure de la terre. Vos deux points et le centre de la terre sont sur un plan. Le centre de la terre est le centre d'un cercle sur ce plan et les deux points sont (à peu près) sur le périmètre de ce cercle. À partir de là, vous pouvez calculer la distance en découvrant quel est l'angle d'un point à l'autre.

Si les points ne sont pas aux mêmes hauteurs, ou si vous devez tenir compte du fait que la terre n'est pas une sphère parfaite, cela devient un peu plus difficile.


2

J'ai récemment dû faire la même chose. J'ai trouvé ce site Web très utile pour expliquer le trig sphérique avec des exemples faciles à suivre.


2

vous pouvez trouver une implémentation de ceci (avec quelques bonnes explications) en F # sur fssnip

voici les parties importantes:


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

J'avais besoin de l'implémenter dans PowerShell, j'espère que cela peut aider quelqu'un d'autre. Quelques notes sur cette méthode

  1. Ne divisez aucune des lignes ou le calcul sera erroné
  2. Pour calculer en KM, supprimez le * 1000 dans le calcul de $ distance
  3. Modifier $ earthsRadius = 3963.19059 et supprimer * 1000 dans le calcul de $ distance pour calculer la distance en miles
  4. J'utilise Haversine, comme d'autres articles l'ont souligné, les formules de Vincenty sont beaucoup plus précises

    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

Version 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

// Peut-être une erreur de frappe?
Nous avons une variable inutilisée dlon dans GetDirection,
je suppose

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

devrait être

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

1
Ce n'est pas une réponse, c'est au mieux un commentaire.
Kevin

1

Voici mon implémentation dans 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

Version Dart

Algorithme 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

Je pense qu'une version de l'algorithme en R manque toujours:

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

Voici une variante de 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
    }
}

Pourquoi avez-vous utilisé le rayon équatorial au lieu du rayon moyen de la Terre?
user13044086

@ user13044086 Bonne question. C'est parce que je l'ai dérivé de la version Java de Paulo Miguel Almeida. Il semble que la version C # utilise également cette distance. D'autres versions ont ici 6371, mais alors vous devez vous rendre compte que tous ces algorithmes peuvent ne pas gérer parfaitement la forme du géoïde de la Terre. N'hésitez pas à modifier cela et à utiliser 6371. Si vous me dites que cela conduit à des valeurs plus précises, je changerai ma réponse.
Csaba Toth

1
6371.008 est couramment utilisé car il minimise l'erreur relative de la formule comme expliqué dans les notes sur la page movable-type.co.uk/scripts/latlong.html#ellipsoid
user13044086

C'est suffisant! Je modifierai ma réponse demain
Csaba Toth

@ user13044086 Merci pour le lien, j'ai édité ma réponse il y a quelque temps sur cette base
Csaba Toth
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.