mobile location listing

Geolocation & Company Locations Listing on Mobile Sites

One of the most common functions of a mobile site is to provide a quick list of locations for the company you're interested in. Ideally, this list would be sorted based on the users current location. That is, company location closest to the users's current location would show at the top of the list.

This involves a few things:

  1. Getting the user's current location, using the mobile browser's GeoLocation API.
  2. Creating an HTML list of locations, with attributes describing the latitude and longitude of the location.
  3. A formula to calculate the distance between two locations (the user's and the company location).
  4. A sorting algorithm, used to move the HTML elements around so they are sorted in order of distance.
  5. A touch of CSS to make it all look decent.

 

How it Works

One of the primary goals of this technique is to make it very easy for a non-programmer to update locations. This means that someone should be able to completely avoid JavaScript to add, remove, or change locations (include a location's lat/long coordinates).

We make this happen by leveraging HTML 5's data attribute. Although this is a new attribute, jQuery can retrieve it on older browsers, making it a perfect place to store a location's latitude and longitude.

We then introduce the JavaScript (jQuery-based) to grab a user's location using the GeoLocation API and apply distance formula to grab the distance between the two points.

Depending on your mobile framework and if you're site is relatively small, you might load all pages into the DOM at once, then let the framework handle the browsing for you. To improve performance, so we don't need to recalculate the distance for each location every time, we've included a variable that is toggled when the user first visits the location page and the distances are calculated. For our example, we're using the jQuery Mobile framework.

Finally, once we have the distances, we use the JavaScript sort function to sort the array of locations, then insert the entire chunk of HTML into the DOM.

 

 

The HTML

<div id="location-wrapper">
<div class="location" data-lat="42.106254" data-long="-76.020856">
  <span class="distance"></span>
  <strong>Location 1</strong> 
  123 Main Street<br />
  MyTown, FL 33952<br />
  941.123.4567 
</div>

<div class="location" data-lat="43.103254" data-long="-74.020356">
  <span class="distance"></span>
  <strong>Location 2</strong> 
  456 South Street<br />
  MyOtherTown, FL 33123<br />
  941.133.4532 
</div>

 

The JavaScript / jQuery

if (typeof Number.prototype.toRad === 'undefined') {
 Number.prototype.toRad = function() {
   return this * Math.PI / 180;
 }
}

(function() {
 var locationsUpdated = false;
 var R = 6371;

 function checkUpdate() {
   if (locationsUpdated) {
     return true;
   }
 
   // loop over locations for closest to "location"
   if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(function(position) {

     // assign a distance to all locations
     $('.location').each(function() { 
       var dLat = ($(this).data('lat') - position.coords.latitude).toRad();
       var dLon = ($(this).data('long') - position.coords.longitude).toRad();
       var lat1 = position.coords.latitude.toRad();
       var lat2 = $(this).data('lat').toRad(); 

       var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
       Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2); 
         var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
             var d = (R * c) * .62;

       d = Math.round(d * 10) / 10;
 
       $(this).data('distance', d);
       $(this).children('.distance').addClass('available').html(d + ' mi.');
     }); 

     // get array of dom elements
     var locationArray = $('.agency');
 
     // sort based on distance
     locationArray.sort(function (a, b) {
       a = parseInt($(a).data('distance'), 10);
       b = parseInt($(b).data('distance'), 10);
       if(a > b) {
        return 1;
       } else if(a < b) {
        return -1;
       } else {
        return 0;
       }
     });
 
     $('#location-wrapper').html(locationArray); 
    });
  }
 
  locationsUpdated = true;
 }
 
 
 if (location.href.indexOf('locations') !== -1) {
   checkUpdate();
 }
 
 $(document).bind('pagebeforechange', function(event, data) {
   if (typeof data['toPage'] === 'string') {
     if (data['toPage'].indexOf('locations') !== -1) {
       checkUpdate();
     }
   }
 });

})();

 

The CSS

.location { margin: 1em 0; }
.location strong { display: block; }
.location .distance { display: block; float: right; width: 70px;
                      margin-top: 2px; font-size: 11px;
                      line-height: 18px; color: #070;
                      border-radius: 10px; background: #f7f7f7;
                      text-align: center; }
.location .distance.available { border: 1px solid #888; }

Why follow me on Twitter?

  • I tweet about new technologies, services or libraries I find interesting
  • Yeah, sometimes I'll post a pet-peeve or rant about something trivial
  • If I discover something that made my web development life easier, I share it
  • I'll shout out any handy tip that I think might be useful to other devs


Tagged , , , .

Updated: 2013-04-29

Phil LaNasa follow us in feedly