Intereting Posts
phpMyAdmin Доступ запрещен Laravel жалуется на запрос с повторяющимися именованными параметрами Подтверждение ввода пользователя, на стороне клиента или на стороне сервера? Получение отрывка из HTML в PHP Библиотека классов php-ews всегда возвращает null Как импортировать CSV-файл в PHP? Обработка элементов в очереди SQS с помощью php-скрипта Как переадресовать массив с помощью несекретных цифровых клавиш Невозможно загрузить видео в учетную запись пользователя facebook с помощью Graph API Удалите пространство имен форм в форме Symfony2 (для REST API) PHP-функция html-функции почты не отображается в письме jQuery Mobile: как правильно отправить данные формы Как изменить динамический тег <title> в php на основе значений URL Является ли учетная запись службы в Google Calendar api v3 заменой ключа и секретным ключом домена в двухногих Oauth в v1? PHP SoapClient – возврат значений атрибутов в ответ

Фильтровать сообщения WordPress по расстоянию между координатами

То, что я пытаюсь сделать, это отфильтровать кучу сообщений WordPress на расстояние между двумя координатами. Существуют координаты, диапазон и категория, введенные пользователем, которые передаются в URL-адресе следующим образом:

/?cat=0&s=5041GW&range=250&lat=51.5654368&lon=5.071263999999928 

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

  $args = array( 'posts_per_page' => 24, 'category' => $_GET["cat"], 'orderby' => 'post_date', 'order' => 'DESC', 'post_type' => 'adressen', 'post_status' => 'publish', ); 

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

Это может быть довольно дорогостоящим вычислительным способом. Прямым способом сделать это было бы получить все сообщения, которые в противном случае соответствовали бы критериям, а затем пропустить их все, отбрасывая сообщения за пределами указанного диапазона.

Трудности возникают из-за отсутствия линейного отображения между метрами и lat / long. Это зависит от того, где вы находитесь на земле. См. Этот вопрос для деталей. Библиотека PHPcoord существует, чтобы сделать этот расчет для вас, но из-за слегка приближенного характера моего предложенного ответа я буду использовать приблизительные методы, описанные на этом веб-сайте, используя формулу Хаверсина .

Я буду использовать следующие формулы:

  • Чтобы вычислить расстояние в км между двумя лат / lng-координатами:

     x = Δλ ⋅ cos φm y = Δφ d = R ⋅ √(x² + y²) 

    где φ – широта в радианах, λ – долгота в радианах, R – радиус Земли (средний радиус = 6 371 км)

  • Чтобы вычислить пункт назначения с учетом начального значения, расстояния и опоры:

     φ2 = asin( sin φ1 ⋅ cos δ + cos φ1 ⋅ sin δ ⋅ cos θ ) λ2 = λ1 + atan2( sin θ ⋅ sin δ ⋅ cos φ1, cos δ − sin φ1 ⋅ sin φ2 ) 

    где θ – подшипник (по часовой стрелке с севера), δ – угловое расстояние d / R, а d – пройденное расстояние. См. Atan2 .


Поэтому мы будем определять следующие вспомогательные функции:

 const R = 6371; // km function distance_between_points_rad($lat1, $lng1, $lat2, $lng2){ // latlng in radians $x = ($lng2-$lng1) * cos(($lat1+$lat2)/2); $y = ($lat2-$lat1); // return distance in km return sqrt($x*$x + $y*$y) * R; } function get_destination_lat_rad($lat1, $lng1, $d, $brng){ return asin( sin($lat1)*cos($d/R) + cos($lat1)*sin($d/R)*cos($brng) ); } function get_destination_lng_rad($lat1, $lng1, $d, $brng){ $lat2 = get_destination_lat_rad($lat1, $lng1, $d, $brng); return $lng1 + atan2(sin($brng)*sin($d/R)*cos($lat1), cos($d/R)-sin($lat1)*sin($lat2)); } function get_bounding_box_rad($lat, $lng, $range){ // latlng in radians, $range in km $latmin = get_destination_lat_rad($lat, $lng, $range, 0); $latmax = get_destination_lat_rad($lat, $lng, $range, deg2rad(180)); $lngmax = get_destination_lng_rad($lat, $lng, $range, deg2rad(90)); $lngmin = get_destination_lng_rad($lat, $lng, $range, deg2rad(270)); // return approx bounding latlng in radians return array($latmin, $latmax, $lngmin, $lngmax); } function distance_between_points_deg($lat1, $lng1, $lat2, $lng2){ // latlng in degrees // return distance in km return distance_between_points_rad( deg2rad($lat1), deg2rad($lng1), deg2rad($lat2), deg2rad($lng2) ); } function get_bounding_box_deg($lat, $lng, $range){ // latlng in degrees, $range in km return array_map(rad2deg, get_bounding_box_rad(deg2rad($lat), deg2rad($lng), $range)); } 

( Runnable in ideone )

Теперь общий процесс:

  1. Создайте ограничивающий прямоугольный квадрат, чтобы отфильтровать сообщения только в нескольких правильных. Это не должно быть слишком дорогостоящим вычислением, но это приблизительное значение, которое может оставить некоторые крайние сообщения, и включать некоторые сообщения, которые не подходят.
  2. Уточните возвращенные сообщения только тем, которые соответствуют счету. Это вычислительно дорогостоящий процесс, следовательно, первый этап. Несколько должностей, исключенных на первом этапе, будут по-прежнему исключены. Ограничительная коробка потенциально может быть увеличена для размещения.

Запрос, который вы хотите использовать, должен включать метаинформацию: см. Здесь полезное руководство по некоторым из этих мета-запросов

 $lat1 = $_GET['lat']; // degrees $lng1 = $_GET['lng']; // degrees $range = $_GET['range']; // km // get the approximate bounding box $bbox = get_bounding_box_deg($lat1, $lng1, $range); // query the posts $args = array( 'posts_per_page' => 24, 'category' => $_GET["cat"], 'orderby' => 'post_date', 'order' => 'DESC', 'post_type' => 'adressen', 'post_status' => 'publish', 'meta_query' => array( 'relation' => 'AND', array( 'key' => 'lat', 'value' => array( $bbox[0], $bbox[1] ), 'type' => 'numeric', 'compare' => 'BETWEEN' ), array( 'key' => 'lng', 'value' => array( $bbox[2], $bbox[3] ), 'type' => 'numeric', 'compare' => 'BETWEEN' ) ) ); $the_query = new WP_Query( $args ); 

Затем сообщения отфильтровываются в цикле:

 // Then filter the posts down in the loop if ( $the_query->have_posts() ) { while ( $the_query->have_posts() ) { $the_query->the_post(); $custom_fields = get_post_custom(); if (isset($custom_fields['lat']) && isset($custom_fields['lng'])){ $lat2 = $custom_fields['lat']; $lng2 = $custom_fields['lng']; $dist = distance_between_points_deg($lat1, $lng1, $lat2, $lng2); if ($dist <= $range){ // post is in range } else { // post out of range, discard } } else { // post has no latlng coords } } } else { // no posts found } /* Restore original Post Data */ wp_reset_postdata(); 

Код WordPress не проверен, поэтому приносим извинения, если ошибки остаются. Однако общая концепция правильна.