Faculté des Sciences, Département de Physique, I.P.N.A.S. (www.ipnas.org)

Cette faculté publie un document vraiment intéressant sur le calcul de la distance entre deux points dont voici l'essentiel :

Calcul de la distance entre deux points

La distance mesurée le long d'un arc de grand cercle entre deux points dont on connaît les coordonnées { lat1,lon1} et {lat2,lon2} est donnée par :

d=acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos(lon1-lon2))

Une formule, mathématiquement équivalente, mais moins sujette aux erreurs d'arrondis pour les courtes distances est :

d=2*asin(sqrt((sin((lat1-lat2)/2))^2 + cos(lat1)*cos(lat2)*(sin((lon1-lon2)/2))^2))

N.B.: Dans un tableur classique, la différence entre les deux formules de calcul est inférieure à 10-9 km, même pour de petites distances.

La valeur de d est obtenue dans une unité correspondant au rayon de la sphère terrestre (R = 6366 km). Si l'arc cosinus rend une valeur en radiant (ce qui le cas dans la plupart des tableurs), il suffit de multiplier le résultat par R pour obtenir la valeur de d en km.

Implémentation en PHP de la distance à vol d'oiseau séparant 2 points ou coordonnées GPS

<?php 
/*
Format: DDD M/D/Y H:M:S 1.00 hrs Datum[106]: WGS 84
ID Date Time Latitude Longitude Altitude km
L ACTIVE LOG
T 06/09/2003 09:52:47 50.19473 6.83212 454.8 0.00000
T 06/09/2003 09:53:58 50.1948 6.83244 452.4 0.02405
T 06/09/2003 09:54:02 50.19381 6.83386 452.9 0.14935
*/
 
$r = 6366;
 
$lat1 = 50.19473;
$lon1 = 6.83212;
 
$lat2 = 50.1948;
$lon2 = 6.83244;
 
echo "Entrées :
lat1:$lat1
lon1:$lon1
 
lat2:$lat2
lon2:$lon2
";
 
/**
 * Conversion des entrées en ° vers des Radians
 */
$lat1 = deg2rad($lat1);
$lon1 = deg2rad($lon1);
$lat2 = deg2rad($lat2);
$lon2 = deg2rad($lon2);
 
/**
 * Formule simple
 * d=acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos(lon1-lon2))
 */
$ds= acos(sin($lat1)*sin($lat2)+cos($lat1)*cos($lat2)*cos($lon1-$lon2));
//$ds= acos(sin(deg2rad($lat1))*sin(deg2rad($lat2))+cos(deg2rad($lat1))*cos(deg2rad($lat2))*cos(deg2rad($lon1-$lon2)));
$dsr = $ds * $r;
 
/**
 * Formule précise
 * d=2*asin(
 				sqrt(
 
					(sin((lat1-lat2)/2))^2 + cos(lat1)*cos(lat2)*(sin((lon1-lon2)/2))^2)
 
				)
 */
$dp= 2 * asin(
				sqrt(
						pow( sin(($lat1-$lat2)/2) , 2) + cos($lat1)*cos($lat2)* pow( sin(($lon1-$lon2)/2) , 2)
					)	
 
				);
$dpr = $dp * $r;
 
echo "
Sorties :
ds:$ds
dsr:$dsr(km)
 
dp:$dp
dpr:$dpr(km)
";
 
//Entrées :
//lat1:50.19473
//lon1:6.83212
//
//lat2:50.1948
//lon2:6.83244
//
//Sorties :
//ds:3.7784067903909E-6
//dsr:0.024053337627628(km)
//
//dp:3.7784109830292E-6
//dpr:0.024053364317964(km)
?>
  • deg2rad : Convertit un nombre de degrés en radians

Implémentation en JAVA de la distance à vol d'oiseau séparant 2 points ou coordonnées GPS

package com.placeoweb;
 
import java.text.DecimalFormat;
 
public class Wgs84 {
 
	/**
	 * @param args
	 */
	public static void main(String[] args) {
 
		DecimalFormat decimalFormat = (DecimalFormat)DecimalFormat.getInstance();
		decimalFormat.applyPattern("###0.##########");
 
		// r
		int r = 6366;
 
		double lat1 = 50.19473;
		double lon1 =  6.83212;        
 
		double lat2 = 50.1948;
		double lon2 =  6.83244;        
 
		long tempsT1;
		long tempsT2;
 
		System.out.println(
				"Point A (lat/lon) : " + decimalFormat.format(lat1) + " " + decimalFormat.format(lon1) + "\n" +
				"Point B (lat/lon) : " + decimalFormat.format(lat2) + " " + decimalFormat.format(lon2) 
							);
 
		/**
		 * Conversion des entrées en ° vers en radian
		 */
		lat1 = Math.toRadians(lat1);
		lon1 = Math.toRadians(lon1);
		lat2 = Math.toRadians(lat2);
		lon2 = Math.toRadians(lon2);
 
		tempsT1 = System.nanoTime();
		double distance = distanceVolOiseauEntre2PointsAvecPrécision(lat1, lon1, lat2, lon2);
		tempsT2 = System.nanoTime();
		System.out.println("Temps (AvecPrécision) : " + String.format("%10d",(tempsT2 - tempsT1)) + " ns");
		double distanceEnKm = distance * r ;
 
		tempsT1 = System.nanoTime();
		double distanceEloigné = distanceVolOiseauEntre2PointsSansPrécision(lat1, lon1, lat2, lon2);
		tempsT2 = System.nanoTime();
		System.out.println("Temps (SansPrécision) : " + String.format("%10d",(tempsT2 - tempsT1)) + " ns");		
		double distanceEloignéEnKm = distanceEloigné * r;
 
		System.out.println(
				"Distance      : " + decimalFormat.format(distance) + " (" + distance + ")\n" +
				"Distance (km) calcul précis pour courtes distances         : " + decimalFormat.format(distanceEnKm) + " km (" + distanceEnKm + ")\n" +
				"Distance (km) calcul non précis pour distances non courtes : " + decimalFormat.format(distanceEloignéEnKm) + " km (" + distanceEloignéEnKm + ")\n" +
				""
		);
 
//		Point A (lat/lon) : 50,19473 6,83212
//		Point B (lat/lon) : 50,1948 6,83244
//		Temps (AvecPrécision) :      34297 ns
//		Temps (SansPrécision) :      24435 ns
//		Distance      : 0,0000037784 (3.7784109830459747E-6)
//		Distance (km) calcul précis pour courtes distances         : 0,0240533643 km (0.024053364318070675)
//		Distance (km) calcul non précis pour distances non courtes : 0,0240533376 km (0.024053337627628308)
 
	}
 
	/**
	 * Distance entre 2 points GPS
	 * http://dotclear.placeoweb.com/post/Formule-de-calcul-entre-2-points-wgs84-pour-calculer-la-distance-qui-separe-ces-deux-points
	 * 
	 * La distance mesurée le long d'un arc de grand cercle entre deux points dont on connaît les coordonnées {lat1,lon1} et {lat2,lon2} est donnée par :
	 * d=acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos(lon1-lon2))
	 * Le tout * 6366 pour l'avoir en km
	 * 
	 * @param lat1
	 * @param lon1
	 * @param lat2
	 * @param lon2
	 * @return
	 */	
	public static double distanceVolOiseauEntre2PointsSansPrécision(double lat1, double lon1, double lat2, double lon2) {
 
		// d=acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos(lon1-lon2))
 
		return
 
			Math.acos(
 
					Math.sin(lat1)*Math.sin(lat2)+Math.cos(lat1)*Math.cos(lat2)*Math.cos(lon1-lon2)
			);
 
	}
 
	/**
	 * Distance entre 2 points GPS
	 * http://dotclear.placeoweb.com/post/Formule-de-calcul-entre-2-points-wgs84-pour-calculer-la-distance-qui-separe-ces-deux-points
	 * 
	 * La distance mesurée le long d'un arc de grand cercle entre deux points dont on connaît les coordonnées {lat1,lon1} et {lat2,lon2} est donnée par :
	 * La formule, mathématiquement équivalente, mais moins sujette aux erreurs d'arrondis pour les courtes distances est :	 * 
	 * d=2*asin(sqrt((sin((lat1-lat2)/2))^2 + cos(lat1)*cos(lat2)*(sin((lon1- lon2)/2))^2))
	 * Le tout * 6366 pour l'avoir en km
	 * 
	 * @param lat1
	 * @param lon1
	 * @param lat2
	 * @param lon2
	 * @return
	 */
	public static double distanceVolOiseauEntre2PointsAvecPrécision(double lat1, double lon1, double lat2, double lon2) {
 
		// d=2*asin(sqrt((sin((lat1-lat2)/2))^2 + cos(lat1)*cos(lat2)*(sin((lon1- lon2)/2))^2))
 
		return
 
			2 *	Math.asin(
 
					Math.sqrt(
 
							Math.pow((Math.sin((lat1 - lat2) / 2)),2)
 
							+
 
							Math.cos(lat1) * Math.cos(lat2) * 
 
																	(
																			Math.pow(
																					Math.sin(
																					((lon1-lon2)/2)
																			)
																			,2)
																	)							
 
 
							)
 
						);
 
	}
 
}
  • Math.toRadians(double) : Converti un angle mesuré en degrés vers son équivalent approximatif en radians. La conversion des dégrées vers radians est généralement inexacte.

On constate que la méthode précise est 10 fois plus longue et n'apporte pas d'écart si l'on compte en mètres.

Implémentation en JAVASCRIPT (JS) de la distance à vol d'oiseau séparant 2 points ou coordonnées GPS

function deg2rad (angle) {
    // Converts the number in degrees to the radian equivalent  
    // 
    // version: 1109.2015
    // discuss at: http://phpjs.org/functions/deg2rad    // +   original by: Enrique Gonzalez
    // *     example 1: deg2rad(45);
    // *     returns 1: 0.7853981633974483
    return (angle / 180) * Math.PI;
}
 
var r = 6366;
 
var lat1 = 50.19473;
var lon1 = 6.83212;
 
var lat2 = 50.1948;
var lon2 = 6.83244;
 
/**
 * Conversion des entrées en ° vers des Radians
 */
lat1 = deg2rad(lat1);
lon1 = deg2rad(lon1);
lat2 = deg2rad(lat2);
lon2 = deg2rad(lon2);
 
/**
 * Formule simple
 * d=acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos(lon1-lon2))
 */
 
var ds = Math.acos( Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon1-lon2) );
ds = ds * r;
alert("Distance en km : " + ds); // Distance en km : 0.024053337627628308

Implémentation en SQL (MySQL) de la distance à vol d'oiseau séparant 2 ou coordonnées points GPS

Calcul de la Distance entre deux coordonnées GPS avec MySQL

Création de la Fonction MySQL pour calculer la distance en mètres entre 2 points.

La solution idéale est d'utiliser la puissance des fonctions MySQL pour réaliser ce traitement complexe et d'extraire en une seule requête les données souhaitées sans risquer de surcharger le serveur.

-- Déclaration de la fonction
-- SELECT VERSION(); 5.1.37-0.dotdeb.1-log 
-- ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)
DELIMITER |
DROP FUNCTION IF EXISTS get_distance_metres|
CREATE FUNCTION get_distance_metres (lat1 DOUBLE, lng1 DOUBLE, lat2 DOUBLE, lng2 DOUBLE) RETURNS DOUBLE
  DETERMINISTIC 
  NO SQL
BEGIN
    DECLARE rlo1 DOUBLE;
    DECLARE rla1 DOUBLE;
    DECLARE rlo2 DOUBLE;
    DECLARE rla2 DOUBLE;
    DECLARE dlo DOUBLE;
    DECLARE dla DOUBLE;
    DECLARE a DOUBLE;
 
    SET rlo1 = RADIANS(lng1);
    SET rla1 = RADIANS(lat1);
    SET rlo2 = RADIANS(lng2);
    SET rla2 = RADIANS(lat2);
    SET dlo = (rlo2 - rlo1) / 2;
    SET dla = (rla2 - rla1) / 2;
    SET a = SIN(dla) * SIN(dla) + COS(rla1) * COS(rla2) * SIN(dlo) * SIN(dlo);
    RETURN (6378137 * 2 * ATAN2(SQRT(a), SQRT(1 - a)));
END|
DELIMITER ;
 
-- Utilisation de la fonction
-- Recherchons par exemple les 10 restaurants les plus proches de la Tour Eiffel à Paris (latitude : 48.858205, longitude : 2.294359) et dont la distance est inférieure à 1 kilomètre.
SELECT *, get_distance_metres('48.858205', '2.294359', lat, lng) AS proximite 
FROM restaurants 
WHERE proximite < 1000 
ORDER BY proximite ASC 
LIMIT 10

Plusieurs parties sont à voir, dans un premier temps il faut trier et extraire un nombre limité d'enregistrements et MySQL fait cela très bien avec les options SORT BY et LIMIT. Ensuite il faut déterminer la distance entre deux points sur Terre. Mathématiquement il s'agit de l'orthodromie qui est le chemin le plus court entre deux points d'une sphère. Dans un langage simple, nous dirions qu'il faut mesurer la longueur de l'arc de grand cercle passant par deux points.

Afin de calculer la distance entre 2 points il faut créer une fonction MySQL (nous parlons ici de procédure stockée) qui prend en paramètre la latitude et la longitude de 2 points (exprimées en degrés).

  • CREATE FUNCTION Syntax CREATE FUNCTION
  • DETERMINISTIC and NOT DETERMINISTIC , CONTAINS SQL, NO SQL, READS SQL DATA, and MODIFIES SQL DATA dans la section Binary Logging of Stored Programs pour palier à l'erreur ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)

Et sur le même site : PHP Distance en mètre entre deux points avec coordonnées

Et dans le même genre avec Doctrine PHP : The Geographical behavior can be used with any data record for determining the number of miles or kilometers between 2 records.

lion1906.com

Formule permettant de calculer une distance orthodromique en kilomètres entre deux points A et B :

Ortho(A,B)=6371 x acos[cos(LatA) x cos(LatB) x cos(LongB-LongA)+sin(LatA) x sin(LatB)]

Avec 6371 qui correspond au rayon de la Terre en Km.

6371 n'est pas une valeur exacte: la Terre n'est pas une sphère parfaite, mais plutôt une sphéroïde. 6371 correspond au rayon moyen (plus d'infos sur la Terre).

experts-exchange.com

Implémentation Bash + Perl :

#!/bin/sh
perl -MMath::Trig -e '$_*=pi/180 for ($lat1,$lon1,$lat2,$lon2)=@ARGV;print 2*asin(sqrt((sin(($lat1-$lat2)/2))**2 + cos($lat1)*cos($lat2)*(sin(($lon1-$lon2)/2))**2))*180/pi' 0 0 10 10