मैं डेटाबेस अनुक्रमित का उपयोग करके और वास्तविक दूरी की गणना की संख्या को कम करके गैब्रिएलक और लिंक किए गए ब्लॉग पोस्ट से उत्तर को संशोधित करूंगा ।
यदि आप उपयोगकर्ता के निर्देशांक जानते हैं और आप अधिकतम दूरी जानते हैं (10 किमी कहते हैं), तो आप एक बाउंडिंग बॉक्स खींच सकते हैं जो कि 20 किमी तक 20 किमी है और बीच में वर्तमान स्थान है। इन अक्षांश निर्देशांक और अनुदैर्ध्य के बीच केवल बाउंडिंग निर्देशांक और क्वेरी प्राप्त करें । अभी तक अपने डेटाबेस क्वेरी में त्रिकोणमिति फ़ंक्शंस का उपयोग न करें, क्योंकि यह अनुक्रमित को उपयोग करने से रोक देगा। (तो आपको एक स्टोर मिल सकता है जो कि आपसे 12 किमी दूर है अगर यह बाउंडिंग बॉक्स के उत्तर-पूर्वी कोने में है, लेकिन हम इसे अगले चरण में फेंक देते हैं।)
केवल कुछ दुकानों के लिए दूरी (जैसे कि पक्षी उड़ता है या वास्तविक ड्राइविंग दिशाओं के साथ, आप पसंद करते हैं) वापस आ जाते हैं। यदि आपके पास बड़ी संख्या में स्टोर हैं, तो यह प्रसंस्करण समय में काफी सुधार करेगा।
संबंधित लुकअप के लिए ( "दस निकटतम स्टोर दें" ) आप एक समान लुकअप कर सकते हैं, लेकिन एक प्रारंभिक दूरी के अनुमान के साथ (इसलिए आप 10 किमी 10 किमी क्षेत्र से शुरू करते हैं, और यदि आपके पास पर्याप्त स्टोर नहीं हैं तो आप इसका विस्तार कर सकते हैं। 20 किमी 20 किमी और इतने पर)। इस प्रारंभिक दूरी के अनुमान के लिए, आप एक बार कुल क्षेत्र में दुकानों की संख्या की गणना करते हैं और इसका उपयोग करते हैं। या आवश्यक प्रश्नों की संख्या लॉग करें और समय के साथ अनुकूलित करें।
मैंने माइक से संबंधित प्रश्न में एक पूर्ण कोड उदाहरण जोड़ा , और यहां एक एक्सटेंशन दिया गया है जो आपको निकटतम एक्स स्थान देता है (त्वरित और मुश्किल से परीक्षण किया गया):
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();