Build a Location-Based Mobile App With HTML5 & Javascript: 4
In the previous part of our series, you’ve seen how to configure “Where I parked my car” to adjust the theme, modify the pages’ transitions, and use the option to display text when showing the page loader widget. Thank to the Web Storage API, we’re now able to store the car’s positions for future use and see the positions’ history whenever we want. This article will explain how I used the Google Maps API to show stored locations on a map and plot a route from the user’s current position to the car. We’ll also delve into the utility functions that we’ll use to put all of the application’s components together.
The Google Maps API
Explaining the Google Maps API in detail is outside the scope of this article, but if you want an introduction to the API, you can read the series of articles I’m running on JSPro.com about Working with Geolocation and the Google Maps API.
The application we’re building makes three different requests to the Maps API: display the car’s position, finds a route from the user current position to the car, and retrieves the address of the car’s position. As you’ve seen for the Position
class in the previous part of the series, I’ll create a Map
class for all of its related methods. This class, which you’ll find inside the file maps.js
file, doesn’t really need properties. All the methods will be static, so the start point of the Map
class is simply the following:
function Map()
{
}
Displaying the Map
The method to display the map, called displayMap()
, will accept two arguments: the user’s position (userPosition
), and the car’s position(carPosition
). If only the first argument is provided (when the method has been called by the user to set his initial parking position) the icon will be shown accordingly. The first class of the Google Maps API you’ll see is google.maps.LatLng
, which represents geographical coordinates in latitude and longitude. I’ll use this class to convert the coordinates retrieved using the Geolocation API into something the Maps API can understand and work with. displayMap()
will show the map using the google.maps.Map
class and then set a marker to assign the user’s position using the google.maps.Marker
class. After setting the right icon, as explained before, the function draws a circle, with the google.maps.Circle
class, around the position to display the accuracy of the geolocation. If both the arguments are given, both position will be shown, and then the function will be called to calculate the route between the two locations.
The full source of this function is listed below.
/**
* Display the map showing the user position or the latter and the car position
*/
Map.displayMap = function(userPosition, carPosition)
{
var userLatLng = null;
var carLatLng = null;
if (userPosition != null)
userLatLng = new google.maps.LatLng(userPosition.coords.latitude, userPosition.coords.longitude);
if (carPosition != null)
carLatLng = new google.maps.LatLng(carPosition.position.latitude, carPosition.position.longitude);
var options = {
zoom: 20,
disableDefaultUI: true,
streetViewControl: true,
center: userLatLng,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var map = new google.maps.Map(document.getElementById('map'), options);
var marker = new google.maps.Marker({
position: userLatLng,
map: map,
title: 'Your position'
});
// If carLatLng is null means that the function has been called when the
// user set his current position and that is when he parked the car so the
// icon will be shown accordingly.
if (carLatLng == null)
marker.setIcon('images/car-marker.png');
else
marker.setIcon('images/user-marker.png');
var circle = new google.maps.Circle({
center: userLatLng,
radius: userPosition.coords.accuracy,
map: map,
fillColor: '#70E7FF',
fillOpacity: 0.2,
strokeColor: '#0000FF',
strokeOpacity: 1.0
});
map.fitBounds(circle.getBounds());
if (carLatLng != null)
{
marker = new google.maps.Marker({
position: carLatLng,
map: map,
icon: 'images/car-marker.png',
title: 'Car position'
});
circle = new google.maps.Circle({
center: carLatLng,
radius: carPosition.position.accuracy,
map: map,
fillColor: '#70E7FF',
fillOpacity: 0.2,
strokeColor: '#0000FF',
strokeOpacity: 1.0
});
// Display route to the car
options = {
suppressMarkers: true,
map: map,
preserveViewport: true
}
this.setRoute(new google.maps.DirectionsRenderer(options), userLatLng, carLatLng);
}
$.mobile.loading('hide');
}
Retrieve Route
Once the user has set his position, he can walk around to whatever location he wants. After, he’ll need to return to his car and that’s where this next function will be used. To find a route from the user’s current position to the car, I’ll create a function called setRoute()
. It’ll use the google.maps.DirectionsService
and the google.maps.DirectionsRequest
class to ask Google to calculate the route. Using google.maps.DirectionsTravelMode
and google.maps.UnitSystem
, I’ll ask to Google to retrieve a walking path (we know the user will be on foot), so that the user can also use one-way streets, and view the walking distance in meters. In conclusion, the function will use the google.maps.DirectionsStatus
class to test if at least one result has been found, and it will employ the google.maps.DirectionsRenderer
class to actually display the route.
The full method’s code is listed below.
/**
* Calculate the route from the user to his car
*/
Map.setRoute = function(directionsDisplay, userLatLng, carLatLng)
{
var directionsService = new google.maps.DirectionsService();
var request = {
origin: userLatLng,
destination: carLatLng,
travelMode: google.maps.DirectionsTravelMode.WALKING,
unitSystem: google.maps.UnitSystem.METRIC
};
directionsService.route(
request,
function(response, status)
{
if (status == google.maps.DirectionsStatus.OK)
directionsDisplay.setDirections(response);
else
{
navigator.notification.alert(
'Unable to retrieve a route to your car. However, you can still find it by your own.',
function(){},
'Warning'
);
}
}
);
}
Retrieve Address
The last method I’ll describe is requestLocation()
, which will be used to retrieve the address of the car’s position and update the lasted stored object that contains the last position, so that when the user sees the positions’ history they will see meaningful text instead of GPS coordinates. To achieve this goal, I’ll use the google.maps.Geocoder
class that lets you convert from a LatLng
object to an address, and vice versa. The latter has just one method, called geocode()
, which accepts a GeocoderRequest
object as a parameter and a callback function that’s used to read the results.
The code of the illustrated function is the following.
/**
* Request the address of the retrieved location
*/
Map.requestLocation = function(position)
{
new google.maps.Geocoder().geocode(
{
'location': new google.maps.LatLng(position.coords.latitude, position.coords.longitude)
},
function(results, status)
{
if (status == google.maps.GeocoderStatus.OK)
{
var positions = new Position();
positions.updatePosition(0, positions.getPositions()[0].coords, results[0].formatted_address);
}
}
);
}
Utility Functions
This section describes several of the functions within the file called functions.js
, the last JavaScript file that powers our mobile application.
Testing for Requirements
“Where I parked my car” doesn’t have a lot of requirements in order to work correctly—just a working Internet connection to be able to use the Google Maps API to execute the functions explained in the previous sections. If the connection isn’t available, We’ll show the user a warning message, using the Cordova Notification API illustrated in the third part of the series, and stop the execution of the method. This test is done using the Cordova Connection API, an object that gives access to the device’s cellular and wifi connection information. This API has just one property called type
that checks the active network connection that is being used and can assume the following (constants) values:
- Connection.UNKNOWN
- Connection.ETHERNET
- Connection.WIFI
- Connection.CELL_2G
- Connection.CELL_3G
- Connection.CELL_4G
- Connection.NONE
As you’ve seen when we talked about the Web Storage API, the type
property has a few quirks that are detailed in the Connection API official documentation.
Now that you learned how the API will be used, I can show you what the check requirements function (check for Internet connection) looks like.
function checkRequirements()
{
if (navigator.network.connection.type == Connection.NONE)
{
navigator.notification.alert(
'To use this app you must enable your internet connection',
function(){},
'Warning'
);
return false;
}
return true;
}
Update Icons Based on the Screen Size
One of the hottest topics of the last few years is surely Responsive Web Design. Citing Wikipedia, Responsive Web Design is an approach to web design in which a site is crafted to provide an optimal viewing experience—easy reading and navigation with a minimum of resizing, panning, and scrolling—across a wide range of devices.
I won’t get into the details of RWD, but having used jQuery Mobile to create mobile application interfaces, I’m familiar with the concept. I tried to improve the default interface of the application using a simple method that checks for the device’s screen size and decides if the buttons inside the header and the footer should display the buttons’ text. This is done using the attribute data-iconpos="notext"
. In the first case, the text is hidden, while in the second, it’s shown. By default, I put with the value notext
to all the buttons of the header or the footer. As the screen breakpoint, I used the value 480px, so devices that have a screen’s width of less than 480 pixels will not display the buttons’ text.
The code of the illustrated function is listed below.
function updateIcons()
{
if ($(window).width() > 480)
{
$('a[data-icon], button[data-icon]').each(
function()
{
$(this).removeAttr('data-iconpos');
}
);
}
else
{
$('a[data-icon], button[data-icon]').each(
function()
{
$(this).attr('data-iconpos', 'notext');
}
);
}
}
Conclusion
In this fourth part of the series, you’ve learned to use the Google Maps API to display the map and retrieve the route between the user’s position and his car. We used the google.maps.Geocoder
class to update the last stored object. In the second section of this article, we learned some of the functions that you’ll see in the file function.js
. In the next article, I’ll explain and show the remaining methods of that file.