Saya akan memodifikasi jawaban dari gabrielk dan posting blog yang ditautkan dengan menggunakan indeks basis data dan meminimalkan jumlah perhitungan jarak aktual .
Jika Anda tahu koordinat pengguna dan Anda tahu jarak maksimum (katakanlah 10km), Anda bisa menggambar kotak pembatas 20km x 20km dengan lokasi saat ini di tengah. Dapatkan koordinat terikat ini dan kueri hanya menyimpan di antara garis lintang dan bujur ini . Jangan gunakan fungsi trigonometri dalam kueri basis data Anda, karena ini akan mencegah indeks digunakan. (Jadi Anda mungkin mendapatkan toko yang berjarak 12 km dari Anda jika berada di sudut timur laut kotak pembatas, tetapi kami membuangnya di langkah berikutnya.)
Hitung saja jarak (seperti burung terbang atau dengan arah mengemudi yang sebenarnya, seperti yang Anda inginkan) untuk beberapa toko yang dikembalikan. Ini akan secara drastis meningkatkan waktu pemrosesan jika Anda memiliki banyak toko.
Untuk pencarian terkait ( "berikan sepuluh toko terdekat" ) Anda dapat melakukan pencarian yang serupa, tetapi dengan tebakan jarak awal (jadi Anda mulai dengan area 10km kali 10km, dan jika Anda tidak memiliki cukup toko, Anda perlu memperluasnya ke 20km kali 20km dan seterusnya). Untuk jarak awal ini, tebak Anda pernah menghitung jumlah toko di seluruh area dan menggunakannya. Atau catat jumlah permintaan yang dibutuhkan dan sesuaikan dengan waktu.
Saya menambahkan contoh kode lengkap di pertanyaan terkait Mike , dan berikut ini adalah ekstensi yang memberi Anda lokasi X terdekat (cepat dan hampir tidak diuji):
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();