Ich würde ändert die Antwort von gabrielk und den verknüpfte Blogeintrag unter Verwendung von Datenbankindizes und die Anzahl der tatsächlichen Entfernungsberechnungen zu minimieren .
Wenn Sie die Koordinaten des Benutzers und die maximale Entfernung (z. B. 10 km) kennen, können Sie einen Begrenzungsrahmen von 20 x 20 km mit der aktuellen Position in der Mitte zeichnen. Holen Sie sich diese Grenzkoordinaten und fragen Sie nur Speicher zwischen diesen Breiten- und Längengraden ab . Verwenden Sie noch keine Trigonometriefunktionen in Ihrer Datenbankabfrage, da dies die Verwendung von Indizes verhindert. (Sie könnten also ein Geschäft in 12 km Entfernung von Ihnen bekommen, wenn es sich in der nordöstlichen Ecke des Begrenzungsrahmens befindet, aber wir werfen es im nächsten Schritt weg.)
Berechnen Sie nur die Entfernung (wie der Vogel fliegt oder mit tatsächlichen Wegbeschreibungen, wie Sie es bevorzugen) für die wenigen Geschäfte, die zurückgegeben werden. Dies wird die Verarbeitungszeit drastisch verkürzen, wenn Sie eine große Anzahl von Geschäften haben.
Für die zugehörige Suche ( "Geben Sie die zehn nächstgelegenen Geschäfte") ) können Sie eine ähnliche Suche durchführen, jedoch mit einer anfänglichen Entfernungsschätzung (Sie beginnen also mit einem Bereich von 10 km x 10 km, und wenn Sie nicht genügend Geschäfte haben, erweitern Sie ihn auf 20 km mal 20 km und so weiter). Für diese anfängliche Entfernung erraten Sie einmal die Anzahl der Geschäfte über die gesamte Fläche und verwenden diese. Oder protokollieren Sie die Anzahl der erforderlichen Abfragen und passen Sie sie im Laufe der Zeit an.
Ich habe bei Mikes verwandter Frage ein vollständiges Codebeispiel hinzugefügt. Hier ist eine Erweiterung, die die nächstgelegenen X-Standorte angibt (schnell und kaum getestet):
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();