Elisa, miembro de nuestro equipo, nos hizo el favor de escribir este escrito porque conoce perfectamente el tema.
Solución:
No hace mucho escribí un ejemplo de la fórmula haversine y lo publiqué en mi sitio web:
/**
* Calculates the great-circle distance between two points, with
* the Haversine formula.
* @param float $latitudeFrom Latitude of start point in [deg decimal]
* @param float $longitudeFrom Longitude of start point in [deg decimal]
* @param float $latitudeTo Latitude of target point in [deg decimal]
* @param float $longitudeTo Longitude of target point in [deg decimal]
* @param float $earthRadius Mean earth radius in [m]
* @return float Distance between points in [m] (same as earthRadius)
*/
function haversineGreatCircleDistance(
$latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371000)
// convert from degrees to radians
$latFrom = deg2rad($latitudeFrom);
$lonFrom = deg2rad($longitudeFrom);
$latTo = deg2rad($latitudeTo);
$lonTo = deg2rad($longitudeTo);
$latDelta = $latTo - $latFrom;
$lonDelta = $lonTo - $lonFrom;
$angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) +
cos($latFrom) * cos($latTo) * pow(sin($lonDelta / 2), 2)));
return $angle * $earthRadius;
➽ Tenga en cuenta que recupera la distancia en la misma unidad a medida que pasa con el parámetro $earthRadius
. El valor predeterminado es 6371000 metros, por lo que el resultado estará en [m] también. Para obtener el resultado en millas, por ejemplo, podría pasar 3959 millas como $earthRadius
y el resultado estaría en [mi]. En mi opinión, es un buen hábito ceñirse a las unidades SI, si no hay una razón particular para hacerlo de otra manera.
Editar:
Como TreyA señaló correctamente, la fórmula de Haversine tiene debilidades con los puntos antípodas debido a errores de redondeo (aunque es estable para distancias pequeñas). Para evitarlos, puede usar la fórmula Vincenty en su lugar.
/**
* Calculates the great-circle distance between two points, with
* the Vincenty formula.
* @param float $latitudeFrom Latitude of start point in [deg decimal]
* @param float $longitudeFrom Longitude of start point in [deg decimal]
* @param float $latitudeTo Latitude of target point in [deg decimal]
* @param float $longitudeTo Longitude of target point in [deg decimal]
* @param float $earthRadius Mean earth radius in [m]
* @return float Distance between points in [m] (same as earthRadius)
*/
public static function vincentyGreatCircleDistance(
$latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371000)
// convert from degrees to radians
$latFrom = deg2rad($latitudeFrom);
$lonFrom = deg2rad($longitudeFrom);
$latTo = deg2rad($latitudeTo);
$lonTo = deg2rad($longitudeTo);
$lonDelta = $lonTo - $lonFrom;
$a = pow(cos($latTo) * sin($lonDelta), 2) +
pow(cos($latFrom) * sin($latTo) - sin($latFrom) * cos($latTo) * cos($lonDelta), 2);
$b = sin($latFrom) * sin($latTo) + cos($latFrom) * cos($latTo) * cos($lonDelta);
$angle = atan2(sqrt($a), $b);
return $angle * $earthRadius;
Encontré este código que me da resultados confiables.
function distance($lat1, $lon1, $lat2, $lon2, $unit)
$theta = $lon1 - $lon2;
$dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
$dist = acos($dist);
$dist = rad2deg($dist);
$miles = $dist * 60 * 1.1515;
$unit = strtoupper($unit);
if ($unit == "K")
return ($miles * 1.609344);
else if ($unit == "N")
return ($miles * 0.8684);
else
return $miles;
resultados:
echo distance(32.9697, -96.80322, 29.46786, -98.53506, "M") . " Miles
";
echo distance(32.9697, -96.80322, 29.46786, -98.53506, "K") . " Kilometers
";
echo distance(32.9697, -96.80322, 29.46786, -98.53506, "N") . " Nautical Miles
";
Es solo una adición a las respuestas de @martinstoeckli y @Janith Chinthana. Para aquellos que tienen curiosidad sobre qué algoritmo es más rápido, escribí la prueba de rendimiento. El mejor resultado de rendimiento muestra una función optimizada de codexworld.com:
/**
* Optimized algorithm from http://www.codexworld.com
*
* @param float $latitudeFrom
* @param float $longitudeFrom
* @param float $latitudeTo
* @param float $longitudeTo
*
* @return float [km]
*/
function codexworldGetDistanceOpt($latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo)
$rad = M_PI / 180;
//Calculate distance from latitude and longitude
$theta = $longitudeFrom - $longitudeTo;
$dist = sin($latitudeFrom * $rad)
* sin($latitudeTo * $rad) + cos($latitudeFrom * $rad)
* cos($latitudeTo * $rad) * cos($theta * $rad);
return acos($dist) / $rad * 60 * 1.853;
Aquí están los resultados de la prueba:
Test name Repeats Result Performance
codexworld-opt 10000 0.084952 sec +0.00%
codexworld 10000 0.104127 sec -22.57%
custom 10000 0.107419 sec -26.45%
custom2 10000 0.111576 sec -31.34%
custom1 10000 0.136691 sec -60.90%
vincenty 10000 0.165881 sec -95.26%
Si posees algún titubeo y forma de enriquecer nuestro artículo eres capaz de ejecutar una acotación y con placer lo observaremos.