То, что я пытаюсь сделать, это отфильтровать кучу сообщений 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 )
Теперь общий процесс:
Запрос, который вы хотите использовать, должен включать метаинформацию: см. Здесь полезное руководство по некоторым из этих мета-запросов
$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 не проверен, поэтому приносим извинения, если ошибки остаются. Однако общая концепция правильна.