3

Querying Nearby Locations in WordPress using Geo Coordinates

 2 years ago
source link: https://thomaspark.co/2019/09/querying-nearby-locations-in-wordpress-using-geo-coordinates/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Querying Nearby Locations in WordPress using Geo Coordinates

In the course of developing Campus Arrival, a WordPress site that provides school-specific packing lists, we added a feature to suggest nearby universities. When viewing a school’s checklist, this feature displays other, nearby colleges. When viewing the front page, it suggests colleges near the visitor’s own location. For example, check out the suggestions for MIT, UCLA, or your own location.

Campus Arrival nearby schools

This was implemented in WordPress by querying nearby schools based on geo coordinates, i.e., latitude and longitude. (Our first pass used a custom taxonomy with in-state schools, but we knew the results could be improved.)

If you’d like to do something similar, follow the steps in this guide.

Post Meta

Start by adding custom post meta fields for latitude and longitude. The WP plugin Advanced Custom Fields is a godsend for creating nice admin-facing interfaces to manage custom post meta.

The Hooks

First we start with the PHP code. The meat of the implementation is this function, cobbled together from this comment on the WordPress StackExchange, that accepts a set of coordinates, along with a few other parameters like the max search distance and number of posts to return, and returns the posts with the nearest latitude and longitude.


function query_neighbors( $latitude, $longitude, $post_id = -1, $distance = 400, $limit = 6 ) {
  global $wpdb;
  $earth_radius = 3959; // miles

  $sql = $wpdb->prepare( "
    SELECT DISTINCT
        p.ID,
        p.post_title,
        map_lat.meta_value,
        map_lng.meta_value,
        ( %d * acos(
        cos( radians( %s ) )
        * cos( radians( map_lat.meta_value ) )
        * cos( radians( map_lng.meta_value ) - radians( %s ) )
        + sin( radians( %s ) )
        * sin( radians( map_lat.meta_value ) )
        ) )
        AS distance
    FROM $wpdb->posts p
    INNER JOIN $wpdb->postmeta map_lat ON p.ID = map_lat.post_id
    INNER JOIN $wpdb->postmeta map_lng ON p.ID = map_lng.post_id
    WHERE p.post_type = 'school'
        AND p.post_status = 'publish'
        AND p.ID != %d
        AND map_lat.meta_key = 'latitude'
        AND map_lng.meta_key = 'longitude'
    HAVING distance < %s
    ORDER BY distance ASC
    LIMIT %d",
    $earth_radius,
    $latitude,
    $longitude,
    $latitude,
    $post_id,
    $distance,
    $limit
  );

  $neighbors = $wpdb->get_results( $sql );

  return $neighbors;
}

This function, along with the AJAX hook below, should be added to your custom plugin or functions.php. In the “do your thing” section, construct your markup, perhaps using a template.


function process_neighbors() {
  check_ajax_referer( 'neighbors_validation', 'security');
  $location = $_POST['location'];
  $url = wp_get_referer();
  $post_id = url_to_postid( $url );

  if ( isset( $location ) ) {
    $neighbors = query_neighbors( $location['latitude'], $location['longitude'], $post_id, 400, 6 );
    $response = '';

    if ( $neighbors ) {
      foreach( $neighbors as $neighbor ) {
        setup_postdata( $neighbor );

        // do your thing

        wp_reset_postdata();
      }
    }

    if ( strlen( trim( $response ) ) > 0 ) {
      wp_send_json_success( $response );
    } else {
      wp_send_json_error();
    }
  }

  die();
}
add_action( 'wp_ajax_nopriv_post_neighbors', 'process_neighbors' );
add_action( 'wp_ajax_post_neighbors', 'process_neighbors' );

Next, enqueue the JS script that you’ll be creating in the next section.


function load_custom_js() {
  $post_id = get_the_ID();
  $location = array(
    'latitude' => get_post_meta( $post_id, 'latitude', true ),
    'longitude' => get_post_meta( $post_id, 'longitude', true ),
    'post_id' => $post_id
  );

  $url = plugins_url( 'scripts/neighbors.js', __FILE__ );

  wp_enqueue_script( 'neighbors_js', $url, array( 'jquery' ), '', true );
  wp_localize_script( 'neighbors_js', 'ajax_neighbors_object', array(
    'ajax_url' => admin_url( 'admin-ajax.php' ),
    'nonce' => wp_create_nonce( 'neighbors_validation' ),
    'location' => $location
  ));
}
add_action( 'wp_enqueue_scripts', 'load_custom_js' );

The Script

Now we write the JavaScript code that makes the AJAX call to the PHP code to find nearby neighbors, then formats the results and appends them to the current page.

Start with a function that takes a location and sends a POST request to the hook above.


async function postNeighbors(location) {
  let response;

  try {
    response = await $.ajax({
      type: 'POST',
      dataType: 'json',
      url: ajax_neighbors_object.ajax_url,
      data: {action: 'post_neighbors', security: ajax_neighbors_object.nonce, location: location}
    });

    return response;
  } catch (error) {
    console.error(error);
  }
}

To compare with another school, call postNeighbors like so. In the doYourThing function, you’ll handle the markup you got in your response, perhaps injecting it into the page and sliding it into view.


postNeighbors(ajax_neighbors_object.location)
  .then(({data})=>doYourThing(data));

To compare with the user’s own location, you just need to add one more step, making use of the handy WordPress geo API.


getUserLocation()
  .then(postNeighbors)
  .then(({data})=>doYourThing(data));

async function getUserLocation() {
  const response = await fetch('https://public-api.wordpress.com/geo/');
  const json = await response.json();

  return json;
}

Check out Campus Arrival to see all this in action.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK