Calculate distance from user to locations and add results to a page's dataset

I’m not a coder, so go easy on me please.

I am trying to arrange a 100+ store location dataset to sort into a repeater according to the nearest store to a user based on their geolocation. I originally looked at ways to set it up as a wixdata query then feed the results into the repeater, which worked fine when the page loaded, but fell apart as soon as filters or other sorts were applied to the dataset.

So what I am looking to do is when the page first loads calculate and store the distance for each venue in the Distance field of the dataset (not the collection). Then once these calculated distances are in the dataset I can apply data filters and sort the resulting filtered items by nearest location into the repeater, or use the indexed Distance for other calculations on the page.

The idea was on page load to loop through each item in the dataset from 1 to [datasetlength], with each pass using setCurrentItemIndex() and getCurrentItem() to get a single “row” of store location data, calculate its distance and then use setFieldValue() to set the store’s Distance before moving on to the next row.

The dataset is connected to the repeater and all the data filter options via the Editor (not coded), while the sort options are coded to run on a button onClick(). Everything is synced & working perfect except the initial function to store each location’s calculated mDistance in the dataset.

The console logs show that the location data is retrieved correctly and the distance calculation function works fine (kudos to the original author), but instead of looping through each item in the dataset from 1 to [datasetlength], it just repeats the last calculated item in the dataset over and over.

#dataset1: has read/write permissions, and is linked to 100-item collection “ShoppingDB” that includes these field keys:

  • mListing: text, the name of the venue
  • mgps: numeric, the latitude of the venue
  • mgps1: numeric, the longitude of the venue
  • mDistance: numeric, this field is blank in the collection and can be used to store each item’s calculated distance in #dataset1

Here are the relevant pieces of code:

import wixData from 'wix-data';
import wixWindowFrontend from 'wix-window-frontend'; 

$w.onReady(function () {
  $w("#dataset1").onReady( () => {
  //On load, default sort dataset by Nearest
  SortbyNearest()
  });
});

function SortbyNearest(){
wixWindowFrontend.getCurrentGeolocation()
  .then((obj) => {
  let lat1 = obj.coords.latitude; //current latitude of the user
  let long1 = obj.coords.longitude; //current longitude of the user
  let datasetlength = $w("#dataset1").getTotalCount(); 
  console.log("Your coordinates: " + lat1 + " , " + long1);
  console.log("dataset length = " + datasetlength.toString())
  for (var i = 0; i < datasetlength; i++) {
    $w("#dataset1").setCurrentItemIndex(i)
      .then( () => {
        let venue = $w("#dataset1").getCurrentItem();
        let lat2 = venue.mgps; //get venue latitude from dataset
        let long2 = venue.mgps1; //get venue longitude from dataset
        let distance = dist(lat1, long1, lat2, long2); //calculate distance between user and venue
        $w("#dataset1").setFieldValue("mDistance", Number(distance));//Set the current item's distance (but only in the dataset)
        console.log(i + " " + venue.mListing + " (" + distance + " km away)")
      });
  }
  })
}

function dist(lat1, lon1, lat2, lon2) {
 function rad(x) { return x * Math.PI / 180; }

 let R = 6378.137;
 let dLat = rad(lat2 - lat1);
 let dLong = rad(lon2 - lon1);

 let a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(rad(lat1)) * Math.cos(rad(lat2)) * Math.sin(dLong / 2) * Math.sin(dLong / 2);
 let c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
 var d = R * c;

 return d.toFixed(2); // return distance in km to 2 decimal points
}

//END

Once all the distances are in the dataset, the user can click buttons to change the sort parameter of the repeater and whatever filters are applied. Example:

// click button to sort by Nearest

$w("#button189").onClick(() => {
  $w("#dataset1").setSort( 
  wixData.sort()
    .ascending("mDistance") 
  );
})

Datasets themselves aren’t really designed to be used like this. Instead you might want to make a database query and perhaps filter your results using something like between on the latitude/longitude so that you only need to calculate distances for destinations within a reasonable distance.

You can then store those calculations using insert although you’d need to store data for each user and that might mean you’d either:

  • Want a second database to store these calculations on a per-user basis
  • Or just don’t store these calculations and instead calculate distances every time the user visits

Given what’s described I’d probably opt for the second option so it doesn’t involve writing and storing this data for each user. The calculations look pretty light on resources so not sure the complexity tradeoff would be worth it here.

I did try coding it as suggested to query the collection in the CMS (ShoppingDB), calculate and sort the results array by distance, and then feed it into a repeater.

wixData.query(“ShoppingDB”)
.find()
.then((results) => {
let venue = results.items;
for (var i = 0; i < venue.length; i++) {
let lat2 = venue[i].venuegps; //property latitude
let long2 = venue[i].venuemgps1; //property longitude
let distance = dist(lat1, long1, lat2, long2);
venue[i].mDistance = Number(distance); //convert to number format suitable for sort
}
// sort by nearest, plug repeater
venue.sort((a, b) => (a.mDistance > b.mDistance) ? 1 : -1);
$w(“#Repeater”).data = venue;
})

And it works… until the user clicks a button to filter the data. Then it all falls apart, presumably because the query (coded) applies to the source CMS collection (“ShoppingDB”), while the filter buttons (set in Editor) apply to the page’s dataset (“#dataset1”, set in Editor to “ShoppingDB”).

Another problem is the sort by nearest only works when showing the full 100+ item collection in the repeater. If for instance I use .limit(6) on the query, it only sorts the first 6 items from the collection relative to each other, but not the 6 closest items in the entire collection.

Is there any way to make the code work so that it:
-sorts the venues by A-Z on page load
-puts the first 6 in the repeater
-lets users filter the data in the repeater (eg. by category, type or name)
-if user clicks a button sorts the filtered data by nearest?

*My previous code – which aimed on page load to calculate and add a distance to each item in the page’s dataset, not the source collection - didn’t work, but would have had the advantage of making it possible to easily sort whatever was in the repeater by distance, even if multiple filters applied, simply by using wixData.sort().