Geolocalisation and Address Completion With Angular and Google Map API

After few days without writting anything, I have now many ideas of things to share, mostly about Angular. So today I will speak about geolocalisation with Angular.

If you don’t know, with html5 you can now easily know exactly where your user is, the navigator will send you the informations from the GPS, the IP address, the GSM network or wathever. For the developper, this is really simple, the navigator provide an API where you just have to call the function getCurrentPosition of navigator.geolocation and thats all :)

So today I wanted to use this API to know the position and then automatically fill the address field.

For that there is something interesting in Angular, is the possibility to create some services, a kind of module just to do something special like give the address of a user.

geolocalisation.coffee
1
2
3
4
5
6
7
8
9
10
App = angular.module 'app', []
App.service "Geolocation", ['$q', '$window', ($q, $window) ->
  @getLocation = ->

  @getAddress = ->

  @extractAddress = (addresses) ->

  @
]
geolocalisation.js
1
2
3
4
5
6
7
8
9
10
11
12
var App;

App = angular.module('app', []);

App.service("Geolocation", [
  '$q', '$window', function($q, $window) {
    this.getLocation = function() {};
    this.getAddress = function() {};
    this.extractAddress = function(addresses) {};
    return this;
  }
]);
Toggle coffeescript/javascript

Ok now our service is created. We will have to add the google map api script to be able to convert coordinate into address.

Let’s write the first function to get the location, this function should return the coordonate of your user (latitude and longitude).

geolocalisation.coffee
1
2
3
4
5
6
7
8
9
10
@getLocation = ->
  deferred = $q.defer()
  if $window.navigator and $window.navigator.geolocation
    $window.navigator.geolocation.getCurrentPosition (position) ->
      deferred.resolve position.coords
    , (error) ->
      deferred.reject "Unable to get your location"
  else
    deferred.reject "Your browser cannot access to your position"
  deferred.promise
geolocalisation.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
this.getLocation = function() {
  var deferred;
  deferred = $q.defer();
  if ($window.navigator && $window.navigator.geolocation) {
    $window.navigator.geolocation.getCurrentPosition(function(position) {
      return deferred.resolve(position.coords);
    }, function(error) {
      return deferred.reject("Unable to get your location");
    });
  } else {
    deferred.reject("Your browser cannot access to your position");
  }
  return deferred.promise;
};
Toggle coffeescript/javascript

Nothing complicated here (except perhaps the use of promise / I have to write something about this also), we just use the API from the browser with a callback in parameter to have the position and we return only the coordonates (because I just need this but you can return all).

Next part, the getAddress function.

geolocalisation.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@getAddress = ->
  @geocoder ||= new google.maps.Geocoder()
  deferred    = $q.defer()
  @getLocation().then (coords) =>
    latlng = new google.maps.LatLng coords.latitude, coords.longitude
    @geocoder.geocode latLng: latlng, (results, status) =>
      if status is google.maps.GeocoderStatus.OK
        deferred.resolve @extractAddress results
      else
        deferred.reject "cannot geocode status: #{status}"
    , ->
      deferred.reject "cannot geocode"

  deferred.promise
geolocalisation.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
this.getAddress = function() {
  var deferred,
    _this = this;
  this.geocoder || (this.geocoder = new google.maps.Geocoder());
  deferred = $q.defer();
  this.getLocation().then(function(coords) {
    var latlng;
    latlng = new google.maps.LatLng(coords.latitude, coords.longitude);
    return _this.geocoder.geocode({
      latLng: latlng
    }, function(results, status) {
      if (status === google.maps.GeocoderStatus.OK) {
        return deferred.resolve(_this.extractAddress(results));
      } else {
        return deferred.reject("cannot geocode status: " + status);
      }
    }, function() {
      return deferred.reject("cannot geocode");
    });
  });
  return deferred.promise;
};
Toggle coffeescript/javascript

Once again, nothing so hard, we instanciate a google.maps.Geocoder object (provided by the google map script) if needed then we call the geocode function with the latitude and longitude from the previous function. This will call google map API and return a list of results with many addresses. We just have to extract the address from the result if everything is ok and resolve the promise (like send a success callback).

Last function, the extractAddress

geolocalisation.coffee
1
2
3
4
5
6
7
8
9
10
11
@extractAddress = (addresses) ->
  result = {}
  for address in addresses
    result.fullAddress ||= address.formatted_address
    result.coord       ||= [address.geometry.location.ob, address.geometry.location.pb]
    for component in address.address_components
      result.street  ||= component.long_name if component.types[0] is "route"
      result.city    ||= component.long_name if component.types[0] is "locality"
      result.zip     ||= component.long_name if component.types[0] is "postal_code"
      result.country ||= component.long_name if component.types[0] is "country"
  result
geolocalisation.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
this.extractAddress = function(addresses) {
  var address, component, result, _i, _j, _len, _len1, _ref;
  result = {};
  for (_i = 0, _len = addresses.length; _i < _len; _i++) {
    address = addresses[_i];
    result.fullAddress || (result.fullAddress = address.formatted_address);
    result.coord || (result.coord = [address.geometry.location.ob, address.geometry.location.pb]);
    _ref = address.address_components;
    for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
      component = _ref[_j];
      if (component.types[0] === "route") {
        result.street || (result.street = component.long_name);
      }
      if (component.types[0] === "locality") {
        result.city || (result.city = component.long_name);
      }
      if (component.types[0] === "postal_code") {
        result.zip || (result.zip = component.long_name);
      }
      if (component.types[0] === "country") {
        result.country || (result.country = component.long_name);
      }
    }
  }
  return result;
};
Toggle coffeescript/javascript

This is an implementation of course it depends of what you need but here I just check all results and I fill the data I want when I found it in the result.

Now we have everything for our service so if you want to get the address of a user you just need to write 2 lines.

controller.coffee
1
2
3
4
...
Geolocation.getAddress().then (address) ->
  $scope.model.address = address
...

Comments

Copyright © 2014 - Anthony Estebe -