First of all, I didn't reach the maximum daily limit of 2,500 addresses.
I have 25 addresses, and I already set the sleep time in JavaScript between each address to 5 seconds. I always get OVER_QUERY_LIMIT error after 18 or 19 addresses are geocoded. The rest 7 or 6 address always not get geocoded.
Google API Geocoding as limit of 5 addresses per second, or they have increased?
Thanks.
Code
function geocodeAddress(geocoder, addresses ) {
var arrayLength = addresses.length;
for (var i = 0; i < arrayLength; i++) {
var address = String(addresses[i]);
// alert(address)
sleep(5000)
geocoder.geocode({'address': address}, function (results, status)
{
if (status === google.maps.GeocoderStatus.OK) {
var result = results[0].geometry.location;
var name = results[0].formatted_address;
writeFile(geocode_file_path, name + ',' + result.toString());
} else {
alert('Geocode was not successful for the following reason: ' + status);
}
});
}
}
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
This is a complex problem. Async is very tricky to get your head around.
Having said that, here's a solution that would work. I would prefer a Promise based solution but Promises are another issue that takes time to understand.
function geocodeAddresses(geocoder, addresses, callback){
var result = [];
// this internal function does the hard work
function geocode(address) {
// get timestamp so we can use it to throttle later
var lastCall = Date.now();
// call google function asynchronously
geocoder.geocode({'address': address}, function (results, status) {
// process the return data here
if (status === google.maps.GeocoderStatus.OK) {
result.push({
location: results[0].geometry.location,
address: results[0].formatted_address
});
} else {
result.push({ error: status });
}
// check to see if there are any more addresses to geocode
if (addresses.length) {
// calculate when next call can be made. 200ms is 5 req / second
var throttleTime = 200 - (lastCall - Date.now());
setTimeout(function(){ // create timeout to run in 'throttletime` ms
// call this function with next address
geocode(addresses.shift());
}, throttleTime);
} else { // all done return result
callback(result);
}
});
}
// start the process - geocode will call itself for any remaining addresses
geocode(addresses.shift());
}
Since this function is asynchronous, you have to use it asynchronously ... hence the callback. So you use it as follows:
geocodeAddresses(geocoder, addresses, function(result){
// do what you need with the result here
console.log(result);
});
This is about as simple as I can make it. I've created a jsbin that mocks out the geocoder call and shows real results. Change the throttleTime to bigger numbers to see it slow down.
I hope this helps.
Related
I am using Google's maps API to geocode two addresses. I defer the returned results and use a $.when().then() method to execute my logic once I get the coordinates for the string addresses. The problem is the API always returns the result as resolved, even if there is an error. For example if there is no internet connection instead of request timing out I get the status as ERROR and result as null or if I enter an invalid address I get the status ZERO_RESULTS and result an empty array []. As I am only interested in getting the proper coordinate results I want to handle all other responses as a geocoding error, which I don't know how to do. You can also see that I have to check if input fields are empty before geocoding because of the same problem.
I am just getting acquainted to asynchronous flow and need some guidance.
I am using jquery 1.9.2 and google maps APIv3. (I can hard code all the conditions but I want to improve my coding skills and have something more generic. Is that possible.)
I will give my code here as well.
function geocode(addString) {
var deferred = $.Deferred();
var geocoder = new google.maps.Geocoder();
var request = {
address: addString
};
geocoder.geocode(request, function(results, status) {
if (status === google.maps.GeocoderStatus.OK) {
deferred.resolve(results[0].geometry.location);
}
else {
// no idea what to do here
}
});
return deferred;
}
function Options() {
var origin = $("#origin-field").val();
var destination = $("#destination-field").val();
if (origin != "" && destination != ""){
var originCoords = geocode(origin);
var destinationCoords = geocode(destination);
$.when(originCoords, destinationCoords)
.then(function(originCoordinates, destinationCoordinates) {
console.log(originCoordinates.lat() + ',' + originCoordinates.lng());
console.log(destinationCoordinates.lat() + ',' + destinationCoordinates.lng());
}, function() {
toastMessage("Geo-coding error");
});
}
else {
toastMessage("Origin and/or Destination missing");
}
}
I solved my problem thanks to this example. As I said I just started looking at asynchronous flow so didn't know how to solve this simple problem.
What I did is just catch all non-OK statuses in an else block and passed it to the deferred.reject() method. So my code became like this.
function geocode(addString) {
var deferred = $.Deferred();
var geocoder = new google.maps.Geocoder();
var request = {
address: addString
};
geocoder.geocode(request, function(results, status) {
if (status === google.maps.GeocoderStatus.OK) {
deferred.resolve(results[0].geometry.location);
}
else {
deferred.reject(status);
}
});
return deferred;
}
function Options() {
var origin = $("#origin-field").val();
var destination = $("#destination-field").val();
var originCoords = geocode(origin);
var destinationCoords = geocode(destination);
$.when(originCoords, destinationCoords)
.then(function(origin, destination) {
//some logic in case of success
}, function(status) {
toastMessage("Geo-coding error:" + status);
});
}
I'm doing a search through google maps, and I want to populate every location in an array. This allows me to sort properly since the API can lag at times and the JSON being returned may not be done correctly.
This is using the Google Maps Places API. Some code is left out to keep it brief. I'm able to get the array to populate within a function, but when I try to pass it to another function it's blank.
var liquorSearchParams = {
location: latLong,
rankBy: google.maps.places.RankBy.DISTANCE,
types: ['liquor_store']
};
//Call the google maps service to perform the search
var service = new google.maps.places.PlacesService(map);
service.nearbySearch(liquorSearchParams, callback);
//Set my blank array
var allPlaces = [];
function callback(results, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
for (var i = 0; i < results.length; i++) {
//createMarker(results[i]);
addPlaceToArray(results[i]);
//this console.log just shows []
console.log(allPlaces);
}
}
}
function addPlaceToArray(place) {
var placeIdRequest = {
placeId: place.place_id
};
service.getDetails(placeIdRequest, placeDetailCallback);
};
function placeDetailCallback(placeDetail, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
var destinationPoint = new google.maps.LatLng(placeDetail.geometry.location.k, placeDetail.geometry.location.B);
var theDistance = google.maps.geometry.spherical.computeDistanceBetween(destinationPoint, latLong);
allPlaces.push({name: placeDetail.name, latitude: placeDetail.geometry.location.k, longitude: placeDetail.geometry.location.B, address: placeDetail.vicinity, phone: placeDetail.formatted_phone_number, website: placeDetail.website, distance: theDistance});
//this console.log returns the populated array that i want
console.log(allPlaces);
return allPlaces;
}
};
I expect that this is happening because allPlaces.push(...) happens in a callback which happens after the console.log(allPlaces).
Here the actual sequence of events as far as I can tell:
You retrieve some liquor stores
You iterate through them
For each store you:
initiate a request for more details
immediately do a console.log(allPlaces) - which is empty because step 3 hasn't happened yet
when the request for more details finishes, you push the details to the allPlaces array.
What you need to do is do the console.log(allPlaces) once all the placeDetailCallbacks have been completed. I recommend Async.js or something similar.
Using Async.js:
var liquorSearchParams = {
location: latLong,
rankBy: google.maps.places.RankBy.DISTANCE,
types: ['liquor_store']
};
var allPlaces = [];
function callback(results, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
async.each(results, addPlaceToArray, function(err) {
// this gets executed when all places have been added to allPlaces
console.log(allPlaces);
});
}
}
//This is called for each item in the results array
//The done parameter is a callback you call when you are, well, done.
function addPlaceToArray(place, done) {
var placeIdRequest = {
placeId: place.place_id
};
var placeDetailCallback = function(placeDetail, status) {
var destinationPoint = new google.maps.LatLng(placeDetail.geometry.location.k, placeDetail.geometry.location.B);
var theDistance = google.maps.geometry.spherical.computeDistanceBetween(destinationPoint, latLong);
allPlaces.push({name: placeDetail.name, latitude: placeDetail.geometry.location.k, longitude: placeDetail.geometry.location.B, address: placeDetail.vicinity, phone: placeDetail.formatted_phone_number, website: placeDetail.website, distance: theDistance});
done(); //if an error occurred here, you can pass it to done
};
service.getDetails(placeIdRequest, placeDetailCallback);
};
//Call the google maps service to perform the search
var service = new google.maps.places.PlacesService(map);
service.nearbySearch(liquorSearchParams, callback);
So I have an array of objects with the following properties: Address, Latitude, Longitude, and Title
poi("139 Main St", 0, 0, "Farmer's Market" ),
poi("18 N Hopkins Ave", 37.4455, -143.7728, "YMCA" ),
poi("42 Universe St", 37.4855, -143.8781, "Original Pizza #32" )
Now, the first object does not have anything set for latitude and longitude, so I was hoping to use Google's Geocode API to fill in the missing data. I included the API script link with my API key, which is working fine.
I then created a for loop that goes through the array and finds any object with missing data. It's supposed to go through and replace any empty cells with the correct information. However, though the information is retrieved, it does so after the loop is completed. I can console out the correct information, but only after the function returns empty data.
for (i=0; i < poiArray.length; i++ ) {
var curr = poiArray[i];
if ( !curr.lat || !curr.long ) {
geocoder = new google.maps.Geocoder();
geocoder.geocode( { 'address': curr.address }, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
cur.lat = results[0].geometry.location.k;
cur.long = results[0].geometry.location.B;
} else {
console.log('failed to include ' + curr.title);
poiArray.splice(i--, 1);
}
});
}
}
I've tried timeout and nesting functions, but nothing seems to work. Any suggestions?
Let me mention a bit more weirdness: if I console after if (status == google.maps.GeocoderStatus.OK) { the variable i in my for loop, I get the last number of my array... every time. And the loop only seems to update the last item in my array if I console my poiArray after the asynchronous data from Google has loaded.
you can solve your issue with function closure. Also, you shouldn't use undocumented properties of the Google Maps Javascript API v3 (.k, .B), they will change with updates of the API and break your code.
var geocoder = new google.maps.Geocoder();
function geocodeAddress(curr, i) {
geocoder.geocode( { 'address': curr.address }, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
cur.lat = results[0].geometry.location.lat();
cur.long = results[0].geometry.location.lng();
} else {
console.log('failed to include ' + curr.title);
poiArray.splice(i--, 1);
}
});
}
for (i=0; i < poiArray.length; i++ ) {
var curr = poiArray[i];
if ( !curr.lat || !curr.long ) {
geocodeAddress(curr, i);
}
}
I'm sorry, I know this has been asked a thousand times, but I've read the responses and I still don't get it. I'm new to Javascript (I started yesterday, actually) and I have the following problem:
I have an asynchronous function, and I need the returned value, but it is undefined, of course. I've read about callbacks, but I'm not sure how they work.
The function is the following:
function getLatLong(address){
var geo = new google.maps.Geocoder;
geo.geocode({'address':address},function(results, status){
if (status == google.maps.GeocoderStatus.OK) {
var returnedLatLng = [];
returnedLatLng["lat"] = results[0].geometry.location.lat();
returnedLatLng["lgn"] = results[0].geometry.location.lng();
locationTarget = new google.maps.LatLng(returnedLatLng.lat,returnedLatLng.lgn);
alert(locationTarget);
return locationTarget;
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
I'm calling this function from an initialize() function and I'm doing it this way:
var location = getLatLong(address);
Well, my question is how would a callback help me out here? and if it's possible.. what code should I use?
Thanks a lot! (This is my first very first question here!)
The most basic solution to start with will be to define your location globally and to respond to the callback you already have:
var location;
function getLatLong(address){
var geo = new google.maps.Geocoder;
geo.geocode({'address':address},function(results, status){
if (status == google.maps.GeocoderStatus.OK) {
var returnedLatLng = [];
returnedLatLng["lat"] = results[0].geometry.location.lat();
returnedLatLng["lgn"] = results[0].geometry.location.lng();
locationTarget = new google.maps.LatLng(returnedLatLng.lat,returnedLatLng.lgn);
alert(locationTarget);
location = locationTarget;
// Additional logic you are using the location for
// After this point your location is defined.
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
getLatLong(address)
So basically your logic needs to be based not on a call to this method that returns location but rather on something that happens after the geocode function callback is called.
try this:
var locationCallback = function(location){
// your fancy code here
};
function getLatLong(address){
var geo = new google.maps.Geocoder;
geo.geocode({'address':address},function(results, status){
if (status == google.maps.GeocoderStatus.OK) {
var returnedLatLng = [];
returnedLatLng["lat"] = results[0].geometry.location.lat();
returnedLatLng["lgn"] = results[0].geometry.location.lng();
locationTarget = new google.maps.LatLng(returnedLatLng.lat,returnedLatLng.lgn);
alert(locationTarget);
//this is your callback
locationCallback(locationTarget);
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
getLatLong(address)
I have tried reverse Geocoding with multiple requests at a time...
My coding is below:
var latlon=new Array();
var lname="";
latlon[0]=new array("11.19","71.20");
latlon[1]=new array("12.89","72.22");
latlon[2]=new array("13.49","73.64");
for(i=0;i<3;i++)
{
lname=locationname(latlon[i][0],latlon[i][1]);
alert(lname);
}
function locationname(lat,lon)
{
var llng = new google.maps.LatLng(lat,lon);
ligeocoder.geocode({'latLng': llng}, function(results, status)
{
if (status == google.maps.GeocoderStatus.OK)
{
if (results[0])
{
loname=results[0].formatted_address;
}
else
{
alert("result failed");
}
}
else
{
alert("Geocoder failed due to: " + status);
}
});
}
It shows error: Geocoder failed due to: OVER_QUERY_LIMIT
This is a limit of the Google geocoding API, not javascript. Basically google will not let you make too many requests at a time, there is no way to get around this limit without breaking the google api terms of service.
If you want to limit the amount of calls you're doing at a time, put your geocoding function in a setInterval.