Two-Minute Tutorial 4 Part 1

TMT4 P1 jQuery Mobile Plugin for Google Maps: Route Finder App

jQuery Mobile / Google Maps on Android

This Two-Minute Tutorial (TMT4 Part1) shows how to use a plugin with AppLaud to extend the functionality of jQuery Mobile available to an Android mobile web app. The plugin used acts as a wrapper for google maps, providing a simplified API for accessing Google Maps from jQuery Mobile: jQuery Google Maps plugin by Johan Säll Larsson. The sample app demonstrates an easy way to show a dynamic route map in a mobile web app and provide interactive directions using the device’s geolocation.

jQuery Google Maps Plugin (UI and Mobile)

The jQuery Google Maps plugin Version 3 is a jQuery plugin written to take away some of the headaches from working with the Google Map v3 JS API. Instead of having to use Google event listeners for simple events like tap, you can use jQuery touch events on the map and markers. It is also very flexible, highly customizable, lightweight (< 4KB) and works out of the box with jQuery Mobile. The plugin is well-documented and supported, and provides the most common functionality available from Google maps.
The jQuery Google Maps plugin documentation integrates links to google maps APIs, classes and methods, shortening the learning curve for including google maps in a mobile web app. 
(The tutorial for the earlier jQuery Google Maps Plugin Version 2 tutorial is here)

Sample App Summary

Use dynamic location to provide map and directions to business location using Google Maps and PhoneGap geolocation:
  • small map showing the location of a business on home screen
  • full screen google interactive route map: current location to business location
  • interactive written directions using google maps
Both maps and the directions are identical in every way (look and functionality) to Google’s version 3 maps, because they are google maps!

Prerequisites
  • Installation of the latest MDS AppLaud plugin, see Get Started
  • Complete TMT0 or equivalent (create project, edit files, run app on device or AVD)
Prep
  • Download the attached zip file tmt4p1.zip
    • /src directory contains:
      • index.html - jQuery Mobile included, all pages and divs needed for app
      • mapapp.js - skeleton javascript file, add javascript code to it in tutorial
      • mapapp.complete.js - the complete javascript file, provided for convenience
      • mapapp.css - important styling for map display
    • /src/jquery.ui.map contains:
      • jquery.ui.map.min.js - minified, version 3 plugin (basic map plugin functionality)
      • jquery.ui.map.js - non-minified, version 3 plugin
      • jquery.ui.map.services.min.js - non-minified, version 3 plugin (maps services functionality)
      • jquery.ui.map.services.js - non-minified, version 3 plugin
Recommended Reading
  • The jQuery Google Maps plugin documentation
    • Complete API documentation with links to all google maps classes and methods
  • jQuery Mobile Docs and Demos
  • Google Maps Geocoding - post on MDS' forum titled "How To: Geocoding, Google Maps..." talks about geocoding with references to Google's excellent documentation on the subject
1. Create a new AppLaud project
 
Using the project creation wizard, create a new project that includes:
  • Built-In PhoneGap
  • jQuery Mobile Libraries
  • Project Contents: locate the /src directory (see Prep step above)
Finish creating the project in the second window: fill in project name, etc. Upon completion the new project's /assets/www/ directory should appear as below.

2. Add Google Maps JS API v3 and the jQuery Google Maps plugin to the project

Adding a jQuery Plugin is usually as simple as including a javascript file in your html file. The reference to the plugin can point to a local copy or reference the file on a server.


To use the plugin javascript locally, import the plugin files:
    Edit index.html to include google maps javascript before mapapp.js:
     <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>

    Edit index.html to include the maps plugin files, before the mapapp.js and after google maps javascript:
     <script type="text/javascript" src="jquery.ui.map/jquery.ui.map.js"></script>
     <script type="text/javascript" src="jquery.ui.map/jquery.ui.map.services.js"></script>

    Sanity test:
    The app can be deployed at this point, before any javascript is added, and should look like the following image. The Map button in the toolbar near the bottom can be used to navigate to the (empty) map and directions pages.

    3. The Business Location (Destination)

    The object mapdata defines the route destination. In the sample app, the location of the fictional business is in Stockholm, Sweden. Google maps uses a LatLng object to hold coordinates, created as shown below.

    var mapdata = { destination: new google.maps.LatLng(59.3327881, 18.064488100000062) };

    Note: Recommended method for implementing this app: use the default coordinates provided for a first run/sanity test. Once everything is running as desired, custom coordinates can be substituted. A convenient website for finding coordinates anywhere in the world is Get Lat Lon by Simon Willison.

    Further Note: Want to use a street address instead? See forum post "How To: Geocoding, Google Maps..."

    4. Create Small Map on Home Page

    The API for the jQuery Google Maps plugin is defined by the wrapper method .gmap() used with a DOM element id (div). The home page, defined in index.html with jQuery data types: <div data-role="page" data-theme="c" id="page-home"> creates the element: <div "id="map_square"></div> on the page where the map will go.

    Add the following code block to mapapp.js.

    // Home page
    $('#page-home').live("pageinit", function() {
        $('#map_square').gmap(
            { 'center' : mapdata.destination,
              'zoom' : 12,
              'mapTypeControl' : false,
              'navigationControl' : false,
              'streetViewControl' : false
            })
            .bind('init', function(evt, map) {
                $('#map_square').gmap('addMarker',
                    { 'position': map.getCenter(),
                      'animation' : google.maps.Animation.DROP
     });                                                                                                                                                                                                               
            });
        $('#map_square').click( function() {
            $.mobile.changePage($('#page-map'), {});
        });
    });


    Alternative method: Modify index.html to include mapapp.complete.js instead of mapapp.js. The file mapapp.complete.js contains all the javascript for the app.

    The code above uses map_square to create the small map on the home page, centered at the business location (mapdata.destination).  Documentation for all google map options can be found here.

    The bound 'init' function of the plugin calls
    the method  'addMarker' to add a marker at the location after the map is created. More information on google markers here.

    The area defined by
    map_square is then bound to the 'click' event handler, which will navigate to the page containing the full map when tapped.

    For the desired layout and appearance, css styling and jQuery Mobile features are used in index.html and mapapp.css to control the size and location of the map.


    5. Create the main map and directions page

    Add the following code to the end mapapp.js. The function fadingMsg is used to briefly display the provided message to the user in a fading message box.

    jQuery Mobile's pageinit event is used to create the map the first time the page is navigated to. The fullscreen map is created using the div id and plugin api: $('#map_canvas').gmap(... This call creates the new map centered at the destination, and binds an init function used by the maps plugin. The init function is called after the new google map is created, initialized and any option values applied.

    The event handler bound to 'tap' on the refresh button (see the footer on page with id="page-map" in index.html) updates the map and directions panel (see the page id="page-dir" in index.html).

    function fadingMsg (locMsg) {
        $("<div class='ui-overlay-shadow ui-body-e ui-corner-all fading-msg'>" + locMsg + "</div>")
        .css({ "display": "block", "opacity": 0.9, "top": $(window).scrollTop() + 100 })
        .appendTo( $.mobile.pageContainer )
        .delay( 2200 )
        .fadeOut( 1000, function(){
            $(this).remove();
       });
    }

    //Create the map then make 'displayDirections' request
    $('#page-map').live("pageinit", function() {
        $('#map_canvas').gmap({'center' : mapdata.destination, 
            'mapTypeControl' : true, 
            'navigationControl' : true,
            'navigationControlOptions' : {'position':google.maps.ControlPosition.LEFT_TOP}
            })
        .bind('init', function() {
            $('.refresh').trigger('tap');        
        });
    });

    $('#page-map').live("pageshow", function() {
        $('#map_canvas').gmap('refresh');
    });

    // Request display of directions, requires jquery.ui.map.services.js
    var toggleval = true; // used for test case: static locations
    $('.refresh').live("tap", function() {
        
                // START: Tracking location with device geolocation
    /*            if ( navigator.geolocation ) { 
                    fadingMsg('Using device geolocation to get current position.');
                    navigator.geolocation.getCurrentPosition ( 
                        function(position) {
                            $('#map_canvas').gmap('displayDirections', 
                            { 'origin' : new google.maps.LatLng(position.coords.latitude, position.coords.longitude), 
                              'destination' : mapdata.destination, 'travelMode' : google.maps.DirectionsTravelMode.DRIVING},
                            { 'panel' : document.getElementById('dir_panel')},
                                  function (result, status) {
                                      if (status === 'OK') {
                                          var center = result.routes[0].bounds.getCenter();
                                          $('#map_canvas').gmap('option', 'center', center);
                                          $('#map_canvas').gmap('refresh');
                                      } else {
                                        alert('Unable to get route');
                                      }
                                  }
                               );         
                        }, 
                        function(){ 
                            alert('Unable to get location');
                            $.mobile.changePage($('#page-home'), {}); 
                        }); 
                    } else {
                        alert('Unable to get location.');
                    }            
    */            // END: Tracking location with device geolocation

                // START: Tracking location with test lat/long coordinates
                // Toggle between two origins to test refresh, force new route to be calculated
                var position = {};
                if (toggleval) {
                    toggleval = false;
                    position = { coords: { latitude: 57.6969943, longitude: 11.9865 } }; // Gothenburg
                } else {
                    toggleval = true;
                    position = { coords: { latitude: 58.5365967, longitude: 15.0373319 } }; // Motala
                }
                $('#map_canvas').gmap('displayDirections', 
                    { 'origin' : new google.maps.LatLng(position.coords.latitude, position.coords.longitude), 
                      'destination' : mapdata.destination, 
                      'travelMode' : google.maps.DirectionsTravelMode.DRIVING },
                      { 'panel' : document.getElementById('dir_panel') },
                        function (result, status) {
                            if (status === 'OK') {
                                var center = result.routes[0].bounds.getCenter();
                                $('#map_canvas').gmap('option', 'center', center);
                                $('#map_canvas').gmap('refresh');
                            } else {
                                alert('Unable to get route');
                            }
                        }); 
                // END: Tracking location with test lat/long coordinates
        $(this).removeClass($.mobile.activeBtnClass);
        return false;
    });

    The refresh function toggles the position values (only for testing) and then makes another call to the plugin method: $('#map_canvas').gmap.

    In this second use of the plugin api, the method
    'displayDirections' is the first parameter. This indicates a request to google maps to calculate the route between the two locations provided in the following options object, which is google's DirectionsRequest (origin and destination). The third value set in the DirectionsRequest, travelMode, tells google maps to provide driving (vs. bicycling or walking) directions. The services part of the plugin are used for this maps request, which requires the additional javascript plugin file: jquery.ui.map.services.js.

    The next parameter, google's DirectionsRendererOptions, uses the DOM element for the panel div, supplying the place where the text directions will go. See
    <div id="dir_panel"></div> in the page with id="page-dir"  in index.html.

    In order to simulate the user changing position relative to the destination, the test version of the javascript uses a toggle value (
    toggleval) to fake a different position with each refresh (origin toggles between Gothenburg and Motala). The block indicated by START and END can be replaced by identical code that uses PhoneGap's geolocation to get current position (see step 7 below) instead of faking it.

    6. Add event handlers and a fading hint box to directions page

    The default behavior of the directions list provided by google maps works the same with the plugin and on mobile devices. Tapping on a direction will zoom in on the associated map to show details of that instruction. Unlike desktop, the map is on another page due to size. When the user taps on an instruction, the zoomed view will automatically appear on the map page. The tap (or click) event handler changes back to the map page to show the zoomed view.

    Add the following code to the end mapapp.js.

    // Go to map page to see instruction detail (zoom) on map page
    $('#dir_panel').live("tap", function() {
        $.mobile.changePage($('#page-map'), {});
    });


    // Briefly show hint on using instruction tap/zoom
    $('#page-dir').live("pageshow", function() {
        fadingMsg("Tap any instruction<br/>to see details on map");
    });


    A fading hint window is displayed to remind the user of this functionality whenever the directions page is shown. The images below show the fullscreen map and directions page.

    The screen shots below show a detailed direction (tap on any instruction in directions list to zoom in on it) and a satellite view of the route after refreshing (toggling to other origin location, tap Satellite on map).


    7. Option: Add PhoneGap's Geolocation to find Route Dynamically
     

    To use geolocation to dynamically find the route, make the following two changes.

    Modify the first line of mapapp.js where the destination location is specified (
    mapdata). Find a location on your continent so the driving directions will be clear. A convenient tool to find latitude and longitude anywhere on the planet is Get Lat Lon by Simon Willison.

    var mapdata = { destination: new google.maps.LatLng(59.3327881, 18.064488100000062) };

    Add the following code to mapapp.js immediately before the
    existing block denoted by START and END comments.  Remove or comment out the existing code (the one using static locations in Sweden). For clarity this block is also denoted by START and END.

        // START: Tracking location with device geolocation
        if ( navigator.geolocation ) {
            fadingMsg('Using device geolocation to get current position.');
            navigator.geolocation.getCurrentPosition (
                function(position) {
                    $('#map_canvas').gmap('displayDirections',
                    { 'origin' : new google.maps.LatLng(position.coords.latitude, position.coords.longitude),
                      'destination' : mapdata.destination, 'travelMode' : google.maps.DirectionsTravelMode.DRIVING},
                    { 'panel' : document.getElementById('dir_panel')},
                          function (result, status) {
                              if (status === 'OK') {
                                  var center = result.routes[0].bounds.getCenter();
                                  $('#map_canvas').gmap('option', 'center', center);
                                  $('#map_canvas').gmap('refresh')
                              } else {
                                  alert('Unable to get route');
                              }
                          }
                       );        
                    },
                    function(){
                        alert('Unable to get location');
                        $.mobile.changePage($('#page-home'), {});
                    });
                } else {
                    alert('Unable to get location.');
                }          

        // END: Tracking location with device geolocation

    Note: From the PhoneGap Documentation on geolocation: The Android 2.x simulators will not return a geolocation result unless the enableHighAccuracy option is set to true.

    8. Challenge: Add more features using jQuery and jQuery Google Maps plugin
     

    8.1 Add radio buttons on the home page to let the user select driving, biking or walking travel mode.

    8.2 Add a control on the main map to toggle a traffic layer.

    8.3 Add multiple markers/locations (i.e. business with multiple locations) and way for user the select the location they want to visit.

    Share your results on the discussion mailing list.

    Had trouble with the above or have comments? Let us know on the mailing list or create an issue.

    Return to the main tutorial page.
    Č
    ċ
    Elizabeth Baldwin,
    Apr 21, 2012, 6:14 PM
    Comments