简体   繁体   中英

Asychronous Google Maps throwing off Javascript - How do callbacks work?

I'm trying to refactor some code, and am trying to use Google maps to calculate longitude and latitude. I orignally ran into a problem with Google maps getting the data ascynronously. Someone was nice enough to suggest a solution, but after a few days, I must conclude that I'm sadly too much of a JS newbie to get it to work. Here's the entire code, along with my original question: Asynchronous Google Maps request throwing off Javascript I'm sure it's something small, but I've tried for 2 days and haven't been able to get a working version. Any assistance would be much appreciated - I have a feeling it's something small, but just can't fathom what that is.

<script type="text/javascript">
   //declare variables
  var map;
  var directionsService;
  var directionsDisplay1, directionsDisplay2, directionsDisplay3;
  var markersArray = [];
  var stations;

  var autocomplete_options = {componentRestrictions: {country: 'us'}};
  var places = new Array();
  places[0] = "Barlays Center, Brooklyn NY";
  places[1] = "Union Square, nyc";

//formerly var autocomplete_start = new google.maps.places.Autocomplete(document.getElementById("id_start"), autocomplete_options);
    var autocomplete_start = places[0];
//var autocomplete_start = new google.maps.places.PlacesService(places[0]);
    var autocomplete_end = places[1];

  //Figure out the distance between your location and the nearest Citibike station
function getDistance(lat1,lng1,lat2,lng2) {
  var i = lat1 - lat2;
  var j = lng1 - lng2;
  return i*i + j*j;
}

function findNearestStation(lat,lng) {
  var min_distance = 99999;
  var closest_station_id;
  $.each(stations.stationBeanList, function(i, station) {
    var distance = getDistance(lat,lng, station.latitude, station.longitude);

    if (distance < min_distance) {
      min_distance = distance;
      closest_station_id = i;
    }
  });

  console.log('Closest station idx: ' + closest_station_id);

  return stations.stationBeanList[closest_station_id];
}
 //Draw on the map
function drawMarker(lat, lng, map, title, marker_text) {
  var point = new google.maps.LatLng(lat, lng);

  var marker = new google.maps.Marker({
    position : point,
    map : map,
    title : title,
    icon: 'http://chart.apis.google.com/chartchst=d_map_pin_letter&chld='+marker_text+'|FE6256|000000'
  });

  markersArray.push(marker);
}


function clearMarkers() {
  for (var i = 0; i < markersArray.length; i++ ) {
    markersArray[i].setMap(null);
  }
  markersArray = [];
}

function drawMap() {
  var center = new google.maps.LatLng(40.704066,-73.992727);
  var mapOptions = {
    mapTypeId: google.maps.MapTypeId.ROADMAP,
    mapTypeControlOptions: { style: google.maps.MapTypeControlStyle.DROPDOWN_MENU },
    zoom: 14,
    center: center
  };

  map = new google.maps.Map(document.getElementById("map"), mapOptions);
  directionsService = new google.maps.DirectionsService();
  directionsDisplay1 = new google.maps.DirectionsRenderer();
  directionsDisplay1.setMap(map);
  directionsDisplay1.setPanel(document.getElementById("directions-panel1"));
  directionsDisplay2 = new google.maps.DirectionsRenderer();
  directionsDisplay2.setMap(map);
  directionsDisplay2.setPanel(document.getElementById("directions-panel2"));
  directionsDisplay3 = new google.maps.DirectionsRenderer();
  directionsDisplay3.setMap(map);
  directionsDisplay3.setPanel(document.getElementById("directions-panel3"));

  $.getJSON('./static/js/stations.json', function(data) {
    stations = data;
    var bounds = new google.maps.LatLngBounds();
    $.each(data.stationBeanList, function(i, station) {
      if (station.statusValue == 'In Service') {
        var point = new google.maps.LatLng(station.latitude, station.longitude);
        bounds.extend(point);
      }
    });
    map.setCenter(bounds.getCenter(), map.fitBounds(bounds));
    autocomplete_start.bindTo('bounds', map);
    autocomplete_end.bindTo('bounds', map);
  });
}

$(document).ready(function(){
  drawMap();
});

$('#btn-reset').click(function(){
  $('#directions-panel1').empty();
  $('#directions-panel2').empty();
  $('#directions-panel3').empty();
  $('#directions-info1').empty();
  $('#directions-info2').empty();
  $('#directions-info3').empty();
  drawMap();
});

$('#btn-get-directions').click(function(){
  //take directions on the map

  var start = autocomplete_start;
  var end   = autocomplete_end;
  var start_lat=getLat(start);
  //this is legacy code - the original had Google autocomplete
  var start_lng;
  var end_lat;
  var end_lng;
 // getLat(start);


  function getLat(loc) {
    var geocoder = new google.maps.Geocoder();
    geocoder.geocode({'address': loc}, function postcodesearch(results, status) 
    {   
      if (status == google.maps.GeocoderStatus.OK) 
    {
      var lat = results[0].geometry.location.lat();
      //alert(lat);
      return lat;

      //alert(start_lng);
    }
  else {
    alert("Error");
  }
  });
 }
//These are original functions I wrote
function getLng(loc) {
var geocoder_lng = new google.maps.Geocoder();
  geocoder_lng.geocode({'address': loc}, function postcodesearch(results, status) 
{   
  if (status == google.maps.GeocoderStatus.OK) 
{
  var lng = results[0].geometry.location.lng();
 // alert(lng);
  return lng;
  //alert(end_lat);

  //doRest();
}
  else {
     alert("Error");
   }
});
}

//This is a far superior function someone else wrote, which I cannot implement
 function getLatLng(loc, callback) {

    var geocoder = new google.maps.Geocoder();
    geocoder.geocode({'address': loc}, function postcodesearch(results, status) 
  {  
    if (status == google.maps.GeocoderStatus.OK) 
    {
        var lat = results[0].geometry.location.lat();
        var lng = results[0].geometry.location.lng();
        alert("234234");
        return(lat, lng);
    }
   }
  );}

getLatLng(start);
doRest()
 //This is the rest of the code
  function doRest(){

  //var start_lat = start.geometry.location.lat();
  //alert("DO REST");

  var start_lng = getLng(start);
  alert(start_lng);
  var start_p = new google.maps.LatLng(getLatLng(start));
  //alert(start_p);
  //alert(start_p);
  //var end_lat = end.geometry.location.lat();
  //var end_lng = end.geometry.location.lng();
  var end_p = new google.maps.LatLng(getLatLng(end));



  var start_station = findNearestStation(start_p);
  var end_station = findNearestStation(end_p);

  alert("asdf");
  var start_station_p = new google.maps.LatLng(start_station.latitude, start_station.longitude);
  alert("wtf");
  var end_station_p = new google.maps.LatLng(end_station.latitude, end_station.longitude);
  // alert("stations");

  var dir_bounds = new google.maps.LatLngBounds();
  dir_bounds.extend(start_p);
  dir_bounds.extend(end_p);
  dir_bounds.extend(start_station_p);
  dir_bounds.extend(end_station_p);

  drawMarker(start_lat, start_lng, map, $('#id_start').val(), '1');
  drawMarker(start_station.latitude, start_station.longitude, map, start_station.stationName, '2');
  drawMarker(end_station.latitude, end_station.longitude, map, end_station.stationName, '3');
  drawMarker(end_lat, end_lng, map, $('#id_end').val(), '4');
}

  // We'll make 3 calls, 1) walking, 2) biking, 3) walking
  // Walk from start to station 1
  var request1 = {
    origin:start_p,
    destination:start_station_p,
    travelMode: google.maps.TravelMode.WALKING
  };
  directionsService.route(request1, function(result, status) {
    if (status == google.maps.DirectionsStatus.OK) {
      $('#directions-info1').html('Walk from ' + $('#id_start').val() + ' to station at ' + start_station.stationName);
      directionsDisplay1.setDirections(result);
    }
  });

  // Bike from station 1 to station 2
  var request2 = {
    origin:start_station_p,
    destination:end_station_p,
    travelMode: google.maps.TravelMode.BICYCLING
  };
  directionsService.route(request2, function(result, status) {
    if (status == google.maps.DirectionsStatus.OK) {
      $('#directions-info2').html('Bike from station at ' + start_station.stationName + ' to station at ' + end_station.stationName);
      directionsDisplay2.setDirections(result);
    }
  });

  // Walk from station 2 to end
  var request3 = {
    origin:end_station_p,
    destination:end_p,
    travelMode: google.maps.TravelMode.WALKING
  };
  directionsService.route(request3, function(result, status) {
    if (status == google.maps.DirectionsStatus.OK) {
      $('#directions-info3').html('Walk from station at ' + end_station.stationName + ' to ' + $('#id_end').val() );
      directionsDisplay3.setDirections(result);
    }
  });

  google.maps.event.addListener(directionsDisplay1, 'directions_changed', function() {
    map.setCenter(dir_bounds.getCenter(), map.fitBounds(dir_bounds));
  });
  google.maps.event.addListener(directionsDisplay2, 'directions_changed', function() {
    map.setCenter(dir_bounds.getCenter(), map.fitBounds(dir_bounds));
  });
  google.maps.event.addListener(directionsDisplay3, 'directions_changed', function() {
    map.setCenter(dir_bounds.getCenter(), map.fitBounds(dir_bounds));
  });
});

The main problem I see is that you're trying to return values from getlatLng -- that won't work, because the geocode call is asynchronous. The key point is to use callbacks. Instead of doing this:

function getLatLng() {
    return somevalue;
}
latlng = getLatLng();
// do something

Do this instead:

function getLatLng(callback) {
    callback(somevalue);
}
getLatLng(function(latlng) {
    // do something
});

Here's an implementation of getLatLng that takes a start, end, and callback. The callback, in turn, is a function that takes four parameters: start_lat, start_lng, end_lat, end_lng. It is unchecked for syntax, but hopefully it helps.

function getLatLng(start, end, callback) {
    var geocoder = new google.maps.Geocoder();

    // geocode the start
    geocoder.geocode({'address': start}, function(results, status) {  
        if (status == google.maps.GeocoderStatus.OK) {
            var startlat = results[0].geometry.location.lat();
            var startlng = results[0].geometry.location.lng();
            console.log('Got start: ' + startlat + ', ' + startlng);

            // geocode the end
            geocoder.geocode({'address': end}, function(results, status) {
                if (status == google.maps.GeocoderStatus.OK) {
                    var endlat = results[0].geometry.location.lat();
                    var endlng = results[0].geometry.location.lng();
                    console.log('Got end: ' + endlat + ', ' + endlng);

                    // invoke the callback function
                    callback(startlat, startlng, endlat, endlng);
                }
            });
       }
    });
}

// the third parameter provided here to getLatLng is the callback function:
getLatLng(start, end, function(start_lat, start_lng, end_lat, end_lng) {
    //This is the rest of the code
    var start_p = new google.maps.LatLng(start_lat, start_lng);
    var end_p = new google.maps.LatLng(end_lat, end_lng);
    var start_station = findNearestStation(start_p);
    var end_station = findNearestStation(end_p);
    // etc
});

Note As a general tip, use your browser's debugging tools (Shift-Ctl-J in Chrome). This should allow you to:

  1. Check for Javascript errors in the console
  2. Add "console.log" statements to your code to help trace its progress
  3. Add breakpoints to the code to enable you to trace its execution

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM