Je modifierais la réponse de gabrielk et du billet de blog lié en utilisant des index de base de données et en minimisant le nombre de calculs de distance réels .
Si vous connaissez les coordonnées de l'utilisateur et que vous connaissez la distance maximale (disons 10 km), vous pouvez dessiner un cadre de délimitation de 20 km sur 20 km avec l'emplacement actuel au milieu. Obtenez ces coordonnées de délimitation et interrogez uniquement les magasins entre ces latitudes et longitudes . N'utilisez pas encore de fonctions de trigonométrie dans votre requête de base de données, car cela empêcherait l'utilisation d'index. (Vous pouvez donc obtenir un magasin à 12 km de vous s'il se trouve dans le coin nord-est du cadre de délimitation, mais nous le jetons à l'étape suivante.)
Calculez uniquement la distance (à vol d'oiseau ou avec les itinéraires réels, selon votre préférence) pour les quelques magasins retournés. Cela améliorera considérablement le temps de traitement si vous avez un grand nombre de magasins.
Pour la recherche associée ( "donnez les dix magasins les plus proches" ), vous pouvez effectuer une recherche similaire, mais avec une estimation de la distance initiale (vous commencez donc avec une zone de 10 km sur 10 km, et si vous n'avez pas assez de magasins, vous la développez pour 20 km sur 20 km et ainsi de suite). Pour cette distance initiale, vous devez calculer le nombre de magasins sur la surface totale et l'utiliser. Ou enregistrez le nombre de requêtes nécessaires et adaptez-vous au fil du temps.
J'ai ajouté un exemple de code complet à la question connexe de Mike , et voici une extension qui vous donne les emplacements X les plus proches (rapides et à peine testés):
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();