1

I have two custom fields on my posts with values that could be like

get_usp-custom-19 = 40.85150386578784
get_usp-custom-20 = 14.258907499999964

Then I receive a GET from a form with some values like this:

$lat = $_GET['usp-custom-19'];
$ln = $_GET['usp-custom-20'];

Now $lat and $ln have latitude and a longitude separated values and I create a query like:

$args = array(
    'post_type' => 'post',
    'meta_query' => array(
        array(
            'relation' => 'AND',
            array(
                'key' => 'get_usp-custom-19',
                'value' => $lat,
                'compare' => 'BETWEEN',
                'type' => 'NUMERIC',
            ),
            array(
                'key' => 'get_usp-custom-20',
                'value' => $ln,
                'compare' => 'BETWEEN',
                'type' => 'NUMERIC',
            ),
        ),
    ),
);

But I get wrong results as it isn't comparing in between, I also tried to set it type as CHAR or DECIMALS but still, wrong results. Also I could get negative coords like -9.258907499999964 and I was reading about cons and sen or using abs(), but I am getting very confused now

UPDATE

Here I try to create a radius

$lat = $_GET['usp-custom-19'];
$ln = $_GET['usp-custom-20'];

$args = get_posts( 
    array( 
        'post_type'      => 'post', 
        'posts_per_page' => -1, 
    ) 
);
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
    while ( $query->have_posts() ) {
        $query->the_post();
        $customCoords = usp_get_meta(false, 'usp-custom-90');
        $arrayCoords = explode( ",", $customCoords );
        $radiusLn = +$arrayCoords[0] + 10;
        $radiusLat = +$arrayCoords[1] + 10;
        $args = array(
            'post_type' => 'post',
            'meta_query' => array(
                array(
                    'relation' => 'AND',
                    array(
                        'key' => 'get_usp-custom-19',
                        'value' => array($ln, $radiusLn),
                        'compare' => '>='
                    ),
                    array(
                        'key' => 'get_usp-custom-20',
                        'value' => array($lat, $radiusLat),
                        'compare' => '<='
                    ),
                ),
            ),
        );
        $query = new WP_Query( $args );
        if ( $query->have_posts() ) {
            while ( $query->have_posts() ) {
                $query->the_post();
                the_title();
            }
        }
    }
}
rob.m
  • 9,843
  • 19
  • 73
  • 162
  • Hi Rob, what is the type of `$lat` and `$ln`? `BETWEEN` is valid when the `value` is an array. – He Wang Nov 27 '18 at 00:26
  • @Speir hey, good question, how would I know it? I suspect is a CHAR but I don't know how to find out what type is GET – rob.m Nov 27 '18 at 00:27
  • you can [check php variable type](http://php.net/manual/en/function.gettype.php) – He Wang Nov 27 '18 at 00:30
  • @Speir oh thanks a lot, they are string – rob.m Nov 27 '18 at 00:31
  • @Speir basically I make a search, the search GET 2 values, longitude and latitude, the each post have 2 custom fields, for both lat and lng, I need to find all posts that are in between then values I get form the form. Maybe I am missing 2 values, I am just getting really confused in regards – rob.m Nov 27 '18 at 00:32
  • It's probably easier for me to post an answer with code. – He Wang Nov 27 '18 at 00:39
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/184292/discussion-between-rob-m-and-speir). – rob.m Nov 27 '18 at 00:40
  • Possible duplicate of [How to query SQL for coords nearest a point?](https://stackoverflow.com/questions/53524205/how-to-query-sql-for-coords-nearest-a-point) – rob.m Nov 30 '18 at 10:59

2 Answers2

0

Basically, you want to make sure that the value of the key and the $lat and $ln are the same type and they can be compared, for example, decimal.

Then you can use >= and <= to compare.

$args = array(
  'post_type' => 'post',
  'meta_query' => array(
     array(
       'relation' => 'AND',
        array(
            'key' => 'get_usp-custom-19', // make sure the value of this field is the same as $lat; 
            'value' => $lat,
            'compare' => '>=',
            'type' => 'NUMERIC',
        ),
        array(
            'key' => 'get_usp-custom-20', // make sure the value of this field is the same as $ln; 
            'value' => $ln,
            'compare' => '<=',
            'type' => 'NUMERIC',
        ),
    ),
  ),
);

You might also want to change the type of $lat and $ln first.

This post explains the compare operators pretty well.

He Wang
  • 647
  • 7
  • 19
  • it's a bit more complex than this, I am missing a value to compare to. In your example, which is correct, we are comparing latitude with longitude, this is because I am missing the logic in the question and your answer too. We are missing a value to compare with. My idea is to create a radius, we take the lowest coords of the post in the loop, then we add 10, so it becomes 45.976 = 55.976, and this will be our radius, now we can use the comparing operators since we're comparing two values. Yet $lat is a string and we need to add 10 as a number, trying to use floor() – rob.m Nov 27 '18 at 01:12
  • Ha, no wonder I felt like this was a bit weird. Then you still need to use `BETWEEN`, but you also need to create an array for both 19 and 20 `value` so you can have a range to compare. – He Wang Nov 27 '18 at 01:19
  • but also, I need 2 loop I guess, because when we search, the query first needs to check for all the posts, order them by lowest number, add +10 to it, and then finally create another loop where we compare them. Unless we have a method to do all of this within the same query,. Any idea? – rob.m Nov 27 '18 at 01:21
  • Using two loops doesn't sound optimal, but I can't think of another way to do this. – He Wang Nov 27 '18 at 01:27
0

I am guessing that your are trying to find the closest posts with respect to lat and long. In this case I think a raw SQL query is easier to use:

$post_ids = $wpdb->get_col( <<<EOD
SELECT m.post_id FROM $wpdb->postmeta m, $wpdb->postmeta n
    WHERE m.post_id=n.post_id AND m.meta_key='get_usp-custom-19' AND n.meta_key='get_usp-custom-20'
        AND (POW( CAST(m.meta_value AS DECIMAL(7,4)) - $lat, 2 )
                + POW( COS(0.0175 * $lat) * ( CAST(n.meta_value AS DECIMAL(7,4)) - $ln ), 2 ) ) )
            < $rad * $rad
EOD
);

The rows in the SQL join have the lat and long and then the results are where the sum of the squares of the differences from a given ($lat,$ln) is less than $rad squared.

Addenda:

The original query omitted the COS correction which must be applied to longitude degrees. This uses the Pythagorean theorem which is only valid on flat surfaces. The two points need to be close enough so that the triangle is essentially on a flat surface. (Otherwise, you need a "Great Circle" formula.) Also, there will be a problem if the two points straddle the opposite of the Prime Meridian - the meridian of 180 degrees - International Date Line.

Response to Comments:

The query as given is indeed to inefficient for large databases. The problem is that the join is done over the entire postmeta table. To fix this you can do the join over derived tables where the left derived table is restricted to rows with latitudes close to the given latitude and the right derived table are restricted to rows with longitude close to the given longitude.

The Pythagorean theorem is indeed not valid on the surface of the sphere. However, the objective here is to find close points not compute completely accurate distances. Since, we are looking for close points the distortion of the Pythagorean theorem is very small as the points are essentially on a flat surface. In my opinion, the Haversine formula is computationally far more expensive and provides unnecessary additional accuracy with respect to this problem.

  • mm let me try this – rob.m Nov 27 '18 at 03:22
  • see my updated question, I am trying to create my won radius, where is it your? -100? – rob.m Nov 27 '18 at 03:23
  • this code is giving an error and page won't load, is it everything closed fine on your code or is it me? https://pastebin.com/9y2FVe13 – rob.m Nov 27 '18 at 03:27
  • it just says HTTP ERROR 500 – rob.m Nov 27 '18 at 03:29
  • removed my code and only left your and page goes to 500 – rob.m Nov 27 '18 at 03:30
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/184294/discussion-between-rob-m-and-magenta). – rob.m Nov 27 '18 at 03:30
  • HI, I am still battling with this, I can't get my head around it on how to use this – rob.m Nov 27 '18 at 08:34
  • Thanks for updating, yet if I do the following I get nothing `$posts = $wpdb->get_col( "SELECT m.post_id FROM $wpdb->postmeta m, $wpdb->postmeta n WHERE m.post_id=n.post_id AND m.meta_key='get_usp-custom-19' AND n.meta_key='get_usp-custom-20' AND (POW( CAST(m.meta_value AS DECIMAL(7,4)) - $lat, 2 ) + POW( COS(0.0175 * $lat) * ( CAST(n.meta_value AS DECIMAL(7,4)) - $ln ), 2 ) ) ) < $rad * $rad"); foreach($posts as $post) { the_title(); }` – rob.m Nov 28 '18 at 15:51
  • A comment in regards of this said: Note that the fact this query is a post meta query that tries to find post IDs via their meta key/value pairs, and not the other way around, makes this query very slow/expensive on a database server. Not only that, but comparing multiple values making it a multidimensional query makes it even more expensive, and even worse, performing a calculation means this is one of the most expensive queries you could perform. The performance will degrade significantly as more rows are added to that table – rob.m Nov 28 '18 at 18:27
  • and this too A moderately sized database would be brought down by a handful of users making these kinds of requests. You will need to investigate special measures, e.g. elastic search, geoindexes, etc. As for the greater circle, that's a math question not a WordPress question, pythagoras' theorem might tell you the distance on a rectangles surface, but the earth is curved, your distance value will be distorted significantly at higher latitudes, and be smaller than reality in most situations – rob.m Nov 28 '18 at 18:27
  • @rob.m I have addressed these issues in the 'Response to Comments' section in my answer. –  Nov 29 '18 at 23:32
  • this is what I ended up with `$post_ids = $wpdb->get_col("SELECT j.post_id FROM $wpdb->postmeta j, $wpdb->postmeta k where j.post_id = k.post_id and j.meta_key='usp-custom-19' and k.meta_key='usp-custom-20' and ( 6371 * acos( cos( radians($lat) ) * cos( radians( j.meta_value ) ) * cos( radians( k.meta_value ) - radians($ln) ) + sin( radians($lat) ) * sin(radians(j.meta_value)) ) ) < 50");` – rob.m Nov 30 '18 at 09:07