按坐标之间的距离过滤wordpress帖子 [英] Filter wordpress posts by distance between coordinates

查看:48
本文介绍了按坐标之间的距离过滤wordpress帖子的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想做的是按2个坐标之间的距离过滤一堆wordpress帖子.用户输入的坐标,范围和类别都是通过URL传递的,如下所示:

What I'm trying to do is filter a bunch of wordpress posts by distance between 2 coordinates. There is coordinates, a range and a category inputted by the user that are passed in the URL like this:

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

然后有一些帖子(并非全部)具有经纬度字段,该字段是我使用插件高级自定义字段"创建的.这些是我传递给get_posts的参数,以获取按类别过滤的帖子:

Then there are posts(not all of them) that have a lat and long field which I created using the plugin Advanced Custom Fields. These are the arguments I pass to get_posts to get the posts that are filtered by category:

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

现在我要尝试的是对此进行修改,以便在实际通过范围和位置时,帖子将被过滤以仅返回其位置在用户搜索的位置范围(以千米为单位)内的帖子为了.我似乎无法为此找到一个好的解决方案,因为我很难使用wordpress及其插件.我真的很感激我能理解的解决方案.

Now what I'm trying to do is modify this so that when a range and location are actually passed, the posts will be filtered to only return posts with a location within the range(in kilometers) of the location the user searched for. I can't seem to be able to find a good solution for this as I'm having a hard time working with wordpress and the plugins it has. I would really appreciate a solution I can understand.

推荐答案

这在计算上可能是相当昂贵的.一种简单的方法是获取所有符合条件的帖子,然后遍历所有帖子,丢弃指定范围以外的帖子.

This could be fairly expensive computationally. The straightforward way to do it would be to get all the posts that otherwise fit the criteria, and then loop through them all, discarding posts outside the specified range.

出现困难是因为米和纬度/经度之间没有线性映射.这取决于您在地球上的位置.有关详细信息,请参见此问题. PHPcoord库可以为您进行此计算,但是由于我建议的答案,我将使用本网站中所述的近似方法使用 Haversine公式.

Difficulties arise because there is not a linear mapping between meters and lat/long. It depends on where you are on the earth. See this question for details. The PHPcoord library exists to make this calculation for you, but due to the slightly approximate nature of my proposed answer, I shall be using approximate methods described on this website using the Haversine formula.

我将使用以下公式:

  • 要计算两个纬度/经度坐标之间的距离(以公里为单位):

  • To calculate the distance in km between two lat/lng coords:

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

其中φ是弧度的纬度,λ是弧度的经度,R是地球半径(平均半径= 6,371 km)

where φ is latitude in radians, λ is longitude in radians, R is earth’s radius (mean radius = 6,371km)

在给定起始纬度,距离和方位的情况下计算目的地:

To calculate the destination given a starting latlng, distance, and bearing:

φ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 .

where θ is the bearing (clockwise from north), δ is the angular distance d/R, and d is the distance travelled. See atan2.

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

We shall therefore define the following helper functions:

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. 创建一个方形方框以将帖子过滤为一个 很少有关于正确的事情.这不应该太计算 昂贵,但是这是一个近似值,可能会留下一些边缘信息 并包括一些不合适的帖子.
  2. 重新整理返回的内容 发布给符合条件的人.这是一个计算 昂贵的过程,因此是第一阶段.排除在以下几篇文章 第一步仍将被排除.边界框可以 可能会变大以容纳.
  1. Create a bounding square-ish box to filter down posts into just a few that are about right. This should not be too computationally expensive, but is an approximation which might leave some edge posts out, and include some posts that do not fit.
  2. Refine the returned posts to just those that fit the bill. This is a computationally expensive process, hence the first stage. The few posts excluded in the first step will still be excluded. The bounding box could potentially be made larger to accommodate.

您要使用的查询应包含元信息: 请参阅此处,以获取有关某些元查询的有用指南

The query you want to use shall include meta information: see here for a useful guide to some of these meta queries

$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 the posts are filtered down in the loop:

// 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代码未经测试,因此如果仍有错误,我们深表歉意.大致的概念是正确的.

The WordPress code is untested, so apologies if errors remain. The general concept is correct though.

这篇关于按坐标之间的距离过滤wordpress帖子的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆