So you have probably seen something similar alot, where someone has a problem with using the "i" variable in a functions inside a for loop. Now that can easily be fixed with:
(function(){
return function() {
//something
}
})(i);
But, how do I do this in my scenario?
GMap.prototype.drawDirection = function (directionsRenderer, directionsService, headMarker, tailMarkers, callback) {
var request;
var array = [];
var count = 0;
for (var i = 0; i < tailMarkers.length; i++) {
count = i;
request = {
origin: headMarker.getPosition(),
destination: tailMarkers[i].getPosition(),
travelMode: google.maps.DirectionsTravelMode.DRIVING,
unitSystem: google.maps.DirectionsUnitSystem.METRIC
};
directionsService.route(request, function (response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsRenderer.setDirections(response);
console.log(i + " " + count);
callback(response.routes[0].legs[0].distance.value, i, tailMarkers.length - 1);
} else {
alert('Error: ' + status);
}
});
}
};
To be more accurate, it's about the directionsService object:
directionsService.route(request, function (response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsRenderer.setDirections(response);
console.log(i + " " + count);
callback(response.routes[0].legs[0].distance.value, i, tailMarkers.length - 1);
} else {
alert('Error: ' + status);
}
});
Now, I don't want to use JFiddle since it would be alot to write, so I'm just going to link to my site where I'm trying this.
http://stud.aitel.hist.no/~andersfy/html5.proj/
If you look at the checkbox in the bottom right corner, where it says "vis min posisjon" you just need to click on that, and you'll see the problem.
The code is in the file galled GMap.js at line 134 to 142.
I hope I'm giving you enough information!
You should not create functions inside of for loops for this very reason.
Your problem is in this closure, that shares the variable i:
function (response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsRenderer.setDirections(response);
console.log(i + " " + count);
callback(response.routes[0].legs[0].distance.value, i, tailMarkers.length - 1);
} else {
alert('Error: ' + status);
}
}
I would rewrite that function to not depend on i, but if you need i then
/*before your for loop */
function createRouter(i){
return function (response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsRenderer.setDirections(response);
console.log(i + " " + count);
callback(response.routes[0].legs[0].distance.value, i, tailMarkers.length - 1);
} else {
alert('Error: ' + status);
}
}
};
/* in your for loop */
directionsService.route(request, createRouter(i));
Related
I'm new to javascript, i wanted to check some data using google maps api but this asynchronuous execution of function is killing me, and i don't really understand how callbacks work. Tried to do it but doesn't work.
function initMap(a, callback) {
var service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix({
origins: [punktpocz[a], punktpocz[a + 1]],
destinations: [punktkonc[a], punktkonc[a + 1]],
travelMode: 'DRIVING',
unitSystem: google.maps.UnitSystem.METRIC,
avoidHighways: false,
avoidTolls: false
}, function (response, status) {
if (status !== 'OK') {
alert('Error was: ' + status);
} else {
var originList = response.originAddresses;
var destinationList = response.destinationAddresses;
for (var i = 0; i < originList.length; i++) {
var results = response.rows[i].elements;
for (var j = 0; j < results.length; j++) {
theDiv.innerHTML += originList[i] + ' to ' + destinationList[j] +
': ' + results[j].distance.text + ' in ' +
results[j].duration.text + '<br>';
wynik.push({ odl: results[j].distance.text, czas: results[j].duration.text });
}
}
callback();
}
});
}
and in the second function:
for(i=0;i<czasmax;i++){
...
punktpocz.push(kie[id-1]);
punktkonc.push(biz[parseInt(zleceniajava[z+1])]);
punktpocz.push(biz[parseInt(zleceniajava[z+1])]);
punktkonc.push( zle[ parseInt(zleceniajava[z])] );
initMap(i*2,function(){
console.log('huzzah, I\'m done!');
});
...
}
and i want for the script to wait until initmap is finished to continue but it doesn't seem to work.
You might look into using promises (I had a similar question before: Javascript Promise/Then Example) or use async.js
(Node.js | MongoDB count(): Getting Count Before and After Inserting Data)
I have a for loop and inside that I am calling one function. But before function response for loop variable is incremented by 1.
I just want to wait until the response has come, then increment var by 1.
Here is my code:
for(var i=0;i<$scope.latitude.length;i++)
{
console.log("inside for :",$scope.latitude[i]);
console.log("i after for:" ,i);
console.log("count before if:" ,count);
if($scope.latitude[i]!=1)
{
console.log("inside if count is :",count);
console.log("i inside if:" ,i);
var lat =$scope.latitude[i];
var lng=$scope.longitude[i];
console.log("lat is going",lat);
console.log("lng is going",lng);
$scope.getDistance(lat,lng).then(function(response,flag){
console.log("flag is" ,flag);
setTimeout(function(){
flag="true";
},1000);
var results = response.rows[0].elements;
console.log(results[0].distance.text);
console.log(results[0].duration.text);
return results[0].distance.text
}).done(function(distanceMatrixResult,flag) {
console.log("flag inside done is:" ,flag);
console.log("count is ",count);
console.log("i inside done:" ,i);
console.log(distanceMatrixResult);
$scope.clients.treasureHunts[count].distance =distanceMatrixResult;
$ionicLoading.hide();
console.log("Results:->>>>>>>>>>>>", JSON.stringify( $scope.clients));
})
$scope.getDistance=function(lat,lng){
console.log("lat inside google distance function:-" ,lat);
console.log("lng inside google distance function:-" ,lng);
var service = new google.maps.DistanceMatrixService();
$scope.destinationDetails = {
lat: lat,
lng: lng
}
console.log("destination:-" ,JSON.stringify( $scope.destinationDetails.lat));
console.log("destination:-" ,JSON.stringify( $scope.destinationDetails.lng));
//getting current location
var onSuccess = function (position) {
console.log('Current Position: ', position.coords.latitude, ',', position.coords.longitude);
$scope.originCenter = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
geocodeLatLng($scope.originCenter);
}
function onError(error) {
console.error('code: ' + error.code + '\n' + 'message: ' + error.message + '\n');
//alert('Error Occured' + error.message);
}
navigator.geolocation.getCurrentPosition(onSuccess, onError);/////////---------------------------------
function geocodeLatLng(originCenter) {
console.log("destination inside function:-" ,JSON.stringify( $scope.destinationDetails.lat));
console.log("destination inside function:-" ,JSON.stringify( $scope.destinationDetails.lng));
$scope.originDetails = new google.maps.LatLng(originCenter.lat, originCenter.lng);
console.log('Origin Details: ',JSON.stringify($scope.originDetails));
$scope.finalDestination = new google.maps.LatLng($scope.destinationDetails.lat, $scope.destinationDetails.lng);
console.log('destination details: ', JSON.stringify($scope.finalDestination));
service.getDistanceMatrix(
{
origins: [$scope.originDetails],
destinations: [$scope.finalDestination],
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.METRIC,
avoidHighways: false,
avoidTolls: false
},
function (response, status) {
if (status !== google.maps.DistanceMatrixStatus.OK) {
console.error(JSON.stringify(response));
d.reject(response);
} else {
console.log("returning response ",JSON.stringify(response));
var flag="true";
d.resolve(response,flag);
}
});
Since js is single threaded (as far as regarded here) there can not be a loop waiting for something other happening in js code (like any callbacks executed..).
Looping over asynchronous calls need to split functionality into the async callback/success-handler (add some error handling as well;), e.g. like this:
function makeNextCall(i, total, onCompleted) {
if( i >= total) {
onCompleted(); // signal completion of whole iteration
} else {
makeAsyncCall(i, { // make the asynchronous call
onSuccess : function() { // define callback
makeNextCall(i+1, total, onCompleted); // next iteration
}
});
}
}
Let's say you have a map to display KmlLayer objects on it, with toggle checkbox for each KmlLayer object, located in a panel.
var Province = new google.maps.LatLng(47.112754, -70.815223);
var map = new google.maps.Map( document.getElementById('map'), {zoom:7,center:Province} );
And you store in KML files a limited number of routes, to optimize the speed of the map.
Direction's requests are made using Google Maps API v3.
And let's say you have a page containing a Google Map in a hidden div, and you make Direction requests through this API.
The array 'waypoints' holds the coordinates of each start and end point of each polyline needed.
var geo_coords = ''; // holds the results
var y; // counter
var delay_per_request = 1000; // default delay in milliseconds between requests
var current_delay = delay_per_request; // current delay between requests
function calcRoute(start,end) {
setTimeout(function(){
var directionsService = new google.maps.DirectionsService();
var request = {
origin: start,
destination: end,
travelMode: google.maps.TravelMode.DRIVING
};
directionsService.route(request, function(response, status) {
// Error?
if (status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT) {
document.getElementById('error_text').innerHTML += "<br>" + start + "/" + end
+ "("+status+") :: Interval:"+current_delay+"ms";
window.scrollTo(0, document.body.scrollHeight);
if (current_delay < 2600) {
current_delay += 200;
}
if (current_delay >= 1600) {
delay_per_request = 1600;
}
retry++;
if (retry < 20) {
calcRoute(start,end);
} else {
document.getElementById('error_text').innerHTML += "<br>The script stopped running after "+retry+" retries"
+ " sur " + start + "/" + end + "("+status+") :: Interval:"+current_delay+"ms";
}
// Positive result?
} else if (status == google.maps.DirectionsStatus.OK) {
var myRoute = response.routes[0].overview_path;
for (i in myRoute) {
if (myRoute[i] != undefined) {
geo_coords += myRoute[i].lat() + "," + myRoute[i].lng() + "_";
}
}
geo_coords += "!";
document.getElementById('error_text').innerHTML += "<br><font color=\"green\">" + start + "/" + end
+ "("+status+") :: Interval:"+current_delay+"ms</font>";
window.scrollTo(0, document.body.scrollHeight);
current_delay = delay_per_request;
retry = 0;
Next();
}
});
},current_delay);
}
function Next() {
if (y <= waypoints.length) {
destination = "from:" + waypoints[y] + " to:" + waypoints[y + 1];
geo_desc += description[y] + " => " + description[y+1] + "|";
calcRoute(waypoints[y],waypoints[y+1]);
y = y +2;
} else {
document.getElementById("information").setAttribute('value',geo_coords);
document.getElementById("show_debug_text").setAttribute('value',geo_desc);
document.getElementById("error").setAttribute('value',document.getElementById('error_text').innerHTML);
document.forms["kml"].submit();
}
}
Next();
My question is the following:
Has anyone noticed that Google lower the priority of your requests after you made some request in batch?
The more request you do, the bigger is the number of OVER_QUERY_LIMIT response you get, the bigger is the delay between requests before you have a successful result, and the bigger is the number of requests before you have a successful result.
Is there any workaround or any reason?
(I know I should use the Direction API Webservice to make my requests.)
Thank you!
I am trying to have a function for calculating routes for google maps be dynamically changed based on data retrieved from a .getJSON. I have tried including the bulk of the function calcRoute() under a .done function, but I am receiving an error in property waypoints in the javascript console. I am at a loss as what to do, because when I don't include the bulk of the function under the .done, the array remains blank (asynchronous call with the .getJSON. Here is the code to give you a better idea:
function calcRoute() {
var start = document.getElementById('start').value;
var end = document.getElementById('end').value;
var waypts = [];
var data = $.getJSON("/westcoast_map.php", {
westcoast_id: $('.opener').data('westcoast_id')
}, function(json) {
return json[1];
});
data.done(function(theData) {
waypts = theData[1];
console.log(waypts); //this spits out the array in the proper format, i.e. {location:city, state, stopover:true},...etc...
var request = {
origin: start,
destination: end,
waypoints: waypts,
optimizeWaypoints: true,
travelMode: google.maps.DirectionsTravelMode.DRIVING
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
var route = response.routes[0];
var summaryPanel = document.getElementById('directions_panel');
summaryPanel.innerHTML = '';
// For each route, display summary information.
for (var i = 0; i < route.legs.length; i++) {
var routeSegment = i + 1;
summaryPanel.innerHTML += '<b>Route Segment: ' + routeSegment + '</b><br>';
summaryPanel.innerHTML += route.legs[i].start_address + ' to ';
summaryPanel.innerHTML += route.legs[i].end_address + '<br>';
summaryPanel.innerHTML += route.legs[i].distance.text + '<br><br>';
}
}
});
});
}
i'm still not sure what you problem is, because the code pared you show, should at least work from the logical part. but there are parts where it is not clear what you try to achive:
var data = $.getJSON("/westcoast_map.php", {
westcoast_id: $('.opener').data('westcoast_id')
}, function(json) {
return json[1];
});
if you expect here that data will become json[1], then your assumption is wrong.
$.getJSON returns always jQuery XHR. the callback function will be called later when the browser received the data.
Here a little example to understand how async works:
the callback functions 1 and 2 are called when the client gets the response for the request, but not before the original script was completely executed, so doSomethingElse() will be always called before the callback function 1 and 2 are executed.
the order in which callback function 1 and 2 are executed depends on which response arrives first.
var test = [];
preparesSomeStuff();
$.getJSON("someurl1",{},function() {
//Callback Function 1
});
doSomething();
$.getJSON("someurl2",{},function() {
//Callback Function 2
});
doSomethingElse();
//<<END OF SCRIPT>>
if you don't want to have your whole code inside of the callback function (e.g. because of readability) you could do it the following way:
function calcRoute() {
var start = document.getElementById('start').value;
var end = document.getElementById('end').value;
var waypts = [];
$.getJSON("/westcoast_map.php", {
westcoast_id: $('.opener').data('westcoast_id')
}, function(theData) {
calcualteRoute(theData[1], start, end);
});
//if you place code here it will be executed before displayResult will be called because getJSON is async
}
function calcualteRoute(waypts, start, end) {
console.log(waypts); //this spits out the array in the proper format, i.e. {location:city, state, stopover:true},...etc...
var request = {
origin: start,
destination: end,
waypoints: waypts,
optimizeWaypoints: true,
travelMode: google.maps.DirectionsTravelMode.DRIVING
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
displayResult(response,status);
}
});
//if you place some code here it will be executed BEFORE displayResult will be called, because
//directionsService.route is async
}
function displayResult(response, status) {
directionsDisplay.setDirections(response);
var route = response.routes[0];
var summaryPanel = document.getElementById('directions_panel');
summaryPanel.innerHTML = '';
// For each route, display summary information.
for (var i = 0; i < route.legs.length; i++) {
var routeSegment = i + 1;
summaryPanel.innerHTML += '<b>Route Segment: ' + routeSegment + '</b><br>';
summaryPanel.innerHTML += route.legs[i].start_address + ' to ';
summaryPanel.innerHTML += route.legs[i].end_address + '<br>';
summaryPanel.innerHTML += route.legs[i].distance.text + '<br><br>';
}
}
I have set an onclick with a variable on a link that calls the calcRoute function for Google maps, and everytime I click the link it says the following Uncaught ReferenceError: NE461UL is not defined The parameter is a postcode by the way.
I have been trying for a while and I can't figure out why it is showing an error.
I have a jquery file with the following line
var distLink = "<a onclick=\"calcRoute(" + postcode + "); return false;\" datalat=" + lat + " datalng=" + lng + " id=\"get-directions\" class=\"directions" + letter +"\">Directions<\/a>";
The calcRoute is in my header
function calcRoute(postcode) {
console.log(marker);
var start = document.getElementById("address").value;
var end = document.getElementById("get-directions").name;
$('get-directions').click(function(){
console.log('click!');
});
var request = {
origin:start,
destination:end,
travelMode: google.maps.TravelMode.DRIVING
};
console.log(JSON.stringify(request));
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
}
});
}
postcode must be passed as a string (extra pair of quotation marks around it):
distLink = "<a onclick=\"calcRoute('" + postcode + "'); return false;\" datalat=" + lat + " datalng=" + lng + " id=\"get-directions\" class=\"directions" + letter +"\">Directions<\/a>";