2

In the below code, I am not able to access the value of variable distances . I think that is because of asynchronous call directionsService.route. How can I get the value variable distances ?

  var totalDistance;
  var distances = new Array();
  var directionsDisplay;
  var directionsService = new google.maps.DirectionsService();
  var map;
  var start = "ABC XYZ";
  var end ; 
  var points = new Array("Location ABC", "Location PQR", "Location XYZ", "Location more", "And     Some other location");

  function initialize() {
    directionsDisplay = new google.maps.DirectionsRenderer();
    var mapOptions = {
      center: new google.maps.LatLng(13.0604220, 80.2495830),
      zoom: 10,
      mapTypeId: google.maps.MapTypeId.ROADMAP,
      draggableCursor: "crosshair"
    }; 
    map = new google.maps.Map(document.getElementById("map-canvas"),
        mapOptions);
    directionsDisplay.setMap(map);
  }
  function calcRoute() {
  for(var j=0;j<points.length;j++)
  {

    end = points[j];
    var waypoints = new Array();
    for(var i=0; i<points.length;i++)
    {
      if(i!=j)
      {
      waypoints.push({location:points[i], stopover: true});
      }
    }
    var request = {
    origin: start,
    destination: end,
    waypoints: waypoints,
    optimizeWaypoints: true,
    travelMode: google.maps.TravelMode.DRIVING
    };
    directionsService.route(request, function(response, status) { 
    if (status == google.maps.DirectionsStatus.OK) {
      var route = response.routes[0];
      totalDistance = 0;
      for ( var i=0;i<route.legs.length;i++)
      {
        totalDistance+=route.legs[i].distance.value;
      }
      distances.push(totalDistance); 
      }
    });
  }
  /*Now I want my distances value to be accessed from here i.e outside for loop.*/
  /*So that I can compare all the distances obtained */
}
  google.maps.event.addDomListener(window, 'load', initialize);

Edit: I have updated complete code. What I am trying to do : I have fixed start point and some waypoints (order not fixed), I am trying to optimize waypoints, my end point is not fixed, it can be any so that to optimize the path, but it is necessary to provide end point while calling directionsService.route method , so I am taking one of the waypoints as end point and keeping rest other in waypoints only and then calculating total distance of the route. So each of the waypoint will become end point one by one , and others will remain waypoint. I will calculate total distance of all the combinations and then I will show only the directions of the route which has minimum distance.

geocodezip
  • 158,664
  • 13
  • 220
  • 245
Shivam
  • 303
  • 4
  • 20

4 Answers4

1

I would avoid calling asynchronous functions from inside a loop. It can be a pain keeping track of everything.

From what I understand of the question you are trying to find the shortest route with an arbitrary number of destinations. Instead of looping through each waypoint, pass the starting address and all of the destinations to the DistanceMatrix service which returns all of the route lengths from the origin to each waypoint. When the results return sort from shortest to longest. The longest destination will be the end address. Then pass the start address, end address, and remaining waypoints to the DirectionService with the optimizeWaypoints turned on.

Demo: http://jsfiddle.net/bryan_weaver/snYJ2/

Relavent Code:

var map;
var origin = "4100 Ashby Road, St. Ann, MO 63074"
var destinations = [
    "2033 Dorsett Village, Maryland Heights, MO 63043",
    "1208 Tamm Avenue, St. Louis, MO 63139",
    "1964 S Old Highway 94 St Charles, MO 63303"];
var directionsDisplay;
var directionsService = new google.maps.DirectionsService();

function calculateDistances() {
    var service = new google.maps.DistanceMatrixService();
    service.getDistanceMatrix({
        origins: [origin], //array of origins
        destinations: destinations, //array of destinations
        travelMode: google.maps.TravelMode.DRIVING,
        unitSystem: google.maps.UnitSystem.METRIC,
        avoidHighways: false,
        avoidTolls: false
    }, callback);
}

function callback(response, status) {
    if (status != google.maps.DistanceMatrixStatus.OK) {
        alert('Error was: ' + status);
    } else {
        //we only have one origin so there should only be one row
        var routes = response.rows[0];               
        var sortable = [];
        var resultText = "Origin: <b>" + origin + "</b><br/>";
        resultText += "Possible Routes: <br/>";
        for (var i = routes.elements.length - 1; i >= 0; i--) {
            var rteLength = routes.elements[i].duration.value;
            resultText += "Route: <b>" + destinations[i] + "</b>, " 
                + "Route Length: <b>" + rteLength + "</b><br/>";
            sortable.push([destinations[i], rteLength]);
        }
        //sort the result lengths from shortest to longest.
        sortable.sort(function (a, b) {
            return a[1] - b[1];
        });
        //build the waypoints.
        var waypoints = [];
        for (j = 0; j < sortable.length - 1; j++) {
            console.log(sortable[j][0]);
            waypoints.push({
                location: sortable[j][0],
                stopover: true
            });
        }
        //start address == origin
        var start = origin;
        //end address is the furthest desitnation from the origin.
        var end = sortable[sortable.length - 1][0];
        //calculate the route with the waypoints        
        calculateRoute(start, end, waypoints);
        //log the routes and duration.
        $('#results').html(resultText);
    }
}

//Calculate the route of the shortest distance we found.
function calculateRoute(start, end, waypoints) {
    var request = {
        origin: start,
        destination: end,
        waypoints: waypoints,
        optimizeWaypoints: true,
        travelMode: google.maps.TravelMode.DRIVING
    };
    directionsService.route(request, function (result, status) {
        if (status == google.maps.DirectionsStatus.OK) {
            directionsDisplay.setDirections(result);
        }
    });
}

function initialize() {
    directionsDisplay = new google.maps.DirectionsRenderer();
    var centerPosition = new google.maps.LatLng(38.713107, -90.42984);
    var options = {
        zoom: 12,
        center: centerPosition,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    map = new google.maps.Map($('#map')[0], options);
    geocoder = new google.maps.Geocoder();
    directionsDisplay.setMap(map);
    calculateDistances();
}

google.maps.event.addDomListener(window, 'load', initialize);
Bryan Weaver
  • 4,455
  • 1
  • 29
  • 39
  • Thank you so much, that solved my problem pretty much. Thanks a lot. Can you also tell me how to do same thing in PHP (not code, but how to proceed, or any tutorial of using PHP with google maps api) . I searched internet but i din't find any relevant information of using PHP with google maps api. Thanks. – Shivam May 22 '13 at 05:54
  • Sorry, I am not familiar with PHP. If I come across anything I will let you know. – Bryan Weaver May 22 '13 at 13:17
0

The request is asynchronous.

You have got to wait until the request completes before you check your global variable.

See this answer Is there any way to wait until the DirectionsService returns results?

EDIT If you really in a fix you can try making a Synchronous call with

jQuery.ajaxSetup({async:false});

making sure you turn it on again after the method completes

jQuery.ajaxSetup({async:true});

This comes with huge warning however as it can cause your browser to lock up use with caution

Community
  • 1
  • 1
Sydwell
  • 4,954
  • 1
  • 33
  • 36
  • 1
    Yes, I do understand that, but I am not able to get how to actually "wait" in my code ? I have another block of code which uses distances variable and needs to be executed just after this code. How to do that ? – Shivam May 21 '13 at 11:43
0

Your alert is firing before the callback has been fired. I'd suggest you create another function and call that from your directionsService.route success callback e.g

 var totalDistance; /*Global Variable */
var distances = new Array();
var directionsService = new google.maps.DirectionsService();
/* Some request */
directionsService.route(request, function(response, status) { 
if (status == google.maps.DirectionsStatus.OK) {
 directionsDisplay.setDirections(response);
 var route = response.routes[0];
 totalDistance = 0;
 for ( var i=0;i<route.legs.length;i++)
 {
   totalDistance+=route.legs[i].distance.value;
 }
 distances.push(totalDistance);

 }
 afterComplete();// New function call moved outside for loop
 });
 function afterComplete(){
 alert(distances);  //Will display null
 }

You could then also remove the global variable and actually pass it into the afterComplete function, thus eliminating the need for a global (unless of course it is needed elsewhere)

TommyBs
  • 9,354
  • 4
  • 34
  • 65
  • I have a for loop, which iterates 5 times and above request is made 5 times, now after 5 times request is made I want to use the value of distances varibale. However in the above solution, it will call function afterComplete each time loop iterates, I want to call afterComplete() only after all iterations of loop are over – Shivam May 21 '13 at 12:15
  • Then move it one line down outside the for loop (i've updated the code to reflect this) – TommyBs May 21 '13 at 13:08
  • Sorry I think I've misinterpreted. Are you saying the directionsService.route function is also in a for loop? If so can you show us the more complete code – TommyBs May 21 '13 at 13:14
  • Yes, directionsService.route function is also inside for loop.I have updated the complete problem. Please help me out to get rid of this problem. – Shivam May 21 '13 at 13:28
0

The DirectionsService is asynchronous. You need to use the results inside the call back function:

function calcRoute() {
  for(var j=0;j<points.length;j++)
  {

    end = points[j];
    var waypoints = new Array();
    for(var i=0; i<points.length;i++)
    {
      if(i!=j)
      {
      waypoints.push({location:points[i], stopover: true});
      }
    }
    var request = {
    origin: start,
    destination: end,
    waypoints: waypoints,
    optimizeWaypoints: true,
    travelMode: google.maps.TravelMode.DRIVING
    };
    directionsService.route(request, function(response, status) { 
      if (status == google.maps.DirectionsStatus.OK) {
        var route = response.routes[0];
        totalDistance = 0;
        for ( var i=0;i<route.legs.length;i++)
        {
          totalDistance+=route.legs[i].distance.value;
        }
        distances.push(totalDistance); 
      }
      else { alert("Distance Service request not successful:"+status); }
      if (distances.length == points.length){
        alert(distances);
      }
    });
  }

The global variable will be available after the data comes back from the server and the callback function finishes running. If you need to do something with it once it is available, do that in the call back function.

example

geocodezip
  • 158,664
  • 13
  • 220
  • 245
  • I am not able to understand how to use callback function ! – Shivam May 21 '13 at 12:35
  • What I am trying to do is, directionsService.route is executing inside a for loop which is iterating 5 times, for each iteration I am calculating totalDistance and pushing inside "distances" global variable, so that after all iterations are finished I can check for minimum of all the distances and then do "setDirections" only for the least distance. Please suggest some way I can do it. – Shivam May 21 '13 at 12:46
  • I have updated the complete problem. Please help me out to get rid of this problem. – Shivam May 21 '13 at 13:28