Skip to content

Instantly share code, notes, and snippets.

@david-meza
Forked from benmj/geocoder-service.js
Last active July 19, 2017 17:22
Show Gist options
  • Save david-meza/ead53923c9f468eecc21 to your computer and use it in GitHub Desktop.
Save david-meza/ead53923c9f468eecc21 to your computer and use it in GitHub Desktop.
An AngularJS Service for intelligently geocoding addresses using Google's API. Queries Google's API synchronously to avoid `google.maps.GeocoderStatus.OVER_QUERY_LIMIT`
(function(angular) {
'use strict';
angular.module('geocoder', ['uiGmapgoogle-maps', 'ngStorage']).factory('geocoderService', ['$q', '$timeout', 'uiGmapGoogleMapApi', '$localStorage',
function ($q, $timeout, mapsApi, $localStorage) {
var locations = $localStorage.locations ? JSON.parse($localStorage.locations) : {};
console.log(locations);
var queue = [];
var geocoder;
// Assign the google.maps object when the API is ready
var google = {};
mapsApi.then( function (maps) {
google.maps = maps;
geocoder = new google.maps.Geocoder();
geocodeNextAddress();
});
// Amount of time (in milliseconds) to pause between each trip to the
// Geocoding API, which places limits on frequency.
var QUERY_PAUSE = 250;
/**
* geocodeNextAddress() - execute the next function in the queue.
* If a result is returned, fulfill the promise.
* If we get an error, reject the promise (with message).
* If we receive OVER_QUERY_LIMIT, increase interval and try again.
*/
var geocodeNextAddress = function () {
if (!geocoder) { $timeout(geocodeNextAddress, QUERY_PAUSE); }
// Don't do anything if there aren't any tasks left
if (!queue.length) { return; }
// Get the next task (though not shift from queue yet, until it is finally resolved)
var task = queue[0];
// If we already processed this address return the stored results and go on to the next item in the queue
if (locations.hasOwnProperty(task.address)) {
queue.shift();
task.d.resolve(locations[task.address]);
if (queue.length) { return geocodeNextAddress(); }
// Otherwise get the results from the geocoder service
} else {
geocoder.geocode({ address : task.address }, function (result, status) {
// Resolve the promise with the results and reset the pause to the default
if (status === google.maps.GeocoderStatus.OK) {
var parsedResult = {
lat: result[0].geometry.location.lat(),
lng: result[0].geometry.location.lng(),
formattedAddress: result[0].formatted_address
};
locations[task.address] = parsedResult;
$localStorage.locations = JSON.stringify(locations);
task.d.resolve(parsedResult);
QUERY_PAUSE = 250;
// Increase the pause up to 1s intervals and keep trying...
} else if (status === google.maps.GeocoderStatus.OVER_QUERY_LIMIT) {
QUERY_PAUSE < 1000 ? QUERY_PAUSE += 250 : QUERY_PAUSE = 1000;
// Reject any other result
} else if (status === google.maps.GeocoderStatus.ZERO_RESULTS) {
task.d.reject({
type: 'zero',
message: 'Zero results for geocoding address ' + task.address
});
} else if (status === google.maps.GeocoderStatus.REQUEST_DENIED) {
task.d.reject({
type: 'denied',
message: 'Request denied for geocoding address ' + task.address
});
} else {
task.d.reject({
type: 'invalid',
message: 'Invalid request for geocoding: status=' + status + ', address=' + task.address
});
}
// Remove from queue if the promise has been resolved or rejected
if (status !== google.maps.GeocoderStatus.OVER_QUERY_LIMIT) { queue.shift(); }
// Go on to the next item in the queue
if (queue.length) { $timeout(geocodeNextAddress, QUERY_PAUSE); }
});
}
};
// Publically available function to push addresses to the queue. Returns the promise so it can be chained with .then
var getLatLng = function (address) {
var d = $q.defer();
queue.push({
address: address,
d: d
});
if (queue.length === 1) { geocodeNextAddress(); }
return d.promise;
};
return {
getLatLng : getLatLng
};
}]);
})(window.angular);
@CarMoreno
Copy link

Would be cool if you could make a tiny example using this service 😄

@StasKolodyuk
Copy link

StasKolodyuk commented Apr 12, 2017

@david-meza, @vimal-aequalisys, @pietromarrone, Seems like line 33 should be like this:
if (!geocoder) { $timeout(geocodeNextAddress, QUERY_PAUSE); return; }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment