Filtering WordPress posts by closest locations

I have WordPress posts that are displayed on a Google Map. The posts pull from a custom post field that includes the latlng value. The lat and lng are together in this value.

The map also shows the users location with Googles GeoLocation.

Read More

Could someone point me in the right direction to filter the posts by the 100 closest posts to the users location?

I am filtering in other instances by query_posts meta key and thought it might work in this case too.

–UPDATE–

I am storing the lat and long values in two separate fields now. I can filter this just fine if I set predetermined values of the users lat/long like this…

  $lat_plus = '40.496755';
  $lat_minus = '35.496755';
  $long_plus = '-85.051597';
  $long_minus = '-89.3874682';
  $the_query = new WP_Query(array(
       'cat'   => $custom_category,
       'posts_per_page' => 50,
       'order' => 'ASC',
       'meta_query' => array(
             'relation' => 'AND',
              array(
                  'key' => 'latitude',
                  'value' => $lat_plus,
                  'compare' => '<',
                  'type' => 'NUMERIC'
              ),
              array(
                  'key' => 'latitude',
                  'value' => $lat_minus,
                  'compare' => '>',
                  'type' => 'NUMERIC'
              ),
              array(
                  'key' => 'longitude',
                  'value' => $long_plus,
                  'compare' => '<',
                  'type' => 'NUMERIC'
              ),
              array(
                  'key' => 'longitude',
                  'value' => $long_minus,
                  'compare' => '>',
                  'type' => 'NUMERIC'
              )

I am wanting to set these values based off the users current location. I am able to get these values and add and subtract by a number to create a square around the users current location with this code.

 <SCRIPT type="text/javascript">
        $(function() {
            if(navigator.geolocation) {
                var fallback = setTimeout(function() { fail('10 seconds expired'); }, 10000);
                navigator.geolocation.getCurrentPosition(
                    function (pos) {
                        clearTimeout(fallback);
                        console.log('pos', pos);
                        var point = new google.maps.LatLng(pos.coords.latitude, pos.coords.longitude);

                        new google.maps.Geocoder().geocode({'latLng': point}, function (res, status) {
                            if(status == google.maps.GeocoderStatus.OK && typeof res[0] !== 'undefined') {
                                var change = '2.5';
                                var lat1 = pos.coords.latitude;
                                var lat2 = parseFloat(lat1,10) + parseFloat(change,10);
                                if(lat2) {
                                    $("._lat_more").html(lat2);
                                } else fail('Failed to parse');
                                var lat3 = parseFloat(lat1,10) - parseFloat(change,10);
                                if(lat3) {
                                    $("._lat_minus").html(lat3);
                                } else fail('Failed to parse');

                                var long1 = pos.coords.longitude;
                                var long2 = parseFloat(long1,10) + parseFloat(change,10);
                                if(long2) {
                                    $("._long_more").html(long2);
                                } else fail('Failed to parse');
                                var long3 = parseFloat(long1,10) - parseFloat(change,10);
                                if(long3) {
                                    $("._long_minus").html(long3);
                                } else fail('Failed to parse');
                                var zip = res[0].formatted_address.match(/,sw{2}s(d{5})/);

                            } else {
                                fail('Failed to reverse');
                            }
                        });
                    }, function(err) {
                        fail(err.message);
                    }
                );
            } else {
                $("._res").html('Geolocation unsupported!');
            }
            function fail(err) {
                console.log('err', err);
                $("._res").html('Error ' + err);
            }
        });
        </SCRIPT>
<?php
 $lat_plus = '<div class="_lat_more"></div>';
 $lat_minus = '<div class="_lat_minus"></div>';
 $long_plus = '<div class="_long_more"></div>';
 $long_minus = '<div class="_long_minus"></div>

echo $lat_plus
echo $lat_minus
echo $long_plus
echo $long_minus
?>

The problem is trying to bring the JavaScript created values into the WordPress meta_query. I’m guessing that since my variables are basically creating a div that WordPress can’t read the value of the div as a meta value.

Could someone help point me in the right direction?

Related posts

Leave a Reply

1 comment

  1. For me the following worked (from http://www.bluefinengineering.com/blog/wordpress-geolocation-query-wp_query-lat-lng-post-metadata). This filters the posts already in PHP.

    First extend the WP_Query class

    <?php
    
    class WP_Query_Geo extends WP_Query {
      var $lat;
      var $lng;
      var $distance;
    
      function __construct($args=array()) {
        if(!empty($args['lat'])) {
          $this->lat = $args['lat'];
          $this->lng = $args['lng'];
          $this->distance = $args['distance'];
          add_filter('posts_fields', array($this, 'posts_fields'));
          add_filter('posts_groupby', array($this, 'posts_groupby'));
          add_filter('posts_join_paged', array($this, 'posts_join_paged'));
        }
    
        parent::query($args);
    
        remove_filter('posts_fields', array($this, 'posts_fields'));
        remove_filter('posts_groupby', array($this, 'posts_groupby'));
        remove_filter('posts_join_paged', array($this, 'posts_join_paged'));
      }
    
      function posts_fields($fields) {
        global $wpdb;
        $fields = $wpdb->prepare(" ((ACOS(SIN(%f * PI() / 180) * SIN(mtlat.meta_value * PI() / 180) + COS(%f * PI() / 180) * COS(mtlat.meta_value * PI() / 180) * COS((%f - mtlng.meta_value) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) AS distance", $this->lat, $this->lat, $this->lng);
        return $fields;
      }
    
      function posts_groupby($where) {
        global $wpdb;
        $where .= $wpdb->prepare(" HAVING distance < %d ", $this->distance);
        return $where;
      }
    
      function posts_join_paged($join) {
        $join .= " INNER JOIN uc_postmeta AS mtlat ON (IF(mtmaster.meta_value != uc_posts.ID, mtmaster.meta_value, uc_posts.ID) = mtlat.post_id AND mtlat.meta_key = 'lat') ";
        $join .= " INNER JOIN uc_postmeta AS mtlng ON (IF(mtmaster.meta_value != uc_posts.ID, mtmaster.meta_value, uc_posts.ID) = mtlng.post_id AND mtlng.meta_key = 'lng') ";
        return $join;
      }
    }
    

    Then use the new WP_Query_Geo class to query the posts:

    <?php
    $args = array(
        'post_status' => 'publish',
        'posts_per_page' => $per_page,
        'paged' => $paged,
        'fields' => 'all',
        'lat' =>  $distance['lat'],
        'lng' =>  $distance['lng'],
        'distance' => $distance['range']
    );
    
    $query = new WP_Query_Geo( $args );