ฉันจะใช้การค้นหาตามตำแหน่ง (รหัสไปรษณีย์) ใน WordPress ได้อย่างไร


19

ฉันกำลังทำงานในไซต์ไดเรกทอรีธุรกิจท้องถิ่นที่จะใช้ประเภทโพสต์ที่กำหนดเองสำหรับรายการธุรกิจ หนึ่งในฟิลด์จะเป็น "รหัสไปรษณีย์" ฉันจะตั้งค่าการค้นหาตามตำแหน่งได้อย่างไร

ฉันต้องการให้ผู้เยี่ยมชมสามารถป้อนรหัสไปรษณีย์ของพวกเขาและเลือกหมวดหมู่และแสดงธุรกิจทั้งหมดภายในรัศมีที่แน่นอนหรือธุรกิจทั้งหมดที่สั่งซื้อตามระยะทาง ฉันเห็นปลั๊กอินสองตัวที่อ้างว่าทำสิ่งนี้ แต่พวกเขาไม่สนับสนุน WordPress 3.0 ข้อเสนอแนะใด ๆ


2
ฉันเริ่มได้รับรางวัลเพราะนี่เป็นคำถามที่น่าสนใจและท้าทาย ฉันมีความคิดบางอย่างของตัวเอง ... แต่ฉันต้องการที่จะดูว่าใครสามารถเกิดขึ้นกับสิ่งที่สง่างามมากขึ้น (และง่ายต่อการสร้าง)
EAMann

ขอบคุณ EAMann นี้อาจช่วยให้: briancray.com/2009/04/01/...
แมตต์

ข้อเสนอแนะเท่านั้นที่ฉันจะมีในขณะนี้ที่นี่คือการใช้ปลั๊กอินเช่นฝัก
NetConstructor.com

@ NetConstructor.com - ฉันจะไม่แนะนำ Pods สำหรับสิ่งนี้ มีประโยชน์จริงๆ Pods เสนอปัญหานี้กับประเภทโพสต์ที่กำหนดเอง
MikeSchinkel

@matt : เมื่อเร็ว ๆ นี้ฉันได้นำบางสิ่งที่คล้ายกันมาใช้แม้ว่าไซต์จะยังไม่สรุปหรือปรับใช้ ที่จริงแล้วก็มีอยู่ด้วยเช่นกัน ฉันวางแผนที่จะทำแพคเกจมันขึ้นมาเป็นปลั๊กอินตัวระบุตำแหน่งร้านค้าในบางจุด แต่มันก็ไม่ใช่บางอย่างที่ฉันสามารถโพสต์เป็นโซลูชันทั่วไปได้ในตอนนี้ ติดต่อฉันออฟไลน์และฉันอาจสามารถช่วยเหลือได้หากคุณไม่ได้รับคำตอบที่ต้องการ
MikeSchinkel

คำตอบ:


10

ฉันจะแก้ไขคำตอบจาก 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();

8

ก่อนอื่นคุณต้องมีตารางที่มีหน้าตาดังนี้:

zip_code    lat     lon
10001       40.77    73.98

... บรรจุสำหรับรหัสไปรษณีย์แต่ละรหัส คุณสามารถขยายได้โดยเพิ่มเขตข้อมูลเมืองและรัฐหากคุณต้องการค้นหาด้วยวิธีนี้

จากนั้นแต่ละร้านค้าจะได้รับรหัสไปรษณีย์และเมื่อคุณต้องการคำนวณระยะทางคุณสามารถเข้าร่วมตาราง lat / long กับข้อมูลร้านค้า

จากนั้นคุณจะค้นหาตารางนั้นเพื่อรับละติจูดและลองจิจูดสำหรับร้านค้าและรหัสไปรษณีย์ของผู้ใช้ เมื่อคุณได้รับมันคุณสามารถเติมอาร์เรย์ของคุณและส่งไปยังฟังก์ชัน "get distance":

$user_location = array(
    'latitude' => 42.75,
    'longitude' => 73.80,
);

$output = array();
$results = $wpdb->get_results("SELECT id, zip_code, lat, lon FROM store_table");
foreach ( $results as $store ) {
    $store_location = array(
        'zip_code' => $store->zip_code, // 10001
        'latitude' => $store->lat, // 40.77
        'longitude' => $store->lon, // 73.98
    );

    $distance = get_distance($store_location, $user_location, 'miles');

    $output[$distance][$store->id] = $store_location;
}

ksort($output);

foreach ($output as $distance => $store) {
    foreach ( $store as $id => $location ) {
        echo 'Store ' . $id . ' is ' . $distance . ' away';
    }
}

function get_distance($store_location, $user_location, $units = 'miles') {
    if ( $store_location['longitude'] == $user_location['longitude'] &&
    $store_location['latitude'] == $user_location['latitude']){
        return 0;

    $theta = ($store_location['longitude'] - $user_location['longitude']);
    $distance = sin(deg2rad($store_location['latitude'])) * sin(deg2rad($user_location['latitude'])) + cos(deg2rad($store_location['latitude'])) * cos(deg2rad($user_location['latitude'])) * cos(deg2rad($theta));
    $distance = acos($distance);
    $distance = rad2deg($distance);
    $distance = $distance * 60 * 1.1515;

    if ( 'kilometers' == $units ) {
        $distance = $distance * 1.609344;
    }

    return round($distance);
}

สิ่งนี้มีความหมายว่าเป็นหลักฐานพิสูจน์แนวคิดไม่ใช่รหัสที่ฉันอยากจะแนะนำให้ดำเนินการ ตัวอย่างเช่นถ้าคุณมี 10,000 ร้านค้ามันจะเป็นการดำเนินการที่ค่อนข้างแพงในการค้นหาทั้งหมดและวนซ้ำและเรียงลำดับตามคำขอทุกครั้ง


ผลลัพธ์อาจถูกแคชหรือไม่ นอกจากนี้มันจะง่ายกว่าไหมถ้าจะค้นหาหนึ่งในฐานข้อมูลรหัสไปรษณีย์ (หรือฟรีถ้ามี) ฐานข้อมูลหรือไม่
matt

1
@matt - คุณหมายถึงอะไรโดยการสืบค้นหนึ่งในเชิงพาณิชย์หรือฟรีใช้ได้? การแคชควรเป็นไปได้เสมอดูcodex.wordpress.org/Transients_API
hakre

@hakre: ไม่เป็นไรฉันคิดว่าฉันกำลังพูดถึงหัวของฉันตอนนี้ ฉันกำลังพูดถึงการใช้ฐานข้อมูลรหัสไปรษณีย์ (USPS, Google Maps ... ) เพื่อให้ได้ระยะทาง แต่ฉันไม่ทราบว่าพวกเขาอาจไม่ได้จัดเก็บระยะทางพวกเขาเพียงแค่เก็บรหัสไปรษณีย์และพิกัดและมันจะ ขึ้นอยู่กับฉันในการคำนวณ
แม

3

เอกสาร MySQL ยังมีข้อมูลเกี่ยวกับส่วนขยายเชิงพื้นที่ ฟังก์ชั่นระยะทางมาตรฐาน () ไม่สามารถใช้ได้ แต่ตรวจสอบหน้านี้: http://dev.mysql.com/tech-resources/articles/4.1/gis-with-mysql.html สำหรับรายละเอียดเกี่ยวกับวิธีการ "แปลง ค่าจุดสองค่าไปยัง LINESTRING แล้วคำนวณความยาวของค่านั้น

โปรดทราบว่าผู้ขายทุกรายมีแนวโน้มที่จะเสนอละติจูดและลองจิจูดที่แตกต่างกันซึ่งแสดงถึง "เซนทรอยด์" ของรหัสไปรษณีย์ นอกจากนี้ยังควรรู้ว่าไม่มีไฟล์ "ขอบเขต" ที่กำหนดรหัสไปรษณีย์จริง ผู้ขายแต่ละรายจะมีชุดขอบเขตของตนเองที่ตรงกับรายการที่อยู่เฉพาะซึ่งประกอบด้วยรหัสไปรษณีย์ USPS (ตัวอย่างเช่นใน "ขอบเขต" บางอย่างคุณจะต้องรวมทั้งสองฝั่งของถนนในที่อื่น ๆ เพียงอันเดียว) พื้นที่การจัดระเบียบรหัสไปรษณีย์ (ZCTA) ที่ผู้ขายใช้กันอย่างแพร่หลาย "ไม่ได้อธิบายถึงพื้นที่จัดส่งรหัสไปรษณีย์อย่างแม่นยำ และไม่รวมรหัสไปรษณีย์ทั้งหมดที่ใช้ในการส่งจดหมาย " http://www.census.gov/geo/www/cob/zt_metadata.html

ธุรกิจในเมืองหลายแห่งจะมีรหัสไปรษณีย์ของตนเอง คุณจะต้องทำให้ชุดข้อมูลสมบูรณ์ที่สุดเท่าที่จะเป็นไปได้ดังนั้นตรวจสอบให้แน่ใจว่าคุณค้นหารายการรหัสไปรษณีย์ที่มีรหัสไปรษณีย์ทั้ง "จุด" (โดยปกติคือธุรกิจ) และรหัสไปรษณีย์ "ขอบเขต"

ผมมีประสบการณ์การทำงานกับข้อมูลรหัสไปรษณีย์จากhttp://www.semaphorecorp.com/ แม้จะไม่ถูกต้อง 100% ตัวอย่างเช่นเมื่อมหาวิทยาลัยของฉันใช้ที่อยู่ทางไปรษณีย์ใหม่และรหัสไปรษณีย์ใหม่รหัสไปรษณีย์นั้นหายไป ที่กล่าวว่ามันเป็นแหล่งข้อมูลเดียวที่ฉันพบว่าแม้จะมีรหัสไปรษณีย์ใหม่เลยดังนั้นไม่นานหลังจากที่มันถูกสร้างขึ้น

ฉันมีสูตรในหนังสือของฉันสำหรับวิธีการตอบสนองคำขอของคุณ ... ใน Drupal มันอาศัยโมดูลเครื่องมือของ Google Maps ( http://drupal.org/project/gmapsเพื่อไม่ให้สับสนกับhttp://drupal.org/project/gmapซึ่งเป็นโมดูลที่มีค่าเช่นกัน) คุณอาจพบตัวอย่างที่มีประโยชน์บางอย่าง โค้ดในโมดูลเหล่านั้นถึงแม้ว่ามันจะไม่ทำงานนอกกรอบใน WordPress

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.