Build a Location-Based Mobile App With HTML5 & Javascript: 5

Share this article

In the previous part of our series, I explained how to use the Google Maps API to display stored locations on a map and to trace a route from the user’s current position to his car. I also demonstrated several utility functions that will be useful during the development of the application. In this penultimate article, I’ll describe the final utility function and those that complete “Where I parked my car”. In the final part of the series, we’ll demonstrate how to run our finished Cordova application optimally.

Utility Functions

As described in the introduction, there is still one more utility function to explain, and that is the one to retrieve the value of a parameter inside a URL.

Retrieving a URL Parameter’s Value

The HTTP protocol has several request methods, but the two most common are GET and POST. As you most likely know, the GET method sends the data through the URL appending variables and values after a question mark (www.website.com?variableone=build&variable2=mobile). Unfortunately, jQuery Mobile doesn’t offer a native method to retrieve a single parameter of the URL. In fact, the only method that does something similar is $.mobile.path.parseUrl(). However, this method returns an object having a property called search that gives you all the parameters, not just the one desired. To meet our development needs, I created the following function.
function urlParam(name)
{
   var results = new RegExp('[?&]' + name + '=([^&#]*)').exec(window.location.href);
   if (results != null && typeof results[1] !== 'undefined')
      return results[1];
   else
      return null;
}

Initializing the Application

Now that you’ve seen all of the utility functions, I’ll explain the two initialization functions that power “Where I parked my car”. The first function we’ll look at, called initApplication(), determines the behavior of application elements like homepage buttons (index.html). The positions’ history page (positions.html). initApplication() is responsible for managing several other elements, which you’ll see in greater detail in the following sections.

Disable Buttons Conditionally

In part 4 of our series, I illustrated the requirements of our application, showing you the function that tests to see if they all are satisfied (checkRequirements()). When the user runs “Where I parked my car”, if the test fails, the buttons to set the position and to find the car will be disabled, and the user will be notified of the connectivity issues. (In this scenario, they lack the Internet access needed to use the Google Maps API.) This is done using the following code:
$('#set-car-position, #find-car').click(function() {
   if (checkRequirements() === false)
   {
      $(this).removeClass('ui-btn-active');
      return false;
   }
});

Be Responsive

To enhance the application interface and make it responsive, I created the function updateIcons() that I’ll use as a handler for the pagebeforecreate and the orientationchange events.
$(document).on('pagebeforecreate orientationchange', updateIcons);

Get or Set the Position

Th next piece of code, one of the most important parts of the whole app, is the callback function attached to the pageshow event of the map-page element. It’s responsible for checking if the page has been required to set or to get the car’s position and act accordingly using the urlParam() function. If the value of the variable requestType is set, we’ll use the getCurrentPosition() method of the Geolocation API to retrieve the position and then store it in the app database using the Web Storage API. After saving the position, I’ll use both requestLocation() and displayMap() methods of our Map class to retrieve the address of the car’s position and to display the map indicating the user position with a marker. On the other hand, if the value of requestType is get, we’ll use the watchPosition() method of the Geolocation API to poll the user location to constantly refresh the route to his car. The necessary code is below.
$('#map-page').live(
   'pageshow',
   function()
   {
      var requestType = urlParam('requestType');
      var positionIndex = urlParam('index');
      var geolocationOptions = {
         timeout: 15 * 1000, // 15 seconds
         maximumAge: 10 * 1000, // 10 seconds
         enableHighAccuracy: true
      };
      var position = new Position();

      $.mobile.loading('show');
      // If the parameter requestType is 'set', the user wants to set
      // his car position else he want to retrieve the position
      if (requestType == 'set')
      {
         navigator.geolocation.getCurrentPosition(
            function(location)
            {
               // Save the position in the history log
               position.savePosition(
                  new Coords(
                     location.coords.latitude,
                     location.coords.longitude,
                     location.coords.accuracy
                  )
               );
               // Update the saved position to set the address name
               Map.requestLocation(location);
               Map.displayMap(location, null);
               navigator.notification.alert(
                  'Your position has been saved',
                  function(){},
                  'Info'
               );
            },
            function(error)
            {
               navigator.notification.alert(
                  'Unable to retrieve your position. Is your GPS enabled?',
                  function(){
                     alert("Unable to retrieve the position: " + error.message);
                  },
                  'Error'
               );
               $.mobile.changePage('index.html');
            },
            geolocationOptions
         );
      }
      else
      {
         if (position.getPositions().length == 0)
         {
            navigator.notification.alert(
               'You have not set a position',
               function(){},
               'Error'
            );
            $.mobile.changePage('index.html');
            return false;
         }
         else
         {
            navigator.geolocation.watchPosition(
               function(location)
               {
                  // If positionIndex parameter isn't set, the user wants to retrieve
                  // the last saved position. Otherwise he accessed the map page
                  // from the history page, so he wants to see an old position
                  if (positionIndex == undefined)
                     Map.displayMap(location, position.getPositions()[0]);
                  else
                     Map.displayMap(location, position.getPositions()[positionIndex]);
               },
               function(error)
               {
                  console.log("Unable to retrieve the position: " + error.message);
               },
               geolocationOptions
            );
         }
      }
   }
);

Initialize the Positions’ History Page

As soon as the user requires the file positions.html, jQuery Mobile will start enhancing the components of the page. After the jQuery framework did its job, we need to retrieve the user’s previous locations and show them as a list. So, I’ll set a callback function to the pageinit event of the position-page element. The callback, will simply call the createPositionsHistoryList() function that will be illustrated in a few moments.
$('#positions-page').live(
   'pageinit',
   function()
   {
      createPositionsHistoryList('positions-list', (new Position()).getPositions());
   }
);

Create the Positions’ History List

As stated before, createPositionsHistoryList()
creates a list, item by item, using the stored position history and the Web Storage API. Each entry in the list has two actions that the user can run. The first action executes once the user touches the text of a previous location’s address, which will show it on the map in the same way that it displays the current car location when “Find car” is clicked. This is achieved by sending the same value (get) to the map file (map.html) for the requestType parameter and by adding an additional one, called index, that indicates which item of the locations’ history must be processed. If the connection isn’t available and a location is selected, the user will be notified of the connectivity problem and no other action will take place. The second action is executed by touching the delete icon that is shown on the right side of the address for each item in the list. This, of course, will delete the selected item both from the list and the database. If there are any problems with deleting the item, the user will be warned. The full source of createPositionsHistoryList() is listed below.
/**
 * Create the positions' history list
 */
function createPositionsHistoryList(idElement, positions)
{
   if (positions == null || positions.length == 0)
      return;

   $('#' + idElement).empty();
   var $listElement, $linkElement, dateTime;
   for(var i = 0; i < positions.length; i++)
   {
      $listElement = $('<li>');
      $linkElement = $('<a>');
      $linkElement
      .attr('href', '#')
      .click(
         function()
         {
            if (checkRequirements() === false)
               return false;

            $.mobile.changePage(
               'map.html',
               {
                  data: {
                     requestType: 'get',
                     index: $(this).closest('li').index()
                  }
               }
            );
         }
      );

      if (positions[i].address == '' || positions[i].address == null)
         $linkElement.text('Address not found');
      else
         $linkElement.text(positions[i].address);

      dateTime = new Date(positions[i].datetime);
      $linkElement.text(
         $linkElement.text() + ' @ ' +
         dateTime.toLocaleDateString() + ' ' +
         dateTime.toLocaleTimeString()
      );

      // Append the link to the <li> element
      $listElement.append($linkElement);

      $linkElement = $('<a>');
      $linkElement.attr('href', '#')
      .text('Delete')
      .click(
         function()
         {
            var position = new Position();
            var oldLenght = position.getPositions().length;
            var $parentUl = $(this).closest('ul');

            position.deletePosition($(this).closest('li').index());
            if (oldLenght == position.getPositions().length + 1)
            {
               $(this).closest('li').remove();
               $parentUl.listview('refresh');
            }
            else
            {
               navigator.notification.alert(
                  'Position not deleted. Something gone wrong so please try again.',
                  function(){},
                  'Error'
               );
            }

         }
      );
      // Append the link to the <li> element
      $listElement.append($linkElement);

      // Append the <li> element to the <ul> element
      $('#' + idElement).append($listElement);
   }
   $('#' + idElement).listview('refresh');
}

Running the Application

In the last section, all the pieces of the application were built and all the HTML, CSS, and JavaScript files were put in their place. So, now you’re ready to build and deploy “Where I parked my car”. But, you have to set the entry functions for the whole application. As you might have guessed, the function will be initApplication() and it will run once Cordova is fully loaded. In this way, you can safely calls the Cordova APIs. To achieve this goal we’ve set initApplication() as a callback function for the deviceready event. This is done by adding the following code to the index.html file as you’ve already in its source listed in the second part of the series.
<script>
   $(document).one('deviceready', initApplication);
</script>

Conclusion

In this article, I explained the last functions of the file function.js. In the next and last part of this series, you’ll see how to create the configuration file (config.xml), described in the first article for use with Adobe PhoneGap Build. The configuration file will help us to specify certain properties of the application like the author, the loading screens, the permissions, and so on. As you may expect, I’ll also publish the link to the repository where you can download the full source so that you can play with it. I’ll also share some final thoughts on what features you can add to further improve the application and what I hope the Cordova team will implement in the next releases that could take our app to the next level.

Frequently Asked Questions (FAQs) about Building a Location-Based Mobile App

How can I ensure the privacy and security of user location data in my app?

Ensuring the privacy and security of user location data is paramount when building a location-based app. You can achieve this by implementing encryption for data in transit and at rest. Also, always ask for user consent before collecting and using their location data. Make sure to clearly communicate why you need this data and how you plan to use it. Additionally, comply with all relevant privacy laws and regulations in your region or in the regions where your app will be used.

How can I improve the accuracy of location data in my app?

The accuracy of location data can be improved by using a combination of GPS, Wi-Fi, and cellular data for location tracking. GPS provides the most accurate location data outdoors, while Wi-Fi and cellular data are more effective indoors. You can also use the Geolocation API’s ‘enableHighAccuracy’ option, but keep in mind that this may consume more battery power.

How can I test my location-based app effectively?

Testing a location-based app can be challenging due to the dynamic nature of location data. You can use emulators and simulators for initial testing, but real-world testing is also important. Consider using crowd testing services that allow you to test your app in different locations and conditions.

How can I handle location data when the user’s device is offline?

You can handle offline scenarios by caching the last known location data on the user’s device. When the device goes offline, you can use this cached data to provide some functionality. Once the device is back online, you can update the location data.

How can I optimize the battery usage of my location-based app?

Optimizing battery usage is crucial for location-based apps as continuous location tracking can drain the battery quickly. You can optimize battery usage by adjusting the frequency of location updates based on the user’s activity level. For example, if the user is stationary, you can reduce the update frequency.

How can I make my location-based app accessible to users with disabilities?

Making your app accessible involves ensuring that all features and content are usable by people with various types of disabilities. This can include providing alternative text for images, ensuring sufficient color contrast, and making the app navigable via keyboard or voice commands.

How can I monetize my location-based app?

There are several ways to monetize a location-based app. You can use a freemium model where basic features are free and advanced features require payment. You can also use in-app advertising or partnerships with local businesses.

How can I handle location data in different regions with different privacy laws?

Handling location data in different regions requires understanding and complying with the privacy laws of those regions. This can involve obtaining user consent, providing clear privacy policies, and implementing appropriate data protection measures.

How can I use location data to improve the user experience in my app?

Location data can be used to provide personalized content and recommendations, improve search results, and enable location-based features. However, always ensure that you respect user privacy and obtain consent before using location data.

How can I handle errors and exceptions in location tracking?

Handling errors and exceptions in location tracking involves providing clear error messages to the user and implementing fallback strategies. For example, if GPS is unavailable, you can fall back to Wi-Fi or cellular data for location tracking.

Aurelio De RosaAurelio De Rosa
View Author

I'm a (full-stack) web and app developer with more than 5 years' experience programming for the web using HTML, CSS, Sass, JavaScript, and PHP. I'm an expert of JavaScript and HTML5 APIs but my interests include web security, accessibility, performance, and SEO. I'm also a regular writer for several networks, speaker, and author of the books jQuery in Action, third edition and Instant jQuery Selectors.

jQuerymobile web tutorialsTutorials
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week