按坐标之间的距离过滤wordpress帖子



我想做的是根据两个坐标之间的距离过滤一堆wordpress帖子。用户输入的坐标、范围和类别在URL中传递,如下所示:

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

还有一些帖子(不是所有的帖子)有一个lat和long字段,这是我使用插件Advanced Custom Fields创建的。以下是我传递给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公式。

我将使用以下公式:

  • 计算两个纬度/经度坐标之间的距离(km):

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

    其中φ是以弧度表示的纬度,λ是以弧度为单位的经度,R是地球半径(平均半径=6371km)

  • 在给定起始位置、距离和方位的情况下计算目的地:

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

    其中θ是方位(从北顺时针方向),δ是角距离d/R,d是行进距离。参见表2。


因此,我们将定义以下辅助函数:

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));
}

(可在视频中运行)

现在,一般流程应为:

  1. 创建一个方形边框,将帖子过滤为很少有人是对的。这不应该过于计算价格昂贵,但这是一个近似值,可能会留下一些边缘柱out,并包含一些不适合的帖子
  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代码未经测试,因此如果仍然存在错误,请道歉。不过,总体概念是正确的。

最新更新