¿Cuál es la forma más rápida de seleccionar el lugar geográfico más cercano de la base de datos mysql?

Tengo una tabla en una base de datos MySQL que contiene coordenadas geográficas y otra información sobre lugares. Cada fila de la tabla representa un lugar geográfico y tiene coordenadas como: Latitud = 45.05235 y Longitud = 8.02354 que es un lugar en algún lugar de Europa.

Dado en algunas coordenadas geográficas de entrada (mismo formato), necesito seleccionar de esa tabla el lugar más cercano, o los lugares más cercanos dentro de un radio.

Ya estoy usando índices, sin embargo quiero acelerar el proceso ya que estas funciones se usan muchas veces.

Podría ayudar si pudiera seleccionar el lugar o los lugares más cercanos dentro de un radio determinado directamente con una consulta. Cualquier otra solución también es muy bienvenida, por supuesto.

Hice una función que obtiene el lugar más cercano (que funciona pero es lento):

<?php
//Function for getting nearest destinations:
function nearest_destination($lat1,$lon1,$radius,$type,$maxdistance){
  //Determine geo bounds:
  $lonlow = $lon1 - rad2deg($maxdistance/6371);
  $lonhigh = $lon1 + rad2deg($maxdistance/6371);
  $latlow = $lat1 - rad2deg($maxdistance/6371);
  $lathigh = $lat1 + rad2deg($maxdistance/6371);
  
  //Database details and connect to database
  include(realpath($_SERVER["DOCUMENT_ROOT"]).'/connect_to_db.php'); 
  //Set initial counters to zero
  $ii=0;
  $i=0;
  
  while($row = mysql_fetch_array($result, MYSQL_ASSOC)){
    $shortnamelist[$ii]=$row['shortname'];
    $fullnamelist[$ii]=$row['fullname'];
    $latitudelist[$ii]=$row['latitude'];
    $longitudelist[$ii]=$row['longitude'];
    $lon2=$row['longitude'];
    $lat2=$row['latitude'];
    
    //Calculate the distance:
    $delta_lon = $lon2 - $lon1;
    $earth_radius = "6371"; # in km
    $distance  = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($delta_lon)) ;
    $distance  = acos($distance);
    $distance  = $earth_radius*$distance;
    $distance  = round($distance, 4);
    $distancelist[$ii] = $distance;
    $ii=$ii+1;
  }
  
  //Select position of nearest, and select the destination
  if(isset($distancelist)){
    $minkey=array_keys($distancelist, min($distancelist));
    $minkey=$minkey[0];
    
    $fullname=$fullnamelist[$minkey];
    $shortname=$shortnamelist[$minkey];
    $latitude=$latitudelist[$minkey];
    $longitude=$longitudelist[$minkey];
    
    // remove the big arrays to conserve memory:
    unset($fullnamelist);
    unset($latitudelist);
    unset($longitudelist);
    unset($distancelist);
    unset($shortnamelist);
  }
  
  if(isset($destinid)=='TRUE'){
    $nearest_destination = array("shortname" => $shortname, "fullname" => $fullname, "latitude" => $latitude, "longitude" => $longitude, "distancelist" => $distancelisting);}
  else $nearest_destination = 0;
  mysql_close ();
  return $nearest_destination;
}
?>

Esta es la función que selecciona los lugares más cercanos dentro de un radio determinado (funciona pero lento):

<?php
//Function for getting nearest destinations:
function nearest_destination($lat1,$lon1,$radius,$type,$maxdistance){
  //Determine geo bounds:
  $lonlow = $lon1 - rad2deg($maxdistance/6371);
  $lonhigh = $lon1 + rad2deg($maxdistance/6371);
  $latlow = $lat1 - rad2deg($maxdistance/6371);
  $lathigh = $lat1 + rad2deg($maxdistance/6371);
  
  // Convert from string to number:
  $lon1=floatval($lon1);
  $lat1=floatval($lat1);
  
  //Database details and connect to database
  include(realpath($_SERVER["DOCUMENT_ROOT"]).'/connect_to_database.php'); //Get DB login details
  
  //Select data from destinations table:
  $sql="SELECT shortname, fullname, latitude, longitude FROM destinations WHERE type='$type' AND longitude > $lonlow AND longitude < $lonhigh AND latitude > $latlow AND latitude < $lathigh";
  $result=mysql_query($sql);
  
  //Set initial counter to zero
  $i=0;
  
  while($row = mysql_fetch_array($result, MYSQL_ASSOC)){
    $lon2=$row['longitude'];
    $lat2=$row['latitude'];
    $lon2=floatval($lon2);
    $lat2=floatval($lat2);
    
    //Calculate the distance:
    $delta_lon = $lon2 - $lon1;
    $earth_radius = "6371"; # in km
    $distance  = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($delta_lon)) ;
    $distance  = acos($distance);
    $distance  = $earth_radius*$distance;
    $distance  = round($distance, 4);
    
    //If distance is smaller than the radius the destination is saved in the array:
    if($distance<$radius){
      $fullname[$i]=$row['fullname'];
      $shortname[$i]=$row['shortname'];
      $latitude[$i]=$row['latitude'];
      $longitude[$i]=$row['longitude'];
      $distancelisting[$i] = $distance;
      $i=$i+1;
    }
  }
  
  if(isset($destinid)=='TRUE'){
    $nearest_destination = array("shortname" => $shortname, "fullname" => $fullname, "latitude" => $latitude, "longitude" => $longitude, "distancelist" => $distancelisting);
  }else $nearest_destination = 0;
  mysql_close ();
  return $nearest_destination;
}
?>

preguntado el 03 de mayo de 12 a las 14:05

Si planea trabajar con una gran cantidad de datos geoespaciales, podría valer la pena echarle un buen vistazo al combo postgresql / postgis... -

2 Respuestas

El uso del soporte mysql gis mejorará sus velocidades, ya que fue creado para esto. Si está constantemente leyendo y comparando distancias, valdrá la pena usar postgis, que es una base de datos geoespacial de soporte completo. Le permitirá indexar sus puntos para consultas de distancia eficientes. MySQL proporciona soporte limitado y se basa en GEOS http://trac.osgeo.org/geos/

http://forge.mysql.com/wiki/GIS_Functions

http://postgis.refractions.net/

El enlace más relevante para esto fue publicado por un comentario de Anigel que da una respuesta exacta a su pregunta. La forma más rápida de encontrar la distancia entre dos puntos de latitud y longitud

contestado el 23 de mayo de 17 a las 13:05

¿Crees que este es el método más rápido cuando usas mysql? - BastiaanWW

Siéntase libre de modificar según sea necesario:

<?php
$center_lat = $_GET["lat"];
$center_lng = $_GET["lng"];
$radius = $_GET["radius"];
$unit = $_GET["unit"];
$unitConst = $unit == "mi" ? 3959 : 6371;
$sql = sprintf("SELECT Address, City, State, Country, PostalCode, PhoneNumber, Lat, Lng, ($unitConst * acos(cos(radians('%s')) * cos(radians(Lat)) * cos(radians(Lng) - radians('%s')) + sin(radians('%s')) * sin(radians(Lat)))) AS Distance FROM destinations WHERE (Lat != '0' AND Lng !=0) HAVING distance < '%s' ORDER BY distance", mysql_real_escape_string($center_lat), mysql_real_escape_string($center_lng), mysql_real_escape_string($center_lat), mysql_real_escape_string($radius));
?>

contestado el 03 de mayo de 12 a las 14:05

Considere cambiar a PDO de PHP o algún equivalente, ya que mysql_* se encuentra en las etapas iniciales de una desaprobación suave. - maiorano84

¿Crees que este es el método más rápido cuando usas mysql? - BastiaanWW

Hago. En lugar de confiar en PHP para calcular todo lo que necesita, está enviando todo a la vez en una sola consulta a la base de datos que arroja los resultados que necesita. - maiorano84

Por favor, deja de escribir código nuevo con el código antiguo. mysql_* funciones Ya no se mantienen y la comunidad ha comenzado la proceso de desaprobación. En su lugar, debe aprender acerca de las declaraciones preparadas y usar cualquiera PDO or MySQLi. Si te importa aprender, aquí hay un tutorial bastante bueno relacionado con PDO. - Fantasma de Madara

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.