Saltar al contenido

Midiendo la distancia entre dos coordenadas en PHP

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.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *