Yo modificaría la respuesta de gabrielk y la publicación del blog vinculado utilizando índices de base de datos y minimizando el número de cálculos de distancia real .
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 centro. Obtenga estas coordenadas de límite y las consultas solo se almacenan entre estas latitudes y longitudes . Aún no use las funciones de trigonometría en su consulta de base de datos, ya que esto evitará que se usen los índices. (Por lo tanto, puede obtener una tienda que esté a 12 km de distancia de usted si está en la esquina noreste del cuadro delimitador, pero la desechamos en el siguiente paso).
Calcule solo la distancia (a medida que el ave vuela o con las instrucciones 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 ( "proporcione las diez tiendas más cercanas" ), puede hacer una búsqueda similar, pero con una estimación de la distancia inicial (así que comienza con un área de 10 km por 10 km, y si si no tiene suficientes tiendas, puede ampliarlo a 20 km por 20 km y así sucesivamente). Para esta conjetura de distancia inicial, una vez calcule el número de tiendas sobre el área total y úselo. O registre la cantidad de consultas que necesita y adáptese con el tiempo.
Agregué una 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();