Including Google Maps into your AngularJS app

I already did this in the past but things change and the plugin I was using has changed too. I use AngularJS with Angular-Bootstrap directives as to save me some time and make the best of Bootstrap stylesheets I’m already using. angular-google-maps is another directive int he same club of bootstrap friends and this is how to include it into your project:

Installing packages

I’m using bower to manage packages at the front end, so first of all let’s install the required packages to make this work. I need the plugin angular-google-maps, which requires angular (obviously), lodash and angular-simple-logger to work. So just make sure you install all required packages using bower:

bower install --save-dev angular
bower install --save-dev angular-google-maps
bower install --save-dev angular-simple-logger
bower install --save-dev lodash

Configuring RequireJS

Just make sure that you set the paths and dependencies:

require.config({
paths: {
'angular': '../vendor/angular/angular.min',
'angular-animate': '../vendor/angular-animate/angular-animate.min',
'angular_bootstrap': "../vendor/angular-bootstrap/ui-bootstrap-tpls.min",
'angular-google-maps': '../vendor/angular-google-maps/dist/angular-google-maps.min',
'lodash': '../vendor/lodash/dist/lodash.min',
'angular-simple-logger': '../vendor/angular-simple-logger/dist/angular-simple-logger.min'
},
shim: {
'angular': { exports: 'angular' },
'angular-animate': { deps: ['angular'] },
'angular-simple-logger': { deps: ['angular'] },
'angular_bootstrap': { deps: ['angular', 'angular-animate'] },
'angular-google-maps': { deps: ['angular', 'lodash', 'angular-simple-logger'] },
}
});

Note: I’m also including some other libs in here just because my app uses them but also as I’m planning to display the google map in a modal, and we need certain other libs for that like angular-bootstrap.

Using it inside your AngularJS App

Now that we have the libraries loaded, we need to include them on our app like this:

define("MyAppsName", ['angular', 'angular_bootstrap', 'angular-google-maps'], function () {
var app = angular.module(GlobalConstants.appName, ['ui.bootstrap', 'uiGmapgoogle-maps']);
});

You may not need to use the define, that’s because I’m using RequireJS and I want to make sure certain modules are there befoer I declare the app.

Configuring the app to use GMaps provider

There is one thing you need to know about using GoogleMaps in your app. We need to load GMaps API v3 and we need it to be ready before we try to load a map using our angular-google-maps plugin. In the past, we had to load such lib asynchronously using RequireJS and the requirejs-async plugin. Now, we can use a provider already inside our angular-google-maps plugin to make sure GMaps API v3 is there before we try to use it:

// Provider to check directives are loaded before calling the map
app.config(['uiGmapGoogleMapApiProvider', function(GoogleMapApiProvider) {
GoogleMapApiProvider.configure({
//key: 'your api key',
libraries: 'weather,geometry,visualization'
});
}]
);

And then, on your controller:

function sendLocationController($scope, uiGmapGoogleMapApi) {

// Init
uiGmapGoogleMapApi.then(function(maps) { // Once GMaps directives are loaded -> instantiate the map
InstantiateTheMap();
TryGettingUserGeolocation();
});

function InstantiateTheMap(){
// ...
}
}

Instantiating a map

Ok, now that everything is ready, you probably noticed my InstantiateMap() function before, I didnd’t show what it has to avoid noise until we get there. AngularJS works using directives, and of course the way we are going to use GMaps is precisely by using directives so we will include something like this in our HTML (I’m including a map and a marker):

<ui-gmap-google-map center='map.center' zoom='map.zoom' events="map.events">
<ui-gmap-marker coords="marker.coords" options="marker.options" idkey="marker.id"></ui-gmap-marker>
</ui-gmap-google-map>

As you can see, those directives will consume two objects from the scope: map and marker (you can give them a different name, just making this simple). Now let’s fill those objects in the scope:

// This is the simple way of instantiating it with a default location:
function InstantiateTheMap(){
$scope.map = {
//default pos: Alan Turing's Memorial
center: { latitude: 53.476698, longitude: -2.2359644 },
zoom: 8,
options: {
streetViewControl: false,
panControl: true
}
};
}

// And this other one would allow you to set a location+zoom and a marker in it:
function positionMap(initialPosition, initialZoom) {
// Marker:
$scope.marker = {
id: "selectedPosition",
coords: {
latitude: initialPosition.Latitude,
longitude: initialPosition.Longitude
},
options: {
draggable: true,
// icon: icon
}
};

// Map
$scope.map = {
zoom: initialZoom,
center: {
latitude: initialPosition.Latitude,
longitude: initialPosition.Longitude
},
options: {
streetViewControl: false,
panControl: true
},
events: {
tilesloaded: function (map) {
$scope.$apply(function () {
//$log.info('this is the map instance', map);
});
},
}
};
}

This should allow you to display a map and add a marker to it into a certain position, I usually try to get the Geoposition of the user to center the map though.

Getting the user’s position

Thanks to HTML5 it’s now possible to get the user’s position using the Geolocation API. I created this service to help me:

require(["angular", GlobalConstants.appName], function () {

angular.module(GlobalConstants.appName).service("GeolocationService",
function GeolocationService() {

this.getUserCoordinates = function (onGettingTheCoordinates, onNotGettingTheCoordinates) {

// Get coords asynch.
if ("geolocation" in navigator) {
var location = {
Latitude: undefined,
Longitude: undefined
}

var geoOptions = {
enableHighAccuracy: true,
maximumAge: 30000,
timeout: 10000
};

function geoSuccess(position) {
location.Latitude = position.coords.latitude;
location.Longitude = position.coords.longitude;

if (onGettingTheCoordinates) {
onGettingTheCoordinates(location);
}
}

function geoFail() {
$rootScope.$emit("showErrorMessage", {
message: "meeeh",
});

if (onNotGettingTheCoordinates) {
onNotGettingTheCoordinates();
}
}

navigator.geolocation.getCurrentPosition(geoSuccess, geoFail, geoOptions);

} else {
// Geolocation is not available on your browser.
if (onNotGettingTheCoordinates) {
onNotGettingTheCoordinates();
}

$rootScope.$emit("showErrorMessage", {
message: " blah",
});
}

}

}
);
});

And then I can use it from my Controller (don’t forget to add the dependency!!):

function TryGettingUserGeolocation(){
GeolocationService.getUserCoordinates(function (coordinates) {
// Got user current coordinates:
positionMap(coordinates, 18);
}, function () {
//Didnt get them, adding default marker, adding a default one I chose:
var AlanTuringMemorialPos = new Coordinates({ Latitude: 53.476698, Longitude: -2.2359644 });
positionMap(AlanTuringMemorialPos, 15);
});
}