{"id":1527,"date":"2013-04-29T19:59:43","date_gmt":"2013-04-29T19:59:43","guid":{"rendered":"http:\/\/www.sitekickr.com\/blog\/?p=1527"},"modified":"2013-04-29T19:59:43","modified_gmt":"2013-04-29T19:59:43","slug":"location-listing-mobile-geolocation","status":"publish","type":"post","link":"https:\/\/www.sitekickr.com\/blog\/location-listing-mobile-geolocation\/","title":{"rendered":"Geolocation &#038; Company Locations Listing on Mobile Sites"},"content":{"rendered":"<p>One of the most common functions of a mobile site is to provide a quick list of locations for the company you&#39;re interested in. Ideally, this list would be sorted based on the users current location. That is, company location closest to the users&#39;s current location would show at the top of the list.<\/p>\n<p>This involves a few things:<\/p>\n<ol>\n<li>Getting the user&#39;s current location, using the mobile browser&#39;s GeoLocation API.<\/li>\n<li>Creating an HTML list of locations, with attributes describing the latitude and longitude of the location.<\/li>\n<li>A formula to calculate the distance between two locations (the user&#39;s and the company location).<\/li>\n<li>A sorting algorithm, used to move the HTML elements around so they are sorted in order of distance.<\/li>\n<li>A touch of CSS to make it all look decent.<\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n<h3>How it Works<\/h3>\n<p>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&#39;s lat\/long coordinates).<\/p>\n<p>We make this happen by leveraging HTML 5&#39;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&#39;s latitude and longitude.<\/p>\n<p>We then introduce the JavaScript (jQuery-based) to grab a user&#39;s location using the GeoLocation API and apply distance formula to grab the distance between the two points.<\/p>\n<p>Depending on your mobile framework and if you&#39;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&#39;t need to recalculate the distance for each location every time, we&#39;ve included a variable that is toggled when the user first visits the location page and the distances are calculated. For our example, we&#39;re using the jQuery Mobile framework.<\/p>\n<p>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.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h3>The HTML<\/h3>\n<p><code>&lt;div id=&quot;location-wrapper&quot;&gt;<br \/>\n\t&lt;div class=&quot;location&quot; data-lat=&quot;42.106254&quot; data-long=&quot;-76.020856&quot;&gt;<br \/>\n\t&nbsp; &lt;span class=&quot;distance&quot;&gt;&lt;\/span&gt;<br \/>\n\t&nbsp; &lt;strong&gt;Location 1&lt;\/strong&gt;&nbsp;<br \/>\n\t&nbsp; 123 Main Street&lt;br \/&gt;<br \/>\n\t&nbsp; MyTown, FL 33952&lt;br \/&gt;<br \/>\n\t&nbsp; 941.123.4567&nbsp;<br \/>\n\t&lt;\/div&gt;<\/p>\n<p>\t&lt;div class=&quot;location&quot; data-lat=&quot;43.103254&quot; data-long=&quot;-74.020356&quot;&gt;<br \/>\n\t&nbsp; &lt;span class=&quot;distance&quot;&gt;&lt;\/span&gt;<br \/>\n\t&nbsp; &lt;strong&gt;Location 2&lt;\/strong&gt;&nbsp;<br \/>\n\t&nbsp; 456 South Street&lt;br \/&gt;<br \/>\n\t&nbsp; MyOtherTown, FL 33123&lt;br \/&gt;<br \/>\n\t&nbsp; 941.133.4532&nbsp;<br \/>\n\t&lt;\/div&gt;<\/code><\/p>\n<p>&nbsp;<\/p>\n<h3>The JavaScript \/ jQuery<\/h3>\n<pre><code>if (typeof Number.prototype.toRad === &#39;undefined&#39;) {\r\n Number.prototype.toRad = function() {\r\n   return this * Math.PI \/ 180;\r\n }\r\n}\r\n\r\n(function() {\r\n var locationsUpdated = false;\r\n var R = 6371;\r\n\r\n function checkUpdate() {\r\n   if (locationsUpdated) {\r\n     return true;\r\n   }\r\n \r\n   \/\/ loop over locations for closest to &quot;location&quot;\r\n   if (navigator.geolocation) {\r\n    navigator.geolocation.getCurrentPosition(function(position) {\r\n\r\n     \/\/ assign a distance to all locations\r\n     $(&#39;.location&#39;).each(function() { \r\n       var dLat = ($(this).data(&#39;lat&#39;) - position.coords.latitude).toRad();\r\n       var dLon = ($(this).data(&#39;long&#39;) - position.coords.longitude).toRad();\r\n       var lat1 = position.coords.latitude.toRad();\r\n       var lat2 = $(this).data(&#39;lat&#39;).toRad(); \r\n\r\n       var a = Math.sin(dLat\/2) * Math.sin(dLat\/2) +\r\n       Math.sin(dLon\/2) * Math.sin(dLon\/2) * Math.cos(lat1) * Math.cos(lat2); \r\n<span style=\"font-family: Arial, Verdana, sans-serif;\">   <\/span>      var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); \r\n<span style=\"font-family: Arial, Verdana, sans-serif;\">          <\/span>   var d = (R * c) * .62;\r\n\r\n       d = Math.round(d * 10) \/ 10;\r\n \r\n       $(this).data(&#39;distance&#39;, d);\r\n       $(this).children(&#39;.distance&#39;).addClass(&#39;available&#39;).html(d + &#39; mi.&#39;);\r\n     }); \r\n\r\n     \/\/ get array of dom elements\r\n     var locationArray = $(&#39;.agency&#39;);\r\n \r\n     \/\/ sort based on distance\r\n     locationArray.sort(function (a, b) {\r\n       a = parseInt($(a).data(&#39;distance&#39;), 10);\r\n       b = parseInt($(b).data(&#39;distance&#39;), 10);\r\n       if(a &gt; b) {\r\n        return 1;\r\n       } else if(a &lt; b) {\r\n        return -1;\r\n       } else {\r\n        return 0;\r\n       }\r\n     });\r\n \r\n     $(&#39;#location-wrapper&#39;).html(locationArray); \r\n    });\r\n  }\r\n \r\n  locationsUpdated = true;\r\n }\r\n \r\n \r\n if (location.href.indexOf(&#39;locations&#39;) !== -1) {\r\n   checkUpdate();\r\n }\r\n \r\n $(document).bind(&#39;pagebeforechange&#39;, function(event, data) {\r\n   if (typeof data[&#39;toPage&#39;] === &#39;string&#39;) {\r\n     if (data[&#39;toPage&#39;].indexOf(&#39;locations&#39;) !== -1) {\r\n       checkUpdate();\r\n     }\r\n   }\r\n });\r\n\r\n})();<\/code><\/pre>\n<p>&nbsp;<\/p>\n<h3>The CSS<\/h3>\n<p><code>.location { margin: 1em 0; }<br \/>\n\t.location&nbsp;strong { display: block; }<br \/>\n\t.location&nbsp;.distance { display: block; float: right; width: 70px; <br \/>\n\t&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; margin-top: 2px; font-size: 11px;<br \/>\n\t&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; line-height: 18px; color: #070; <br \/>\n\t&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; border-radius: 10px; background: #f7f7f7; <br \/>\n\t&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; text-align: center; }<br \/>\n\t.location&nbsp;.distance.available { border: 1px solid #888; }<\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>One of the most common functions of a mobile site is to provide a quick list of locations for the company you&#39;re interested in. Ideally,&hellip;<\/p>\n","protected":false},"author":1,"featured_media":1549,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"amp_status":""},"categories":[132],"tags":[250,252,235,86],"_links":{"self":[{"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/posts\/1527"}],"collection":[{"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/comments?post=1527"}],"version-history":[{"count":7,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/posts\/1527\/revisions"}],"predecessor-version":[{"id":1550,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/posts\/1527\/revisions\/1550"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/media\/1549"}],"wp:attachment":[{"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/media?parent=1527"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/categories?post=1527"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/tags?post=1527"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}