[英]Filter wordpress posts by distance between coordinates
我想要做的是通過2個坐標之間的距離過濾一堆wordpress帖子。 用戶輸入的坐標,范圍和類別在URL中傳遞,如下所示:
/?cat=0&s=5041GW&range=250&lat=51.5654368&lon=5.071263999999928
然后有一些帖子(不是全部)有一個lat和long字段,我使用插件高級自定義字段創建。 這些是我傳遞給get_posts以獲取按類別過濾的帖子的參數:
$args = array(
'posts_per_page' => 24,
'category' => $_GET["cat"],
'orderby' => 'post_date',
'order' => 'DESC',
'post_type' => 'adressen',
'post_status' => 'publish',
);
現在我要做的就是修改它,以便在實際傳遞范圍和位置時,帖子將被過濾為僅返回位置在用戶搜索位置的范圍(以千米為單位)內的帖子。 我似乎無法找到一個好的解決方案,因為我很難使用wordpress及其插件。 我真的很感激我能理解的解決方案。
這在計算上可能相當昂貴。 直接的方法是獲取符合條件的所有帖子,然后循環遍歷所有帖子,丟棄指定范圍之外的帖子。
由於米和緯度/長度之間沒有線性映射,因此出現了困難。 這取決於你在地球上的位置。 有關詳情,請參閱此問題 。 PHPcoord庫的存在是為了你的計算,但由於我提出的答案的略微近似性質,我將使用本網站上描述的近似方法使用Haversine公式 。
我將使用以下公式:
要計算兩個lat / lng坐標之間的距離(以km為單位):
x = Δλ ⋅ cos φm y = Δφ d = R ⋅ √(x² + y²)
其中φ是弧度的緯度,λ是弧度的經度,R是地球的半徑(平均半徑= 6,371km)
在給定起始緯度,距離和方位的情況下計算目的地:
φ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));
}
( 在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代碼未經測試,如果錯誤仍然存在,請道歉。 一般的概念是正確的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.