Support

Account

Home Forums Front-end Issues WP Query closest locations by ACF Google Map values

Solved

WP Query closest locations by ACF Google Map values

  • Hi folks,
    Working on a project lately where I have a custom post type, say its called Locations.

    On the single Locations pages, I’m hoping to show an archive of the 3 closest other locations using WP_Query. Each location has location data saved in an ACF Google Map field called map_pin.

    I’ve been struggling to sort this one out. Anyone dealt with this?

  • To do that you would need to have a database full of locations or do the math to do all the calculations based on the longitude and latitude. Without some type of database you’d need to get all the posts/fields and do the calculations 1 at a time? Sorry I can’t give you more help than that https://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula.

    I use the pro/gold version of WP Google Maps, it does all of this for me https://www.wpgmaps.com/purchase-professional-version/

  • So, I posted a response with the solution but the site seems to have deleted it. Here it is again:

    John, thanks for the response. You were correct in your assessment. Unfortunately WP Google Maps was not possible for this project, although I do use that plugin frequently (it’s great).

    My Coworker Steve was able to solve this particular issue for me. I’ll post that solution below:

    So the goal here was to display an archive of the closest 3 locations on the single page of the custom post type in question. Each post is using the ACF Google Map field and has associated Google Map data.

    in functions.php (or mu-plugin):

    function get_distance($lat1, $lon1, $lat2, $lon2) {
        $theta = $lon1 - $lon2;
        $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +  cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
        $dist = acos($dist);
        $dist = rad2deg($dist);
        $dist = $dist * 60 * 1.1515;
        return $dist;
    }

    In the custom post type single page:

    <?php 
    $current_post = get_the_ID();
    $location = get_field('map_pin'); // Adjust to your Google Map field name
    $current_post_lat = $location['lat'];
    $current_post_long = $location['lng'];
    $query = new WP_Query( array(
        'post_type' => 'post-type-name',
        'posts_per_page' => -1,
        'post__not_in' => array($current_post)
      )
    );
    $post_1_dist = 100;
    $post_1 = "0";
    $post_2_dist = 101;
    $post_2 = "0";
    $post_3_dist = 102;
    $post_3 = "0";
    while ($query->have_posts()) : $query->the_post(); 
    $location = get_field('map_pin');
    $dist = get_distance($current_post_lat, $current_post_long, $location['lat'], $location['lng']);
    if ($dist < $post_1_dist){
      $post_3 = $post_2;
      $post_3_dist = $post_2_dist;
      $post_2 = $post_1;
      $post_2_dist = $post_1_dist;
      $post_1 = get_the_ID();
      $post_1_dist = $dist;
    }
    if (($dist < $post_2_dist) && ($dist > $post_1_dist)){
      $post_3 = $post_2;
      $post_3_dist = $post_2_dist;
      $post_2 = get_the_ID();
      $post_2_dist = $dist;
    }
    if (($dist < $post_3_dist) && ($dist > $post_1_dist) && ($dist > $post_2_dist)){
      $post_3 = get_the_ID();
      $post_3_dist = $dist;
    }
    endwhile; wp_reset_postdata(); ?>
    
    //Results
    <?php echo get_the_title($post_1); ?>
    <?php echo get_the_title($post_2); ?>
    <?php echo get_the_title($post_3); ?>
  • This works, but has the downside that you have to go through all locations. If you have thousands of locations, this could be a significant resource-hog.

  • Adding another response to subscribe to updates.

  • @alan-brown @mastababa this should help out as a reference and working solution / starting point (and finish there as well most likely). Either with the WP_Query or the rest api Reference link

  • @mastababa If this needed to be done for thousands of locations what I would do would be to create an action that started a CRON job that would do all of the cacluations. I would likely create some type of complex array of data that would store all of the needed information to allow for easy retrieval of the information. How that might look would depend on what I wanted to do.

    Let’s say that I wanted to do what the OP was doing and show the closes 3 other locations. My array would probably look like this.

    
    $option = array(
      // each element of this array is a post ID => array of closest 3 posts
      1234 => array(1567, 7657, 5654),
      // etc
    )
    

    You can then just grab the post ID’s to get from this array.

    Doing the CRON that does all the indexing would be beyond my ability to help here.

  • Yeah, a cron job and a big lookup table would be the fastest. But this would not work for calculating nearby locations for random locations. Say, the location, as reported by the browser, of the current user.

    For another project, I’m using a quick and dirty solution:

    I take the location for which I want to retrieve nearby posts and round the latitude and longitude to the nearest degree. Then I search for posts with a location that match latitude and longitude, or one degree lower.
    So, I end up with all posts in the same degree ‘square’ and three nearby ‘squares’. Then, if needed, I can perform a closest neighbour calculation on this subset of posts, and ignore those that are too far away (and are perhaps false positives).

    Perhaps to clarify, this is my argument list:

    $args = array(
    	"post_type"			=> "any",
    	"order"				=> "RAND",
    	"post__not_in"		=> array(get_the_ID()),
    	"meta_query"		=> array(
    		'relation'		=> "OR",
    		array(
    			"relation"	=> "AND",
    			array(
    				"key"	=> "location",
    				"value"	=> '"'.$law.'.',
    				"compare"	=> "LIKE"
    			),
    			array(
    				"key"	=> "location",
    				"value"	=> '"'.$low.'.',
    				"compare"	=> "LIKE"
    			),
    		),
    	)
    );
  • In the case of finding a location close to a variable location then you’d need a lookup table that stores the longitude and latitude for each post then you can loop over this table to calculate distances for each and find the ones you want. You’d still need to loop over every location but you would not need to retrieve every post to do it which would be much faster. You could also store the results in a transient so you did not need to do the same lookup if it was already done.

    
    $locations = array(
      $post_id => array('lon' => $lon, 'lat' => $lat)
    )
    

    In this case you would not really need a cron as you could just update the values for each post when saved, but it would mean that you’d also probably want to remove them when a post is deleted to prevent the table from being larger than necessary. Possible delete any transients if you decide to use them as well.

Viewing 9 posts - 1 through 9 (of 9 total)

You must be logged in to reply to this topic.