WordPress'de konuma dayalı (posta kodu) aramayı nasıl uygulayabilirim?


19

İş girişleri için özel yazı türlerini kullanacak bir yerel iş dizini sitesinde çalışıyorum. Alanlardan biri "Posta Kodu" olacaktır. Konum tabanlı bir aramayı nasıl ayarlayabilirim?

Ziyaretçilerin posta kodlarını girip bir kategori seçmelerini ve belirli bir yarıçapa sahip tüm işletmeleri veya mesafeye göre sipariş edilen tüm işletmeleri göstermelerini istiyorum. Bunu iddia eden birkaç eklenti gördüm ama WordPress 3.0'ı desteklemiyorlar. Herhangi bir öneri?


2
Bir ödül kazanıyorum çünkü bu ilginç ve zorlu bir soru. Kendime ait birkaç fikrim var ... ama kimsenin daha zarif (ve inşa edilmesi daha kolay) bir şey bulabileceğini görmek istiyorum.
EAMann

Teşekkürler EAMann. Bu yardımcı olabilir: briancray.com/2009/04/01/…
matt

şu anda burada olurdu sadece öneri bakla gibi bir eklenti kullanmaktır
NetConstructor.com 4:10

@ NetConstructor.com - Bunun için Pod önermek olmaz; Pods'un bu sorunu Özel Mesaj Türlerine karşı sağladığı fayda gerçekten yoktur.
MikeSchinkel

@matt : Son zamanlarda site henüz tamamlanmamış veya dağıtılmamış olmasına rağmen buna çok benzer bir şey uyguladım. Aslında biraz da var. Bir noktada bir mağaza bulma eklentisi olarak paketlemeyi planlıyorum, ancak henüz genel bir çözüm olarak gönderebileceğim bazıları değil. Çevrimdışı olarak bana ulaşın ve ihtiyacınız olan cevabı alamazsanız size yardımcı olabilirim.
MikeSchinkel

Yanıtlar:


10

Ben veritabanı dizinleri kullanarak ve gerçek mesafe hesaplamaları sayısını en aza indirerek gabrielk ve bağlantılı blog yazı cevap değiştirirsiniz .

Kullanıcının koordinatlarını ve maksimum mesafeyi (10km diyelim) biliyorsanız, geçerli konum ortada 20km x 20km olan bir sınırlayıcı kutu çizebilirsiniz. Bu sınırlayıcı koordinatları alın ve yalnızca bu enlemler ve boylamlar arasındaki depoları sorgulayın . Veritabanı sorgunuzda henüz trigonometri işlevlerini kullanmayın; bu, dizinlerin kullanılmasını önler. (Yani sınırlayıcı kutunun kuzey-doğu köşesinde ise sizden 12km uzakta bir mağaza alabilirsiniz, ancak bir sonraki adımda dışarı atarız.)

Sadece iade edilen birkaç mağaza için mesafeyi (kuş uçarken veya gerçek sürüş yönleriyle) hesaplayın. Bu, çok sayıda mağazanız varsa işlem süresini önemli ölçüde artıracaktır.

İlgili arama için ( "en yakın on mağazayı verin" ) benzer bir arama yapabilirsiniz, ancak başlangıçtaki bir mesafe tahminiyle (böylece 10 km x 10 km'lik bir alanla başlarsınız ve yeterli mağazanız yoksa, 20km x 20km ve benzeri). Bu başlangıç ​​mesafesi için bir kez toplam alan üzerindeki mağaza sayısını hesaplar ve kullanırsınız. Veya gereken sorgu sayısını günlüğe kaydedin ve zaman içinde uyum sağlayın.

Mike'ın ilgili sorusuna tam bir kod örneği ekledim ve işte size en yakın X konumunu (hızlı ve zorlukla test edilmiş) veren bir uzantı var:

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

İlk önce şuna benzer bir tabloya ihtiyacınız var:

zip_code    lat     lon
10001       40.77    73.98

... her posta kodu için doldurulur. Bu şekilde arama yapmak istiyorsanız, şehir ve eyalet alanları ekleyerek bunu genişletebilirsiniz.

Daha sonra her mağazaya bir posta kodu verilebilir ve mesafeyi hesaplamanız gerektiğinde lat / uzun tabloyu mağaza verilerine katılabilir.

Ardından mağaza ve kullanıcının posta kodlarının enlem ve boylamını almak için bu tabloyu sorgulayacaksınız. Bir kez olsun dizi doldurabilir ve bir "mesafe olsun" işlevine geçirebilirsiniz:

$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);
}

Bu aslında bir uygulama kanıtı anlamına gelir, aslında uygulamayı tavsiye ederim kod değil. Örneğin, 10.000 mağazanız varsa, hepsini sorgulamak ve her istekte bunları sıralamak ve sıralamak oldukça pahalı bir işlem olacaktır.


Sonuçlar önbelleğe alınabilir mi? Ayrıca, ticari olarak kullanılabilen (veya varsa ücretsiz) posta kodu veritabanlarından birini sorgulamak daha kolay olur mu?
matt

1
@matt - Ticari veya ücretsiz olanlardan birini sorgulamakla ne demek istiyorsun? Önbellekleme her zaman mümkün olmalıdır, bkz. Codex.wordpress.org/Transients_API
hakre

@hakre: Boşver, sanırım şimdi başımın üstünde konuşuyorum. Mesafeleri almak için bir posta kodu veritabanları (USPS, Google Maps ...) kullanmaktan bahsediyorum ama muhtemelen mesafeleri saklamadıklarını fark etmedim, sadece posta kodunu ve koordinatlarını saklıyorlar ve hesaplamak için bana kal.
matt

3

MySQL belgeleri ayrıca uzamsal uzantılar hakkında bilgi içerir. Garip bir şekilde, standart distance () işlevi kullanılamıyor, ancak bu sayfayı kontrol edin: http://dev.mysql.com/tech-resources/articles/4.1/gis-with-mysql.html "dönüştürme" ile ilgili ayrıntılar için iki POINT değerini bir LINESTRING öğesine ayarlayın ve ardından bunun uzunluğunu hesaplayın. "

Her satıcının bir posta kodunun "centroid" ini temsil eden farklı enlem ve boylamlar sunabileceğini unutmayın. Ayrıca hiçbir gerçek tanımlı posta kodu "sınır" dosyaları olmadığını bilmek önemlidir. Her satıcının, USPS posta kodunu oluşturan belirli adres listelerine kabaca uyan kendi sınırları vardır. (Örneğin, bazı "sınırlarda" sokağın her iki tarafını da, yalnızca bir tarafına eklemeniz gerekir.) Satıcılar tarafından yaygın olarak kullanılan Posta Kodu Tablo Alanları (ZCTA), Posta Kodu teslim alanlarını tam olarak tasvir etmez, ve posta teslimi için kullanılan tüm Posta Kodlarını dahil etmeyin " http://www.census.gov/geo/www/cob/zt_metadata.html

Birçok şehir merkezi işletmesinin kendi posta kodu olacaktır. Mümkün olduğunca eksiksiz bir veri kümesi isteyeceksiniz, bu nedenle hem "nokta" posta kodlarını (genellikle işletmeler) hem de "sınır" posta kodlarını içeren bir posta kodu listesi bulduğunuzdan emin olun.

Http://www.semaphorecorp.com/ adresinden posta kodu verileriyle çalışma deneyimim var . Bu bile% 100 doğru değildi. Örneğin, kampüsüm yeni bir posta adresi ve yeni posta kodu aldığında, posta kodu yanlış yerleştirildi. Bununla birlikte, oluşturulduktan hemen sonra yeni posta kodunu bile HAD olarak bulduğum tek veri kaynağı olduğunu söyledi.

Kitabımda isteğinizi tam olarak nasıl karşılayacağınıza dair bir tarifim vardı ... Drupal'da. Google Haritalar Araçlar modülüne dayanıyordu ( http://drupal.org/project/gmaps , http://drupal.org/project/gmap , aynı zamanda değerli bir modül ile karıştırılmamalıdır .) Bazı yararlı örnekler bulabilirsiniz. Bu modüllerdeki kod, elbette, WordPress'teki kutunun dışında çalışmayacaklar.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.