3

I am stuck into this problem where the main requirement is to find all results from the database which are within 500 miles of the given latitude and longitude. I have stored the lati and longi into the post_meta along with the zip code of those latitude and longitude now the idea is that when ever user searches for any zip code and select the miles from the drop down all the results matching that query should come for instance.

User makes a query like this

500 mi of 51310

The query should return all the results which are within 500 mi of the zip code 51310.

Here's what I have done so far. This is the main function that is actually making the query to the database.

if (isset($_GET['s']) && !empty($_GET['s'])) {

        $searchterm = isset($q->query_vars['s']) ? sanitize_text_field($q->query_vars['s']) : '';

        // we have to remove the "s" parameter from the query, because it will prevent the posts from being found
        $q->query_vars['s'] = '';

        if ($searchterm != '') {

            $search_radius = $this->geocode($searchterm);

            if (!$search_radius) {
                return false;
            }

            $lat = $search_radius['lat']; // get the lat of the requested address
            $lng = $search_radius['lng']; // get the lng of the requested address
            // we'll want everything within, say, 30km distance
            $distance = isset($_GET['within']) && !empty($_GET['within']) ? floatval($_GET['within']) : 50;

            // earth's radius in km = ~6371
            $radius = auto_listings_metric() == 'yes' ? 6371 : 3950;

            // latitude boundaries
            $maxlat = $lat + rad2deg($distance / $radius);
            $minlat = $lat - rad2deg($distance / $radius);

            // longitude boundaries (longitude gets smaller when latitude increases)
            $maxlng = $lng + rad2deg($distance / $radius / cos(deg2rad($lat)));
            $minlng = $lng - rad2deg($distance / $radius / cos(deg2rad($lat)));

            // build the meta query array
            $radius_array = array(
                'relation' => 'AND',
            );

            $radius_array[] = array(
                'key' => '_al_listing_lat',
                'value' => array($minlat, $maxlat),
                'type' => 'DECIMAL(10,5)',
                'compare' => 'BETWEEN',
            );
            $radius_array[] = array(
                'key' => '_al_listing_lng',
                'value' => array($minlng, $maxlng),
                'type' => 'DECIMAL(10,5)',
                'compare' => 'BETWEEN',
            );

            return apply_filters('auto_listings_search_radius_args', $radius_array);

And to find the geocode I have written this function.

$address = urlencode(esc_html($address));

    // google map geocode api url
    $url = auto_listings_google_geocode_maps_url($address);

    $arrContextOptions = array(
        "ssl" => array(
            "verify_peer" => false,
            "verify_peer_name" => false,
        ),
    );

    // get the json response
    $resp_json = file_get_contents($url, false, stream_context_create($arrContextOptions));

    // decode the json
    $resp = json_decode($resp_json, true);

    //pp( $resp );
    // response status will be 'OK', if able to geocode given address 
    if ($resp['status'] == 'OK') {

        // get the lat and lng
        $lat = $resp['results'][0]['geometry']['location']['lat'];
        $lng = $resp['results'][0]['geometry']['location']['lng'];

        // verify if data is complete
        if ($lat && $lng) {

            return array(
                'lat' => $lat,
                'lng' => $lng,
            );
        } else {
            return false;
        }
    } else {
        return false;
    }

If I do not select the within field this code returns the exact results of that particular zip code which is good but when I select within field it displays nothing. Can someone tell me what in this code I am doing wrong.

11
  • use just 'DECIMAL' for meta query 'type'
    – inarilo
    Jan 3 '18 at 9:19
  • That doesn't help bro
    – Mohsin
    Jan 3 '18 at 10:05
  • it's sis ;) how are you storing the long lat values? they should not be stored as arrays
    – inarilo
    Jan 3 '18 at 10:34
  • Oh extremely sorry sis, I am not storing them as an array the real flow is that when user search for any zip code I fetch the lat and lon stored against that zip code and the rest of the algo is in my question
    – Mohsin
    Jan 3 '18 at 10:38
  • looks like 's' is not being unset
    – inarilo
    Jan 3 '18 at 11:01
1

The problem was when I send the query to the database it was changing my array level other than that the 's' value was not going empty due to which it was preventing my records to be fetched and the third and most important one was the way I was making my query by changing my function resolved my problem.

Here is the updated code for this logic in case some one else need to make such functionality.

The main function where all the data is gathering up and making a query

public function pre_get_posts($q) {

    // check if the user is requesting an admin page 
    if (is_admin() || !$q->is_main_query())
        return;

    if (!is_post_type_archive('auto-listing'))
        return;

    if (!is_search())
        return;

    $meta_query = array();

    $year_query[] = $this->year_query();
    $model_query[] = $this->model_query();
    $condition_query[] = $this->condition_query();
    $odometer_query[] = $this->odometer_query();
    $price_query[] = $this->price_meta_query();
    $body_type_query = $this->body_type_query();
    $country_query = $this->country_query();
    $transmission_query = $this->transmission_query();
    $radius_query[] = $this->radius_query($q);

    $query_1 = array_merge($country_query, $year_query, $model_query, $condition_query, $price_query, $odometer_query, $transmission_query);
    // if our radius query fails, fall back to keyword searching
    // will fail with no map API key
    if (empty($radius_query[0]) || !$radius_query[0]) {
        $keyword_query[] = $this->keyword_query($q);
        $query_2 = $keyword_query;
    } else {
        $query_2 = $radius_query;
    }

    // if no keyword
    if (empty($_GET['s'])) {
        $query_1['relation'] = 'AND';
        $meta_query[] = $query_1;
    }

    // if keyword
    if (!empty($_GET['s'])) {
        $query_2['relation'] = 'OR';
        $meta_query[] = $query_1;
        $meta_query[] = $query_2;
        $meta_query['relation'] = 'AND';
    }

    $q->set('meta_query', $meta_query);

    $q->set('tax_query', $body_type_query);

    $q->set('post_type', 'auto-listing');

}

Radius query function that was actually doing the magic

public function radius_query($q) {

    if (isset($_GET['s']) && !empty($_GET['s'])) {

        $searchterm = isset($q->query_vars['s']) ? sanitize_text_field($q->query_vars['s']) : '';

        // we have to remove the "s" parameter from the query, because it will prevent the posts from being found
        $q->query_vars['s'] = '';

        if ($searchterm != '') {

            $search_radius = $this->geocode($searchterm);

            if (!$search_radius)
                return false;

            $lat = $search_radius['lat']; // get the lat of the requested address
            $lng = $search_radius['lng']; // get the lng of the requested address
            // we'll want everything within, say, 30km distance
            $distance = isset($_GET['within']) && !empty($_GET['within']) ? floatval($_GET['within']) : 0.1;

            // earth's radius in km = ~6371
            $radius = auto_listings_metric() == 'yes' ? 6371 : 3950;

            // latitude boundaries
            $maxlat = $lat + rad2deg($distance / $radius);
            $minlat = $lat - rad2deg($distance / $radius);

            // longitude boundaries (longitude gets smaller when latitude increases)
            $maxlng = $lng + rad2deg($distance / $radius / cos(deg2rad($lat)));
            $minlng = $lng - rad2deg($distance / $radius / cos(deg2rad($lat)));

            // build the meta query array
            $radius_array = array(
                'relation' => 'AND',
            );

            $radius_array[] = array(
                'key' => '_al_listing_lat',
                'value' => array($minlat, $maxlat),
                'type' => 'DECIMAL(10,5)',
                'compare' => 'BETWEEN',
            );
            $radius_array[] = array(
                'key' => '_al_listing_lng',
                'value' => array($minlng, $maxlng),
                'type' => 'DECIMAL(10,5)',
                'compare' => 'BETWEEN',
            );

            return apply_filters('auto_listings_search_radius_args', $radius_array);
        }
    }
}

And now the final function that was fetching the lat and long from the zip code that user enters

private function geocode($address) {
    // url encode the address
    $address = urlencode(esc_html($address));

    // google map geocode api url
    $url = auto_listings_google_geocode_maps_url($address);

    $arrContextOptions = array(
        "ssl" => array(
            "verify_peer" => false,
            "verify_peer_name" => false,
        ),
    );

    // get the json response
    $resp_json = file_get_contents($url, false, stream_context_create($arrContextOptions));

    // decode the json
    $resp = json_decode($resp_json, true);

    //pp( $resp );
    // response status will be 'OK', if able to geocode given address 
    if ($resp['status'] == 'OK') {

        // get the lat and lng
        $lat = $resp['results'][0]['geometry']['location']['lat'];
        $lng = $resp['results'][0]['geometry']['location']['lng'];

        // verify if data is complete
        if ($lat && $lng) {

            return array(
                'lat' => $lat,
                'lng' => $lng,
            );
        } else {
            return false;
        }
    } else {
        return false;
    }
}

In order to find post based on this answer one must have the zip code stored into the post_meta table to fetch the lati and longi and the formula that is given in the code to find the max and min boundaries will get the lat and lng itself. I hope I gave the answer in detail if not please feel free to write comments.

Not the answer you're looking for? Browse other questions tagged or ask your own question.