How To Use Geolocation and Google Maps With Ionic

In this article we will look at implementing geolocation features in to our Ionic application, in particular presenting the location on an embedded Google Map. This feature can come in useful for many different scenarios, but for the purposes of this tutorial we will keep it simple and just present the user’s current location on a simple map.

One thing to take in to account is the date this article was published. I have used Geolocation with Ionic for just over a year, and the open-source projects I use are in constant development. That means, over time the steps required to reproduce the results from this article may differ slightly. If you find that this article has become outdated, please send a message so I can update it!

Getting Started

When writing this tutorial I was using Ionic version 1.3.2.

In this tutorial we will be using the following libraries:

The tutorial will assume that you have Ionic set up and installed in your development environment with access to the common command-line tools such as Bower. If you need help getting started with an Ionic development environment, watch this video.

Installing the plugins

We’ll start by installing our dependencies and plugins – Cordova should already be installed as it is a dependency of Ionic.

  1. Navigate to the directory of your Ionic project in the command line.
  2. Install ngCordova with Bower using the following command: bower install ngCordova
  3. Install ngCordova Geolocation by using the following command: cordova plugin add cordova-plugin-geolocation
  4. Install angular-google-maps with Bower by using the following command: bower install angular-google-maps
  5. Open the index.html file of your Ionic project and add the new JS scripts to the header. At the time of writing, these are ngcordova, angular-google-maps, lodash, angular-simple-logger. There may be more automatically-downloaded dependencies in the future. Ensure that the scripts are added in the correct order of dependency.
  6. Add the following tag to the header of your index.html file: <script src='//maps.googleapis.com/maps/api/js?sensor=false'></script>. It represents the Google Maps API and will allow us to display the Google Map embed in our application.
  7. Now add the angular-google-maps and ngCordova module as a dependency to your Ionic app. In most projects, this can be done in the app module declaration located in www/js/app.js. Your app module declaration should now look something like this:
    angular.module('myAppName', ['ionic', 'myAppName.controllers', 'myAppName.services', 'ngCordova', 'uiGmapgoogle-maps'])
    

Adding a blank map

I will be adding my map to a page called locator. I have already created a state, controller and template file as declared in my app.js file:

.state('app.locator', {
    url: '/locator',
    views: {
      'menuContent': {
        templateUrl: 'templates/locator.html',
        controller: 'locatorCtrl'
      }
    }
  })

Within the templates/locator.html file, add the following snippet where you want your map to appear. Ensure that you add the code within the ion-content tags.

<div id="map_canvas">
    <ui-gmap-google-map center="map.center" zoom="map.zoom" options="options"></ui-gmap-google-map>
</div>

Where map and options are both variables on the scope that we will declare next…

Now head over to your controller for your locator page and add the following dummy code:

$scope.map = {
    center: {
        latitude: 51.629415,
        longitude: 4.404418
    },
    zoom: 14
};

You will need to make sure that $scope has been injected in to your controller. Your controller should now look like this:

.controller('mapCtrl', function($scope) {
    $scope.map = {
        center: {
            latitude: 51.629415,
            longitude: 4.404418
        },
        zoom: 14
    };
})

The above co-ordinates should show Antwerp, Belgium with a zoom level of 14. Of course, you can play with the values and compare the results. Just make sure you use valid latitude and longitude values, and that your value for zoom is between 1 and 20 so that it satisfies the conditions outlined in the angular-google-maps documentation.

Finally, we need to add a line of CSS to control the height of our map. If you try loading your application now, you shouldn’t get any errors but you wont be able to see your map either. Open the www/css/style.css file and add the following line:

.angular-google-map-container {
    height: 300px; 
}

Adjust the value for height as required for your needs.

Now, if you load your application you should see a map displayed based on the latitude and longitude co-ordinates set above.

Angular Complexity With Geolocation

Things get a little more complicated from here. Without scaring you away from this page, let me briefly explain the problem:

When you load a page in Ionic, the Angular framework runs a digest loop. During this, the HTML is updated and the JavaScript is executed. So when we load the page with the map, the HTML is executed asynchronously (at the same time) with the JavaScript. The ui-gmap-google-map takes a required parameter called center which requires the latitude and longitude at which to centre the map. Since finding the geolocation of the user may take a few seconds, it is run asynchronously and that means the HTML may render before we get a location values for the user.

This means if you try to load the user’s geolocation in the controller, more often that not, the Google Map component will throw and error claiming that you are not passing it any latitude and longitude values. This means we need to pre-determine the user’s location before loading the locator state, view and controller.

Thankfully, the Angular UI-Router which comes bundled with Ionic provides State Resolves. These are bits of code which execute before a state, view or controller is loaded – and so we can use a resolve to determine the user’s location before loading the map!

Creating a State Resolve To Determine The Geolocation

Disclaimer: A State Resolve may not be the best way to work around the complexity mentioned above, but I think it’s the most logical and straight-forward.

Open up your project’s www/js/app.js file and scroll to the state for that you created earlier in this tutorial. In my case, it’s called app.locator.

Add the following resolve property to your state as shown below:

.state('app.locator', {
    url: '/locator',
    views: {
        'menuContent': {
            resolve: {
                userLocationInfo: function ($cordovaGeolocation) {
                var options = {timeout: 10000, enableHighAccuracy: true};

                return $cordovaGeolocation.getCurrentPosition(options)
                .then(function (response) {
                    var locationInfo = {
                        center: {
                            latitude: response.coords.latitude,
                            longitude: response.coords.longitude,
                        },
                        zoom: 16
                    };
                    return locationInfo;
                });
            }
        },
        templateUrl: 'templates/locator.html',
        controller: 'locatorCtrl'
    }
})

In the code above, we create a resolve variable called userLocationInfo which we can inject in to the controller locatorCtrl. The value of the userLocationInfo variable is determined by a function in to which we inject the $cordovaGeolocation service. We then make the asynchronous call to get the current position, and upon success we place the latitude and longitude values in to an object, set the zoom and then return the value. So once the function has completed execution, the value of the variable userLocationInfo is as follows:

userLocationInfo = {
    center: {
        latitude: 51.629415,
        longitude: 1.281927
    },
    zoom: 16
};

Please note that the above resolve is enough for the purposes of this tutorial, but for real applications you should consider implementing error handling incase the call to getCurrentPosition fails.

We can now go back to the locatorCtrl in www/js/controllers.js and inject the userLocationInfo value by adding it as a function parameter as follows:

.controller('mapCtrl', function($scope, $state, userLocationInfo) ...

Inside the controller we need to replace our existing $scope.map object with the values we resolved using Angular UI-Router. This can be simply done by removing our existing $scope.map declaration and replacing it with:

$scope.map = userLocationInfo;

If we reload our app once again, we should see that the map displayed represents our geolocation.

For more customizations to the map such as adding points of interest, satellite view and disabling zoom I would strongly suggest you check out the official angular-google-maps documentation.

Share on Facebook1Tweet about this on TwitterShare on Google+0Share on Reddit3Share on LinkedIn0Email this to someone

Subscribe to Email Updates