Using Location Data with PhoneGap

Exclusive offer: get 50% off this eBook here
PhoneGap 3 Beginner's Guide

PhoneGap 3 Beginner's Guide — Save 50%

A guide to building cross-platform apps using the W3C standards-based Cordova/PhoneGap framework with this book and ebook

€20.99    €10.50
by Giorgio Natili | October 2013 | Beginner's Guides Open Source

In this article by Giorgio Natili, the author of the book PhoneGap 3 Beginner's Guide, you will:

  • Learn about geolocation and how its data are available in the device
  • Explore the differences between the HTML5 and the PhoneGap Geolocation APIs
  • Learn how to use the PhoneGap Geolocation API and how to integrate the Google Maps API in an app
  • Learn how to use Geolocation data in conjunction with external service providers such as Google Places

(For more resources related to this topic, see here.)

An introduction to Geolocation

The term geolocation is used in order to refer to the identification process of the real-world geographic location of an object. Devices that are able to detect the user's position are becoming more common each day and we are now used to getting content based on our location ( geo targeting ).

Using the Global Positioning System (GPS )—a space-based satellite navigation system that provides location and time information consistently across the globe—you can now get the accurate location of a device. During the early 1970s, the US military created Navstar, a defense navigation satellite system. Navstar was the system that created the basis for the GPS infrastructure used today by billions of devices. Since 1978 more than 60 GPS satellites have been successfully placed in the orbit around the Earth (refer to http://en.wikipedia.org/wiki/List_of_GPS_satellite_launches for a detailed report about the past and planned launches).

The location of a device is represented through a point. This point is comprised of two components: latitude and longitude. There are many methods for modern devices to determine the location information, these include:

  • Global Positioning System (GPS)
  • IP address
  • GSM/CDMA cell IDs
  • Wi-Fi and Bluetooth MAC address

Each approach delivers the same information; what changes is the accuracy of the device's position. The GPS satellites continuously transmit information that can parse, for example, the general health of the GPS array, roughly, where all of the satellites are in orbit, information on the precise orbit or path of the transmitting satellite, and the time of the transmission. The receiver calculates its own position by timing the signals sent by any of the satellites in the array that are visible.

The process of measuring the distance from a point to a group of satellites to locate a position is known as trilateration . The distance is determined using the speed of light as a constant along with the time that the signal left the satellites.

The emerging trend in mobile development is GPS-based "people discovery" apps such as Highlight, Sonar, Banjo, and Foursquare. Each app has different features and has been built for different purposes, but all of them share the same killer feature: using location as a piece of metadata in order to filter information according to the user's needs.

The PhoneGap Geolocation API

The Geolocation API is not a part of the HTML5 specification but it is tightly integrated with mobile development. The PhoneGap Geolocation API and the W3C Geolocation API mirror each other; both define the same methods and relative arguments. There are several devices that already implement the W3C Geolocation API; for those devices you can use native support instead of the PhoneGap API.

As per the HTML specification, the user has to explicitly allow the website or the app to use the device's current position.

The Geolocation API is exposed through the geolocation object child of the navigator object and consists of the following three methods:

  • getCurrentPosition() returns the device position.
  • watchPosition() watches for changes in the device position.
  • clearWatch() stops the watcher for the device's position changes.

The watchPosition() and clearWatch() methods work in the same way that the setInterval() and clearInterval() methods work; in fact the first one returns an identifier that is passed in to the second one. The getCurrentPosition() and watchPosition() methods mirror each other and take the same arguments: a success and a failure callback function and an optional configuration object. The configuration object is used in order to specify the maximum age of a cached value of the device's position, to set a timeout after which the method will fail and to specify whether the application requires only accurate results.

var options = {maximumAge: 3000, timeout: 5000, enableHighAccuracy: true }; navigator.geolocation.watchPosition(onSuccess, onFailure, options);

Only the first argument is mandatory; but it's recommended to handle always the failure use case.

The success handler function receives as argument, a Position object. Accessing its properties you can read the device's coordinates and the creation timestamp of the object that stores the coordinates.

function onSuccess(position) { console.log('Coordinates: ' + position.coords); console.log('Timestamp: ' + position.timestamp); }

The coords property of the Position object contains a Coordinates object; so far the most important properties of this object are longitude and latitude. Using those properties it's possible to start to integrate positioning information as relevant metadata in your app.

The failure handler receives as argument, a PositionError object. Using the code and the message property of this object you can gracefully handle every possible error.

function onError(error) { console.log('message: ' + error.message); console.log ('code: ' + error.code); }

The message property returns a detailed description of the error, the code property returns an integer; the possible values are represented through the following pseudo constants:

  • PositionError.PERMISSION_DENIED, the user denies the app to use the device's current position
  • PositionError.POSITION_UNAVAILABLE, the position of the device cannot be determined

    If you want to recover the last available position when the POSITION_UNAVAILABLE error is returned, you have to write a custom plugin that uses the platform-specific API. Android and iOS have this feature. You can find a detailed example at http://stackoverflow.com/questions/10897081/retrieving-last-known-geolocation-phonegap.

  • PositionError.TIMEOUT, the specified timeout has elapsed before the implementation could successfully acquire a new Position object

    JavaScript doesn't support constants such as Java and other object-oriented programming languages. With the term "pseudo constants", I refer to those values that should never change in a JavaScript app.

One of the most common tasks to perform with the device position information is to show the device location on a map. You can quickly perform this task by integrating Google Maps in your app; the only requirement is a valid API key. To get the key, use the following steps:

  1. Visit the APIs console at https://code.google.com/apis/console and log in with your Google account.
  2. Click the Services link on the left-hand menu.
  3. Activate the Google Maps API v3 service.

Time for action – showing device position with Google Maps

Get ready to add a map renderer to the PhoneGap default app template. Refer to the following steps:

  1. Open the command-line tool and create a new PhoneGap project named MapSample.

    $ cordova create ~/the/path/to/your/source/ mapmample com.gnstudio.pg.MapSample MapSample

  2. Add the Geolocation API plugin using the command line.

    $ cordova plugins add https: //git-wip-us.apache.org /repos/asf/cordova-plugin-geolocation.git

  3. Go to the www folder, open the index.html file, and add a div element with the id value #map inside the main div of the app below the #deviceready one.

    <div id='map'></div>

  4. Add a new script tag to include the Google Maps JavaScript library.

    <script type="text/javascript" src ="https: //maps.googleapis.com/maps/api/js?key= YOUR_API_KEY &sensor=true"> </script>

  5. Go to the css folder and define a new rule inside the index.css file to give to the div element and its content an appropriate size.

    #map{ width: 280px; height: 230px; display: block; margin: 5px auto; position: relative; }

  6. Go to the js folder, open the index.js file, and define a new function named initMap.

    initMap: function(lat, long){ // The code needed to show the map and the // device position will be added here }

  7. In the body of the function, define an options object in order to specify how the map has to be rendered.

    var options = { zoom: 8, center: new google.maps.LatLng(lat, long), mapTypeId: google.maps.MapTypeId.ROADMAP };

  8. Add to the body of the initMap function the code to initialize the rendering of the map, and to show a marker representing the current device's position over it.

    var map = new google.maps.Map(document.getElementById('map'), options); var markerPoint = new google.maps.LatLng(lat, long); var marker = new google.maps.Marker({ position: markerPoint, map: map, title: 'Device\'s Location' });

  9. Define a function to use as the success handler and call from its body the initMap function previously defined.

    onSuccess: function(position){ var coords = position.coords; app.initMap(coords.latitude, coords.longitude); }

  10. Define another function in order to have a failure handler able to notify the user that something went wrong.

    onFailure: function(error){ navigator.notification.alert(error.message, null); }

  11. Go into the deviceready function and add as the last statement the call to the Geolocation API needed to recover the device's position.

    navigator.geolocation.getCurrentPosition(app.onSuccess, app.onError, {timeout: 5000, enableAccuracy: false});

  12. Open the command-line tool, build the app, and then run it on your testing devices.

    $ cordova build $ cordova run android

What just happened?

You integrated Google Maps inside an app. The map is an interactive map most users are familiar with—the most common gestures are already working and the Google Street View controls are already enabled.

To successfully load the Google Maps API on iOS, it's mandatory to whitelist the googleapis.com and gstatic.com domains. Open the .plist file of the project as source code (right-click on the file and then Open As | Source Code ) and add the following array of domains:

<key>ExternalHosts</key> <array> <string>*.googleapis.com</string> <string>*.gstatic.com</string> </array>

Other Geolocation data

In the previous example, you only used the latitude and longitude properties of the position object that you received. There are other attributes that can be accessed as properties of the Coordinates object:

  • altitude, the height of the device, in meters, above the sea level.
  • accuracy, the accuracy level of the latitude and longitude, in meters; it can be used to show a radius of accuracy when mapping the device's position.
  • altitudeAccuracy, the accuracy of the altitude in meters.
  • heading, the direction of the device in degrees clockwise from true north.
  • speed, the current ground speed of the device in meters per second.

Latitude and longitude are the best supported of these properties, and the ones that will be most useful when communicating with remote APIs. The other properties are mainly useful if you're developing an application for which Geolocation is a core component of its standard functionality, such as apps that make use of this data to create a flow of information contextualized to the geolocation data. The accuracy property is the most important of these additional features, because as an application developer, you typically won't know which particular sensor is giving you the location and you can use the accuracy property as a range in your queries to external services.

There are several APIs that allow you to discover interesting data related to a place; among these the most interesting are the Google Places API and the Foursquare API.

The Google Places and Foursquare online documentation is very well organized and it's the right place to start if you want to dig deeper into these topics. You can access the Google Places docs at https://developers.google.com/maps/documentation/javascript/places and Foursquare at https://developer.foursquare.com/.

The itinero reference app for this article implements both the APIs. In the next example, you will look at how to integrate Google Places inside the RequireJS app.

In order to include the Google Places API inside an app, all you have to do is add the libraries parameter to the Google Maps API call. The resulting URL should look similar to http://maps.google.com/maps/api/js?key=SECRET_KEY&sensor=true&libraries=places.

The itinero app lets users create and plan a trip with friends. Once the user provides the name of the trip, the name of the country to be visited, and the trip mates and dates, it's time to start selecting the travel, eat, and sleep options.

When the user selects the Eat option, the Google Places data provider will return bakeries, take-out places, groceries, and so on, close to the trip's destination. The app will show on the screen a list of possible places the user can select to plan the trip.

For a complete list of the types of place searches supported by the Google API, refer to the online documentation at https://developers.google.com/places/documentation/supported_types.

PhoneGap 3 Beginner's Guide A guide to building cross-platform apps using the W3C standards-based Cordova/PhoneGap framework with this book and ebook
Published: September 2013
eBook Price: €20.99
Book Price: €34.99
See more
Select your format and quantity:

Time for action – discovering places with Google Places

Execute the following steps:

  1. Add the Geolocation API plugin using the command line.

    $ cordova plugins add https: //git-wip-us.apache.org /repos/asf/cordova-plugin-geolocation.git

  2. Download the async RequireJS plugin from GitHub at https://github.com/millermedeiros/requirejs-plugins/blob/master/src/async.js and save it to the js/libs/require/plugins folder. This plugin allows you to asynchronously load external libraries or dependencies. In this example, it will be used to load the Google Places API.
  3. Add the path to the async plugin into the configuration of RequireJS you already defined in the main.js file.

    require.config({ paths: { // All the already defined paths async: 'libs/require/plugins/async' }, // Other configurations });

  4. Go to the js/utils folder and create a new RequireJS module named gmaps. This module allows your app to download the Google Places API only when requested by another module; use the module's name to access the Google Maps API.

    ;define('utils/ gmaps' , ['async!http: //maps.google.com/maps/ api/js?key=YOUR_KEY&sensor=true& libraries =places'], function(){ return window.google.maps; });

  5. Create a new Mustache template named create-find-places-tpl.html to show to the user the available data providers (i.e. Google and FourSquare), to search for and return places in the selected city/country, and to save it in the tpl folder.

    <section id='create-find-places' class='gray-background'> <div class="results-vertical"> <h1 class='resultsText'>{{searchingFor}}</h1> <h2 class='resultsText'>{{place}}</h2> <ul class='placeproviders'> <li id='google'><a href='google'>{{google}}</a></li> <li id='square'><a href='square'>{{square}}</a></li> </ul> <div id='map'></div> </div> <div id='places-results'></div> </section>

  6. Create a new RequireJS module named FindPlaceView.js, define its dependencies, and save it into the js/views/create/findplaces folder (the module uses the templateProvider utility to load a new template, Mustache to add the results on the screen, and the spinner utility to show when the app is loading new data). The content of the file will look like the following snippet of code.

    ;define('views/create/findplaces/FindPlacesView', ['utils/ templateProvider' , 'mustache' , 'utils/ spinner' ], (function(tplProvider, mustache, spinner){ // The module variables and functions will go here }));

  7. Create another RequireJS module named FindPlaces.js with an explicit dependency to the module FindPlaceView.js and save it into the js/controllers folder. The content of the file will look like the following snippet of code:

    ;define('controllers/FindPlaces', ['views/create/findplaces/FindPlacesView'],(function(view){ // The module variables and functions will go here }));

  8. Open the FindPlaces.js module and define a function to init the module and another one to start it and expose both functions using a return object. The init function stores the data needed by the template and the city used as search target; the start function initializes the view. The init function also registers a listener in order to enable the interactivity on the Google Places and Foursquare selectors only when the template is loaded.

    function init (data, city){ section = data; targetCity = city; addEventListener('initFinders', onInitFinders); } function start (){ view.render(section.template, section.data); } return{ init: init, start: start }

  9. Define the onInitFinders listener and add to its body a call to the Array.prototype.forEach method in order to define the click event listener for all the <a> tags used to render the data provider options.

    function oninitFinders(evt) { var links = document.getElementsByClassName ('placeproviders')[0].getElementsByTagName('a'); forEach.call(links, function(link){ link.addEventListener('click', getPlaces); }); }

  10. Open the FindPlaceView.js module previously created, define the render function, and expose it using the return object. The render function loads a new template and defines a listener for the templateReady event.

    function getPlaces(evt) { var provider = this.href; if(provider.indexOf('google') >= 0){ loadFromGoogle(); }else if(provider.indexOf('foursquare') >= 0){ // The code to use FourSquare is available online } evt.preventDefault(); }

  11. Define the onTemplateReady function in the FindPlaceView.js file and inside its body add the code needed to populate the template and to notify the FindPlaces.js controller that it can define the listeners for the data source selectors.

    function onTemplateReady(event) { var app = document.querySelector('div.app'); app.innerHTML = event.detail.html; var event = document.createEvent("Event"); event.initEvent("initFinders", true, true); this.dispatchEvent(event); }

  12. To complete the FindPlacesView.js module, define and expose a function to append data to the places-results div tag part of the create-find-places-tpl.html template.

    function results (value) { var tpl = '<ul>{{#places}}<li><img src ="{{icon}}" >'; tpl += '<a href="#">{{name}} - {{vicinity}}</a>'; tpl += '</li>{{/places}}</ul>'; var target = document.getElementById('places-results'); target.innerHTML = mustache.to_html(tpl, value); }

  13. Go back to the FindPlace.js module, define the getPlaces event listener, and call the method to load data using the Google Places API or the Foursquare API, depending on the user's selection.

    function getPlaces(evt) { var provider = this.href; if(provider.indexOf('google') >= 0){ loadFromGoogle(); }else if(provider.indexOf('foursquare') >= 0){ // The code to use FourSquare is available online } evt.preventDefault(); }

  14. In the same file, add the function to load the data using the Google Places API. The first thing the function has to do is to require the gmaps module already defined and waiting until the API is available.

    function loadFromGoogle() { require(['utils/gmaps'], function(gmaps){ // Do some stuff when the API is available }); }

  15. When the API is available, the anonymous function used as an argument in the require function has to create a new Geocoder object in order to recover the latitude and longitude of the target city. The Geocoder object is created in the js/controllers/FindPlaces.js file.

    var geocoder = new gmaps.Geocoder(); var address = targetCity; geocoder.geocode({'address': address}, function(results, status) { // Start to search data });

  16. When the value of the status argument used within the geocode method handler is OK you can recover the latitude and the longitude needed to define the center of the map to be used to perform the places search.

    if (status == gmaps.GeocoderStatus.OK) { var latitude = results[0].geometry.location.lat(); var longitude = results[0].geometry.location.lng(); var center = new gmaps.LatLng(latitude, longitude); var mapDiv = document.getElementById('map'); var options = { mapTypeId: gmaps.MapTypeId.ROADMAP, center: center, zoom: 9, radius: 50000 }; var map = new gmaps.Map(mapDiv, options); // The request to the API will go here }

  17. Inside the conditional statement, add a request object to specify the center of the request, the radius, and the types of places. Use the object to start a Nearby Search (a search for places within a specified area by keyword or type) and define a listener for the results.

    var request = { location: center, radius: '500', types: ['bakery', 'meal_takeaway', 'cafe'] }; var service = new gmaps.places.PlacesService(map); service. nearbySearch (request, onResult );

  18. Create the onResult function. The function receives two arguments, one is the search result and the other is the status of the research; according to the request status, populate the places property of the currentData object and then populate the view with the recovered data.

    function onResult(results, status) { if(status == google.maps.places.PlacesServiceStatus.OK){ var currentData = {places: []}; for (var i = 0; i < results.length; i++) { var place = results[i]; var data = {}; data.name = place.name; data.vicinity = place.vicinity; data.icon = place.icon; currentData.places.push(data); } view.results(currentData); } }

    The properties name of the currentData object and the variables defined in the template snippet used in the FindPlacesView.js module match.

  19. Open the Create.js file saved in the js/controllers folder and add the snippet of code needed to load the new controller.

    var section = {template: 'create-find-places-tpl.html', data: { searchingFor: 'Places to eat something', place: 'Rome - IT', google: 'Google Places', foursquare: 'FourSquare' }}; require(['controllers/FindPlaces'], function(controller){ controller.init(section); controller.start(); });

What just happened?

The itinero app is now able to get information about places near the destination of the trip and the user can now add them to the trip's plan and notes.

Summary

In this article, you learned how to get the Geolocation information from a device and how to integrate external Geolocation service in the app. In the next article, you will learn how to manipulate a file, access the device Camera, and perform some basic image manipulation.

Resources for Article :

Further resources on this subject:


PhoneGap 3 Beginner's Guide A guide to building cross-platform apps using the W3C standards-based Cordova/PhoneGap framework with this book and ebook
Published: September 2013
eBook Price: €20.99
Book Price: €34.99
See more
Select your format and quantity:

About the Author :


Giorgio Natili

Giorgio Natili is an author, educator, community leader, W3C member, and founder of www.gnstudio.com—a boutique Rome-based development and design studio, specializing in engaging and accessible web and mobile experiences. A strong proponent of agile development practices, his areas of expertise include standards-based application development, client-side scripting, gaming, and video streaming. His previous speaking engagements include Adobe Max, 360|Flex, FITC, XP 2010 and 2012, 360|Stack 2013, and several community-driven conferences. Also, he is the founder of the community www.codeinvaders.net and the main organizer of the Mobile Tea, Italy conference.

Books From Packt


HTML5 iPhone Web Application Development
HTML5 iPhone Web Application Development

Instant PhoneGap Social App Development
Instant PhoneGap Social App Development

PhoneGap Mobile Application Development Cookbook
PhoneGap Mobile Application Development Cookbook

PhoneGap 2.x Mobile Application Development Hotshot
PhoneGap 2.x Mobile Application Development Hotshot

Adobe Captivate 7 for Mobile Learning
Adobe Captivate 7 for Mobile Learning

Creating Mobile Apps with jQuery Mobile
Creating Mobile Apps with jQuery Mobile

WordPress Mobile Applications with PhoneGap
WordPress Mobile Applications with PhoneGap

PhoneGap Beginner's Guide
PhoneGap Beginner's Guide


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software