Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
An AngularJS Service for intelligently geocoding addresses using Google's API. Makes use of localStorage (via the ngStorage package) to avoid unnecessary trips to the server. Queries Google's API synchronously to avoid `google.maps.GeocoderStatus.OVER_QUERY_LIMIT`
/*global angular: true, google: true, _ : true */
'use strict';
angular.module('geocoder', ['ngStorage']).factory('Geocoder', function ($localStorage, $q, $timeout) {
var locations = $localStorage.locations ? JSON.parse($localStorage.locations) : {};
var queue = [];
// Amount of time (in milliseconds) to pause between each trip to the
// Geocoding API, which places limits on frequency.
var queryPause = 250;
/**
* executeNext() - 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 executeNext = function () {
var task = queue[0],
geocoder = new google.maps.Geocoder();
geocoder.geocode({ address : task.address }, function (result, status) {
if (status === google.maps.GeocoderStatus.OK) {
var latLng = {
lat: result[0].geometry.location.lat(),
lng: result[0].geometry.location.lng()
};
queue.shift();
locations[task.address] = latLng;
$localStorage.locations = JSON.stringify(locations);
task.d.resolve(latLng);
if (queue.length) {
$timeout(executeNext, queryPause);
}
} else if (status === google.maps.GeocoderStatus.ZERO_RESULTS) {
queue.shift();
task.d.reject({
type: 'zero',
message: 'Zero results for geocoding address ' + task.address
});
} else if (status === google.maps.GeocoderStatus.OVER_QUERY_LIMIT) {
queryPause += 250;
$timeout(executeNext, queryPause);
} else if (status === google.maps.GeocoderStatus.REQUEST_DENIED) {
queue.shift();
task.d.reject({
type: 'denied',
message: 'Request denied for geocoding address ' + task.address
});
} else if (status === google.maps.GeocoderStatus.INVALID_REQUEST) {
queue.shift();
task.d.reject({
type: 'invalid',
message: 'Invalid request for geocoding address ' + task.address
});
}
});
};
return {
latLngForAddress : function (address) {
var d = $q.defer();
if (_.has(locations, address)) {
$timeout(function () {
d.resolve(locations[address]);
});
} else {
queue.push({
address: address,
d: d
});
if (queue.length === 1) {
executeNext();
}
}
return d.promise;
}
};
});
@rahulchavan30

This comment has been minimized.

Copy link

@rahulchavan30 rahulchavan30 commented Nov 12, 2013

the function execute next is not getting fired, i have entered zip codes to the queue but nothing happens...what am i missing

@ninjaonsafari

This comment has been minimized.

Copy link

@ninjaonsafari ninjaonsafari commented Nov 27, 2013

f (_.has(locations, address)) {

Is there a way to do this without _?

@ninjaonsafari

This comment has been minimized.

Copy link

@ninjaonsafari ninjaonsafari commented Nov 27, 2013

and could you provide a example for the usage?

@mwawrusch

This comment has been minimized.

Copy link

@mwawrusch mwawrusch commented Dec 8, 2013

Great service, but does not survive minification. Can you change your gist like so:

angular.module('geocoder', ['ngStorage']).factory('Geocoder', ['$localStorage', '$q', '$timeout', function ($localStorage, $q, $timeout) {

and

} ]);

at the last line

How to use (in Coffeescript):

$scope.geocode = (address) ->
  p = Geocoder.latLngForAddress address

  fnSuccess = (latLng) ->
    $scope.data.latitude = latLng.lat
    $scope.data.longitude = latLng.lng

  fnError= () ->
    # not found somewhere.
    console.log "ERROR"
  p.then fnSuccess,fnError
@suras

This comment has been minimized.

Copy link

@suras suras commented Dec 31, 2013

thanks for the gist
similar way for getting latlng with location api and reverse geocoding
https://gist.github.com/suras/8196789

@TylerL-uxai

This comment has been minimized.

Copy link

@TylerL-uxai TylerL-uxai commented Mar 20, 2014

I'd like to see an example of this is used. It looks really promising!

@avaliani

This comment has been minimized.

Copy link

@avaliani avaliani commented Apr 8, 2014

Thanks for sharing. Very helpful.

BUG REPORT: you are missing a call to "$rootScope.$apply()" at line 62. When the resultCallback for "geocoder.geocode( resultCallback )" is invoked it is not part of the angular $digest cycle. Therefore, you need to explicitly call "$rootScope.$apply()" (and of course include $rootScope in your dependencies).

Once you do this then your code works like a charm!

Thank you.

@avaliani

This comment has been minimized.

Copy link

@avaliani avaliani commented Apr 9, 2014

BUG REPORT: Additionally, executeNext() is not being called if the response type is zero results, request denied or invalid result.

I've forked the gist to make the fixes. I've also simplified the query pause logic (didn't like the query pause timeout creeping upwards and I wanted to be more aggressive with retries). Link below:

https://gist.github.com/avaliani/10214857

@david-meza

This comment has been minimized.

Copy link

@david-meza david-meza commented Jan 27, 2016

Thank you for this gist. I took the liberty of forking it and modifying it to work with the angular-google-maps library.

Here's my gist if anyone wants to use it.

@saurabhudaniya200

This comment has been minimized.

Copy link

@saurabhudaniya200 saurabhudaniya200 commented Mar 11, 2016

_.has(locations, address) can be replaced with Object.keys(locations).indexOf(address) > -1

This can remove the _ dependency

@marlowBlackwood

This comment has been minimized.

Copy link

@marlowBlackwood marlowBlackwood commented Jun 3, 2016

@benmj thanks for posting this gist, and @david-meza, thanks for modifying it to work with angular-google-maps. I'm using angular-local-storage in my project, so I made a fork that works with it instead of ngStorage.

Here's the modified version

@vimal-aequalisys

This comment has been minimized.

Copy link

@vimal-aequalisys vimal-aequalisys commented Jul 12, 2016

Great service, When I try to integrate, I get the below error.
TypeError: Cannot read property 'geocode' of undefined
Could you please suggest me a fix?

Thanks

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