Skip Navigation

[Resolved] Sorting Orderby Distance

The Toolset Community Forum is closed, for technical support questions, please head on to our Toolset Professional Support (for paid clients), with any pre-sale or admin question please contact us here.
This support ticket is created 6 years, 11 months ago. There's a good chance that you are reading advice that it now obsolete.
This is the community support forum for Types plugin, which is part of Toolset. Toolset is a suite of plugins for developing WordPress sites without writing PHP.

Everyone can read this forum, but only Toolset clients and people who registered for Types community support can post in it.

Sun Mon Tue Wed Thu Fri Sat
8:00 – 12:00 8:00 – 12:00 8:00 – 12:00 8:00 – 12:00 8:00 – 12:00 - -
13:00 – 17:00 13:00 – 17:00 13:00 – 17:00 13:00 – 17:00 13:00 – 17:00 - -

Supporter timezone: America/New_York (GMT-04:00)

This topic contains 15 replies, has 3 voices.

Last updated by jesseM-3 6 years, 11 months ago.

Assisted by: Christian Cox.

Author
Posts
#595325

Hi,
i need help with my custom plug-in.

I made some pluigns to calculate distance via shortcode. With these shortcode i can:

- Retrive user latitude & longitude (user-lat, user-lon)
- Add coordinates as custom field with the hook save_post (location-lat, location-lon)
- Calculate distance via shortcode with the 4 attributes (user lat, user lon, location-lat, location-lon)

Now i just need to order posts in my views by distance. What is the best way to make it compatible with toolset plugins ?

#595402

Hi, Views uses the standard WP_Query from WordPress to perform its searches. WordPress doesn't include the ability to sort query results by a value calculated on-the-fly from post meta data, and Views is not able to add that functionality just yet. The parameters that you can use to order a query are listed here:
https://codex.wordpress.org/Class_Reference/WP_Query#Order_.26_Orderby_Parameters

Maps 1.4 will include built-in distance filters that don't require custom code, but it's not quite ready yet and I don't have a timeline for its availability. Right now, the only way I can think of to accomplish what you want is to use the wpv_filter_query_post_process filter to reorder the results of your query using the calculation you have already implemented in your distance shortcode. Note that this approach will not work with paginated results, because each page of results will be sorted independently of the results on other pages. With that being said, here's the documentation for the query post process filter:
https://toolset.com/documentation/programmer-reference/views-filters/#wpv_filter_query_post_process

And here's a sample of how you can use PHP usort to sort the array of results:

add_filter( 'wpv_filter_query_post_process', 'distance_sort_query_func', 10, 3 );
function distance_sort_query_func( $query, $view_settings, $view_id ) {
  if ( !empty( $query->posts ) &&  $view_id == 12345) {
    usort($query->posts, "calculated_distance_cmp");
  }
  return $query;
}
function calculated_distance_cmp($a, $b)
{
  $along = get_post_meta($a->ID, 'wpcf_longitude', true);
  $alat = get_post_meta($a->ID, 'wpcf_latitude', true);
  $blong = get_post_meta($b->ID, 'wpcf_longitude', true);
  $blat = get_post_meta($b->ID, 'wpcf_latitude', true);
  $user_long = ?; // not sure how you get this
  $user_lat = ?;   // not sure how you get this
  $res = 0;

  // add your code here to 
  // calculate distance a and distance b 
  $dista = ?;
  $distb = ?;
  
  
  if ( $dista < $distb ) {
     $res = 1;
  }
  return $res;
}

Change 12345 to match your View's ID, then change the lat and long values as needed and add your distance calculation code.

#595430

Hi Christian!
Thank you for your help. I should only need to know what do you mean for distance a or distance B. What is The differente?

#595437

Sure, the documentation for PHP usort might be helpful here:
hidden link

When you usort an array of results, the callback function calculated_distance_cmp is called repeatedly to compare each result with other results. The return value of this function is either 0 or 1 to indicate whether post A should be sorted before post B. $a and $b represent the two posts currently being compared. So the calculations will be basically identical for both $dista and $distb, but the variables you use in those calculations will be different. $dista will be calculated using $along and $alat (long and lat for post $a), but $distb will be calculated using $blong and $blat (long and lat for post $b).

#595440

Sorry but l still don’t understand.
The distance beetween Two post is just One

I have to calcutate distance beetween userlocation and post ordering The view ASC by distance value

#595445

I will try to clarify.

The problem is that the distance from a User to any post is not constant. Distances must be calculated using the current User's location and the current post location. Distance will be different for each User, for each post. This means that you will not store distances in the database - they are calculated on-the-fly. There is no way to directly sort the results by distance, so we need a different sorting method.

Assume your algorithm to determine distance is very simple:

distance = (post lat + post long) / (userlat + userlong)

Your real algorithm is much more complex but will include these variables:

  • post latitude
  • post longitude
  • user latitude
  • user longitude
  • In order to sort a list of results by distance, you must calculate the distance for each result and compare the distances of all the results against each other. That means you must calculate each distance using the proper post latitudes and post longitudes. For example:
    distance to post 1 = (post 1 lat + post 1 long) / (userlat + userlong)
    distance to post 2 = (post 2 lat + post 2 long) / (userlat + userlong)
    distance to post 3 = (post 3 lat + post 3 long) / (userlat + userlong)
    distance to post 4 = (post 4 lat + post 4 long) / (userlat + userlong)
    ...and so on for all posts...

    To sort a list by calculation in PHP, you need to use usort. The usort callback function compares each distance with each other distance by repeatedly comparing only two distances at a time using the algorithm you provide, until all the results have been sorted. Here is a sample of the usort function logic:

    First comparison:
    distance 1 = (post 1 lat + post 1 long) / (userlat + userlong)
    distance 2 = (post 2 lat + post 2 long) / (userlat + userlong)
    is distance 1 greater than distance 2?
    if so, distance 1 is first. if not, distance 2 is first.

    Second comparison
    distance 2 = (post 2 lat + post 2 long) / (userlat + userlong)
    distance 3 = (post 3 lat + post 3 long) / (userlat + userlong)
    is distance 2 greater than distance 3?
    If so, distance 2 is first. if not, distance 3 is first.

    Third comparison
    distance 3 = (post 3 lat + post 3 long) / (userlat + userlong)
    distance 4 = (post 4 lat + post 4 long) / (userlat + userlong)
    is distance 3 greater than distance 4?
    If so distance 3 is first. If not, distance 4 is first.

    ... and so on until all the results have been compared with all the other results and the sort order has been determined. This is how usort compares a set of results using a calculation. So you must provide an algorithm to calculate distance to post "a" and to calculate distance to post "b". Your algorithm must support variables - different latitude and longitude for each post. In my code, these are represented by the variables $along, $alat, $blong, $blat. Our simple algorithm becomes:

      $along = get_post_meta($a->ID, 'wpcf_longitude', true);
      $alat = get_post_meta($a->ID, 'wpcf_latitude', true);
      $blong = get_post_meta($b->ID, 'wpcf_longitude', true);
      $blat = get_post_meta($b->ID, 'wpcf_latitude', true);
      $user_long = ?; // not sure how you get this
      $user_lat = ?;   // not sure how you get this
      $res = 0;
     
      $dista = ($alat + $along) / ($user_lat + $user_long); // replace this with the correct algorithm using $alat and $along
      $distb = ($blat + $blong) / ($user_lat + $user_long); // replace this with the correct algorithm using $blat and $blong
       
      if ( $dista < $distb ) {
         $res = 1;
      }
      return $res;
    
    #595722

    Hi Christian!!

    I really want to thank you about your help! Finally i success!!

    If somebody need my code i can give it free. It lasted 2 months because i'm not a programmer.

    Is there any kind of best practice about to retrive geolocation? Because I actually made it putting them as parameters in the url to retrive them via GET.

    #595928

    Great!! I'm not aware of any best practices for geolocation, unfortunately. As an alternative that does not use URL parameters, you could use hidden fields in the search form filters. Use JavaScript to set these values based on the User's geolocation information, then you can access those values in the $_POST superglobal.

    #596206

    Hi Christian,
    it works, but i noticed that enabling this function it affects all other views order. Disabling, other views works my code is:

    add_filter( 'wpv_filter_query_post_process', 'distance_sort_query_func', 10, 3 );
    //4614
    function distance_sort_query_func( $query, $view_settings, $view_id ) {
      if ( !empty( $query->posts ) && ($view_id == 4734 or $view_id=4614) ) {
        usort($query->posts, "calculated_distance_cmp");
      }
      return $query;
    }
    #596364

    Looks like a typo:

    $view_id=4614
    

    You should use "==" to test equivalency.

    #596845

    WOW IT WORKS!

    Can i use this for generating json via wp-rest api of wordpress ?

    #596934

    Check out this REST API integration that was started but not yet implemented in our core code:
    hidden link
    This isn't something we provide support for here in the forum, but it might be useful.

    Right now, we offer the render_view function in PHP to access the results of any View in code. Click the orange "+ More" buttons for examples:
    https://toolset.com/documentation/programmer-reference/views-api/#render_view

    #596939

    Also, I am excited to announce that Maps 1.4 has been released! This update includes distance-based custom searches without the need for custom code, so it seems like it might be relevant for your project.

    #597462

    Hallo, i would love to use toolset function to retrive coordinates, but actually maps does not have a documentation about them?

    I think:
    - It does not order them by distance as i've seen
    - It is thinked for displayng markers

    If i'm wrong it could be usefull to retrive my posts ordered by distance, and use pagination. It is possibile ?

    I also would like to use wp-types to make a web application but i need to understand how can i do thanks to "render_view" to make this in wp-rest api

    #597755

    Check out our blog post for more information about Maps 1.4:
    https://toolset.com/2017/12/toolset-maps-1-4-with-geolocation-filtering-by-distance-and-custom-map-styles/
    - You can order posts by distance from the current User (with the User's permission), or by distance from a static location.
    - You can predefine distance filter criteria, or allow the User to select criteria on the front-end.
    - You can use a map to show the results, and Views pagination is available to paginate the results.

    Regarding render_view, this function can be used anywhere in PHP to render the results of a View. However, integrating it with the REST API would require your own custom code. The repo I linked to in Github provides REST API endpoints for rendering the results of a View without the need for render_view.

    The forum ‘Types Community Support’ is closed to new topics and replies.