簡體   English   中英

按坐標之間的距離過濾wordpress帖子

[英]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中運行

現在,一般過程應是:

  1. 創建一個邊界square-ish框,將帖子過濾到幾個右邊的帖子。 這不應該在計算上太昂貴,但是可能會留下一些邊緣帖子,並包含一些不合適的帖子。
  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代碼未經測試,如果錯誤仍然存​​在,請道歉。 一般的概念是正確的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM