Modificaría la respuesta de gabrielk y la publicación de blog vinculada mediante el uso de índices de bases de datos y minimizando el número de cálculos de distancia reales .
Si conoce las coordenadas del usuario y conoce la distancia máxima (por ejemplo, 10 km), puede dibujar un cuadro delimitador de 20 km por 20 km con la ubicación actual en el medio. Obtenga estas coordenadas limitantes y solo almacene consultas entre estas latitudes y longitudes . Todavía no use las funciones de trigonometría en su consulta de base de datos, ya que esto evitará que se utilicen índices. (Por lo tanto, es posible que obtenga una tienda que esté a 12 km de distancia si está en la esquina noreste de la caja delimitadora, pero la descartamos en el siguiente paso).
Solo calcule la distancia (como vuela el pájaro o con las indicaciones de manejo reales, como prefiera) para las pocas tiendas que se devuelven. Esto mejorará drásticamente el tiempo de procesamiento si tiene una gran cantidad de tiendas.
Para la búsqueda relacionada ( "dar las diez tiendas más cercanas" ) puede hacer una búsqueda similar, pero con una suposición de distancia inicial (por lo que comienza con un área de 10 km por 10 km, y si no tiene suficientes tiendas, la expande a 20 km por 20 km y así sucesivamente). Para esta distancia inicial, suponga que una vez calcule el número de tiendas sobre el área total y úsela. O registre la cantidad de consultas necesarias y adáptese con el tiempo.
Agregué un ejemplo de código completo en la pregunta relacionada de Mike , y aquí hay una extensión que le brinda las ubicaciones X más cercanas (rápida y apenas probada):
class Monkeyman_Geo_ClosestX extends Monkeyman_Geo
{
public static $closestXStartDistanceKm = 10;
public static $closestXMaxDistanceKm = 1000; // Don't search beyond this
public function addAdminPages()
{
parent::addAdminPages();
add_management_page( 'Location closest test', 'Location closest test', 'edit_posts', __FILE__ . 'closesttest', array(&$this, 'doClosestTestPage'));
}
public function doClosestTestPage()
{
if (!array_key_exists('search', $_REQUEST)) {
$default_lat = ini_get('date.default_latitude');
$default_lon = ini_get('date.default_longitude');
echo <<<EOF
<form action="" method="post">
<p>Number of posts: <input size="5" name="post_count" value="10"/></p>
<p>Center latitude: <input size="10" name="center_lat" value="{$default_lat}"/>
<br/>Center longitude: <input size="10" name="center_lon" value="{$default_lon}"/></p>
<p><input type="submit" name="search" value="Search!"/></p>
</form>
EOF;
return;
}
$post_count = intval($_REQUEST['post_count']);
$center_lon = floatval($_REQUEST['center_lon']);
$center_lat = floatval($_REQUEST['center_lat']);
var_dump(self::getClosestXPosts($center_lon, $center_lat, $post_count));
}
/**
* Get the closest X posts to a given location
*
* This might return more than X results, and never more than
* self::$closestXMaxDistanceKm away (to prevent endless searching)
* The results are sorted by distance
*
* The algorithm starts with all locations no further than
* self::$closestXStartDistanceKm, and then grows this area
* (by doubling the distance) until enough matches are found.
*
* The number of expensive calculations should be minimized.
*/
public static function getClosestXPosts($center_lon, $center_lat, $post_count)
{
$search_distance = self::$closestXStartDistanceKm;
$close_posts = array();
while (count($close_posts) < $post_count && $search_distance < self::$closestXMaxDistanceKm) {
list($north_lat, $east_lon, $south_lat, $west_lon) = self::getBoundingBox($center_lat, $center_lon, $search_distance);
$geo_posts = self::getPostsInBoundingBox($north_lat, $east_lon, $south_lat, $west_lon);
foreach ($geo_posts as $geo_post) {
if (array_key_exists($geo_post->post_id, $close_posts)) {
continue;
}
$post_lat = floatval($geo_post->lat);
$post_lon = floatval($geo_post->lon);
$post_distance = self::calculateDistanceKm($center_lat, $center_lon, $post_lat, $post_lon);
if ($post_distance < $search_distance) {
// Only include those that are in the the circle radius, not bounding box, otherwise we might miss some closer in the next step
$close_posts[$geo_post->post_id] = $post_distance;
}
}
$search_distance *= 2;
}
asort($close_posts);
return $close_posts;
}
}
$monkeyman_Geo_ClosestX_instace = new Monkeyman_Geo_ClosestX();