matt
  • 0
Мастер

Как реализовать поиск по местоположению (почтовый индекс) в WordPress?

  • 0

Я работаю над сайтом местного бизнес-каталога, который будет использовать настраиваемые типы сообщений для бизнес-записей. Одно из полей будет «Почтовый индекс». Как настроить поиск по местоположению?

Я бы хотел, чтобы посетители могли вводить свой почтовый индекс и выбирать категорию, а также отображать все предприятия в определенном радиусе или все предприятия, упорядоченные по расстоянию. Я видел пару плагинов, которые утверждают, что делают это, но они не поддерживают WordPress 3.0. Какие-либо предложения?

Share
  1. Я начинаю щедрость, потому что это интересный и сложный вопрос. У меня есть несколько собственных идей… но я хочу посмотреть, сможет ли кто-нибудь придумать что-то более элегантное (и более простое в сборке).

    • 0
  2. Я бы изменил ответ от gabrielk и связанного сообщения в блоге, используя индексы базы данных и сводя к минимуму количество расчетов фактического расстояния.

    Если вы знаете координаты пользователя и знаете максимальное расстояние (скажем, 10 км), вы можете нарисовать ограничивающую рамку размером 20 на 20 км с текущим местоположением посередине. Получите эти ограничивающие координаты и запросите только хранилища между этими широтами и долготами. Пока не используйте тригонометрические функции в запросе к базе данных, так как это предотвратит использование индексов. (Таким образом, вы можете получить магазин, который находится в 12 км от вас, если он находится в северо-восточном углу ограничивающей рамки, но мы выбрасываем его на следующем шаге.)

    Рассчитайте расстояние (по прямой или по фактическим направлениям движения, как вам больше нравится) только для тех немногих магазинов, которые были возвращены. Это значительно сократит время обработки, если у вас большое количество магазинов.

    Для связанного поиска ( «дайте десять ближайших магазинов» ) вы можете выполнить аналогичный поиск, но с начальным приближением расстояния (таким образом, вы начинаете с области 10 км на 10 км, и если у вас недостаточно магазинов, вы расширяете его до 20км на 20км и так далее). Для этого начального расстояния вы рассчитываете количество магазинов на общей площади и используете его. Или зарегистрируйте количество необходимых запросов и адаптируйте их с течением времени.

    Я добавил полный пример кода в соответствующий вопрос Майка, и вот расширение, которое дает вам ближайшие 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();
    
    • 0
  3. Сначала вам нужна таблица, которая выглядит примерно так:

    zip_code    lat     lon
    10001       40.77    73.98
    

    … заполняется для каждого почтового индекса. Вы можете расширить это, добавив поля города и штата, если хотите искать таким образом.

    Затем каждому магазину может быть присвоен почтовый индекс, и когда вам нужно рассчитать расстояние, вы можете присоединить таблицу широты/долготы к данным магазина.

    Затем вы запросите эту таблицу, чтобы получить широту и долготу для магазина и почтовые индексы пользователя. Как только вы его получите, вы можете заполнить свой массив и передать его функции «получить расстояние»:

    $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 магазинов, было бы довольно дорого запрашивать их все, перебирать и сортировать их при каждом запросе.

    • 0
  4. Документация MySQL также включает информацию о пространственных расширениях. Как ни странно, стандартная функция Distance() недоступна, но проверьте эту страницу: http://dev.mysql.com/tech-resources/articles/4.1/gis-with-mysql.html для получения подробной информации о том, как «преобразовать два значения POINT в LINESTRING, а затем вычислить их длину».

    Обратите внимание, что каждый поставщик, вероятно, предложит разные широты и долготы, представляющие «центроид» почтового индекса. Также стоит знать, что не существует реальных «граничных» файлов с почтовым индексом. У каждого поставщика будет свой собственный набор границ, которые примерно соответствуют конкретным спискам адресов, составляющих почтовый индекс USPS. (Например, в некоторых «границах» вам нужно будет включить обе стороны улицы, в других — только одну.) Зоны табулирования почтовых индексов (ZCTA), широко используемые продавцами, «не точно отображают области доставки почтовых индексов, и не включать все почтовые индексы, используемые для доставки почты» http://www.census.gov/geo/www/cob/zt_metadata.html

    Многие предприятия в центре города будут иметь свой собственный почтовый индекс. Вам нужен как можно более полный набор данных, поэтому убедитесь, что вы нашли список почтовых индексов, который включает как «точечные» почтовые индексы (обычно предприятия), так и «пограничные» почтовые индексы.

    У меня есть опыт работы с данными почтовых индексов с http://www.semaphorecorp.com/. Даже это не было на 100% точным. Например, когда мой кампус получил новый почтовый адрес и новый почтовый индекс, почтовый индекс был неуместен. Тем не менее, это был единственный источник данных, который, как я обнаружил, вообще ИМЕЛ новый почтовый индекс, так вскоре после того, как он был создан.

    У меня в книге был рецепт, как именно удовлетворить вашу просьбу… в Drupal. Он опирался на модуль Google Maps Tools ( http://drupal.org/project/gmaps, не путать с http://drupal.org/project/gmap, тоже достойный модуль). Вы можете найти полезный образец код в этих модулях, хотя, конечно, они не будут работать из коробки в WordPress.

    • 0

Оставить ответ

You must login to add an answer.