0

I'm doing an Json call to retrieve an a list of locations with information details for each location. longitude and latitude are included in this info.

I am using Google's distance matrix api to get the distance from coordinates provided as a query in the url.

I thought the most logical way to do this would be to loop through the locations and compare searched and location coordinates per location. I am appending some of the info returned from the distance matrix calls onto my data array.

This works fine except the data returned from the distance matrix is only usable within the return function for the distance matrix. I pass the wanted data from the distance matrix return function into a function outside the matrix return function but still inside the $.each loop. This seamed like a step towards solving my problem but I'm stuck.

I need to use the original data but with the distance matrix data appended so that I can sort the items in the data by distance and output it. I don't think a sort function would work well inside of the $.each loop so I need to break out of the loop with the appended version of the data intact. at least I think. any help would be appreciated.

var ltlg = [];
var sLat = parseFloat(35.2219971);
var sLng = parseFloat(-101.83129689999998);
var sLatLng = {lat: sLat, lng: sLng};

var locLat;
var locLng;
var locLatLng = [];

$.getJSON("file.json",function(data){

    if(sLat && sLng) {

        $.each(data, function(x, y){

            locLat = data[x].Address.Location.Latitude;
            locLng = data[x].Address.Location.Longitude;

            //locLatLng.push({lat: locLat, lng: locLng});

            var service = new google.maps.DistanceMatrixService;

            service.getDistanceMatrix({
                origins: [sLatLng],
                destinations: [{lat: locLat, lng: locLng}],
                travelMode: 'DRIVING',
                unitSystem: google.maps.UnitSystem.IMPERIAL,
                avoidHighways: false,
                avoidTolls: false
            }, function(response, status) {
                if (status !== 'OK') {          
                    console.log('status is ' + status);        
                } else {
                    var distance = response.rows[0].elements[0].distance.value;
                    var miles = response.rows[0].elements[0].distance.text;
                    var time = response.rows[0].elements[0].duration.text;

                    addData(distance, miles, time);
                }

            });

            function addData(distance, miles, time) {
                data[x]["distance"] = distance;
                data[x]["miles"] = miles;
                data[x]["time"] = time;
            }

        });

        console.log(JSON.stringify(data));

    } else {
        console.log('no query');
    }

});

I replaced the query variables with static lat lng for the snippet.

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
JSum
  • 597
  • 9
  • 20
  • Why dont you use Array.map/forEach/filter ? – Jonas Wilms Jul 02 '17 at 09:44
  • @Jonasw I don't know how to do that. Could you give me an example? – JSum Jul 02 '17 at 09:45
  • Please do not vandalize your posts. By posting on the Stack Exchange network, you've granted a non-revocable right for SE to distribute that content (under the [CC BY-SA 3.0 license](https://creativecommons.org/licenses/by-sa/3.0/)). By SE policy, any vandalism will be reverted. If you would like to disassociate this post from your account, see [What is the proper route for a disassociation request?](https://meta.stackoverflow.com/q/323395) – Bugs Jul 02 '17 at 10:24
  • @JSum unfortunately, Stack Exchange keeps a version history of all edits for all questions and answers. To my knowledge, there is no way to 100% redact sensitive information once it has been posted on the site, unless contacting the site owners and requesting manual removal of the history is possible, but that I am not sure of. – Patrick Roberts Jul 03 '17 at 16:05

2 Answers2

0

You may want to implement a global callback, sth like:

function loaddata(callback){
  var counter=0;
  data.forEach(function(y,x){
    //get data
   function addData(distance, miles, time) {
     data[x]["distance"] = distance;
     data[x]["miles"] = miles; 
     data[x]["time"] = time;
     if(++counter==data.length){
      callback(data);
     }
   }
  });
}

So you can do:

loaddata(function(data){
 console.log(data);
});

Data now contains distance,miles and time.

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • I would really like to know why that worked. can you point me in the direction of documentation about this type of function? – JSum Jul 02 '17 at 10:02
  • @JSum https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call – Jonas Wilms Jul 02 '17 at 10:27
0

The answer that @Jonasw has provided is unnecessarily complicated, and because a new addData() is declared for every single iteration, and a completely pointless counter is initialized, this consumes a lot of memory that can be conserved with a smarter approach. Since you have asynchronous callbacks for every data point, and you want to depend on all of them completing, the approach that makes the most sense would utilize Promise.all():

var sLat = 35.2219971;
var sLng = -101.83129689999998;
var sLatLng = {
  lat: sLat,
  lng: sLng
};

var service = new google.maps.DistanceMatrixService;

function serviceCallback(value, resolve, response, status) {
  if (status !== 'OK') {
    console.log('status is ' + status);

    reject(status);
  } else {
    value.distance = response.rows[0].elements[0].distance.value;
    value.miles = response.rows[0].elements[0].distance.text;
    value.time = response.rows[0].elements[0].duration.text;

    resolve(value);
  }
}

function promiseExecutor(value, resolve, reject) {
  service.getDistanceMatrix({
    origins: [sLatLng],
    destinations: [{
      lat: value.Address.Location.Latitude,
      lng: value.Address.Location.Longitude
    }],
    travelMode: 'DRIVING',
    unitSystem: google.maps.UnitSystem.IMPERIAL,
    avoidHighways: false,
    avoidTolls: false
  }, serviceCallback.bind(null, value, resolve));
}

$.getJSON("file.json", function (data) {
  if (![sLat, sLng].some(isNaN)) {
    Promise.all(data.map(function (value) {
      return new Promise(promiseExecutor.bind(null, value));
    })).then(function (data) {
      console.log(JSON.stringify(data, null, 2));
    });
  } else {
    console.log('no query');
  }
});

However, I see you are making a request for each individual data point when you are able to make one for every single data point at once, so to save even more resources, you can try something like this:

var sLat = 35.2219971;
var sLng = -101.83129689999998;
var sLatLng = {
  lat: sLat,
  lng: sLng
};

var service = new google.maps.DistanceMatrixService;

$.getJSON("file.json", function (data) {
  if (![sLat, sLng].some(isNaN)) {
    service.getDistanceMatrix({
      origins: [sLatLng],
      destinations: data.map(function (value) {
        return {
          lat: value.Address.Location.Latitude,
          lng: value.Address.Location.Longitude
        };
      }),
      travelMode: 'DRIVING',
      unitSystem: google.maps.UnitSystem.IMPERIAL,
      avoidHighways: false,
      avoidTolls: false
    }, function (response, status) {
      if (status !== 'OK') {
        console.log('status is ' + status);
      } else {
        response.rows[0].elements.forEach(function (element, index) {
          if (element.status === 'OK') {
            data[index].distance = element.distance.value;
            data[index].miles = element.distance.text;
            data[index].time = element.duration.text;
          }
        });

        console.log(JSON.stringify(data, null, 2));
      }
    });
  } else {
    console.log('no query');
  }
});
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153