ฉันจะแก้ไขคำตอบจาก gabrielkและโพสต์บล็อกที่เชื่อมโยงโดยใช้ฐานข้อมูลดัชนีและการลดจำนวนของการคำนวณระยะทางที่เกิดขึ้นจริง
หากคุณรู้พิกัดของผู้ใช้และคุณรู้ระยะทางสูงสุด (พูด 10 กม.) คุณสามารถวาดกล่องขอบเขตที่ 20 กม. 20 กม. ด้วยตำแหน่งปัจจุบันที่อยู่ตรงกลาง รับพิกัดขอบเขตและแบบสอบถามจะเก็บเฉพาะระหว่างละติจูดและลองจิจูดเหล่านี้เท่านั้น ยังไม่ใช้ฟังก์ชันตรีโกณมิติในการสืบค้นฐานข้อมูลเนื่องจากจะป้องกันไม่ให้ใช้ดัชนี (ดังนั้นคุณอาจได้รับร้านค้าที่อยู่ห่างจากคุณ 12 กม. หากอยู่ในมุมตะวันออกเฉียงเหนือของช่อง จำกัด แต่เราจะโยนมันออกไปในขั้นตอนถัดไป)
คำนวณระยะทางเท่านั้น (ตามที่นกบินหรือด้วยทิศทางการขับรถตามที่คุณต้องการ) สำหรับร้านค้าบางแห่งที่ส่งคืน นี่จะเป็นการปรับปรุงเวลาการประมวลผลอย่างมากหากคุณมีร้านค้าจำนวนมาก
สำหรับการค้นหาที่เกี่ยวข้อง ( "ให้สิบร้านค้าที่ใกล้ที่สุด" ) คุณสามารถทำการค้นหาที่คล้ายกัน แต่ด้วยการเดาระยะทางเริ่มต้น (ดังนั้นคุณเริ่มต้นด้วย 10km โดย 10km พื้นที่และถ้าคุณไม่มีร้านค้าเพียงพอคุณขยายไป 20km โดย 20km และอื่น ๆ ) สำหรับระยะทางเริ่มต้นนี้ให้เดาว่าคุณจะคำนวณจำนวนร้านค้าทั่วพื้นที่รวมและใช้มัน หรือบันทึกจำนวนข้อความค้นหาที่ต้องการและปรับตัวตามเวลา
ฉันได้เพิ่มตัวอย่างโค้ดแบบเต็มที่คำถามที่เกี่ยวข้องกับ Mikeและนี่คือส่วนขยายที่ให้ตำแหน่ง X ใกล้เคียงที่สุด (ทดสอบอย่างรวดเร็วและแทบจะไม่):
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();