Direction, Distance and Destinations

With all these new smartphones in the hands of lost and confused owners, we need a better way to represent distances and directions to destinations. The immediate examples that jump to mind are augmented reality apps which let you see another world through your phone’s camera. While this is interesting, there is a simpler way: letting people know how far away they are and if they are getting warmer or colder.

In the app world, you can easily tap into the phone’s array of sensors such as the GPS and compass, but what people rarely know is that you can do the same with HTML. The native versus web app debate will never subside, but at least we can show you how to replicate some of the functionality progressively in HTML and JavaScript.

In this tutorial, we’ll walk through how to create a simple webpage listing distances and directions of a few popular locations around the world. We’ll use JavaScript to access the device’s geolocation API and also attempt to access the compass to get a heading. Both of these APIs are documented, to be included in the W3C geolocation API specification, and can be used on both desktop and mobile devices today.

To get started, we need a list of a few locations around the world. I have chosen the highest mountain peak on each continent so you can see a diverse set of distances and directions.

Mountain °Latitude °Longitude
Kilimanjaro -3.075833 37.353333
Vinson Massif -78.525483 -85.617147
Puncak Jaya -4.078889 137.158333
Everest 27.988056 86.925278
Elbrus 43.355 42.439167
Mount McKinley 63.0695 -151.0074
Aconcagua -32.653431 -70.011083

Source: Wikipedia

We can put those into an HTML list to be styled and accessed by JavaScript to create some distance and directions calculations.

The next thing we need to do is check to see if the browser and operating system have geolocation support. To do this we test to see if the function is available or not using a single JavaScript if statement.

<script>
// If this is true, then the method is supported and we can try to access the location
if (navigator.geolocation) {
	navigator.geolocation.getCurrentPosition(geo_success, geo_error);
}
</script>

The if statement will be false if geolocation support is not present, and then it is up to you to do something else instead as a fallback. For this example, we’ll do nothing since our page should work as is and only get progressively better if more functionality is available.

The if statement will be true if there is support and therefore will continue inside the curly brackets to try to get the location. This should prompt the reader to accept or deny the request to get their location. If they say no, the second function callback is processed, in this case a function called geo_error; whereas if the location is available, it fires the geo_success function callback.

The function geo_error(){ } isn’t that exciting. You can handle this in any way you see fit. The success function is more interesting. We get a position object passed into the function which contains a series of exciting attributes, namely the latitude and longitude of the device’s current location.

function geo_success(position){
	gLat = position.coords.latitude;
	gLon = position.coords.longitude;
}

Now, in the variables gLat and gLon we have the user’s approximate geographical position. We can use this information to start to calculate some distances between where they are and all the destinations.

At the time of writing, you can also get position.coords.heading, but on Windows and iOS devices this returned NULL. In the future, if and when this is supported, this is also where you can easily grab the compass information.

Inside the geo_success function, we want to loop through the HTML to get all of the mountain peaks’ latitudes and longitudes and compute the distance.

...
$('.geo').each(function(){
	// Get the lat/lon from the HTML
	tLat = $(this).find('.lat').html()
	tLon = $(this).find('.lon').html()

	// compute the distances between the current location and this points location
	dist = distance(tLat,tLon,gLat,gLon);

	// set the return values into something useful
	d = parseInt(dist[0]*10)/10;
	a = parseFloat(dist[1]);

	// display the value in the HTML and style the arrow
	$(this).find('.distance').html(d+' km away');
	$(this).find('.direction').css('-webkit-transform','rotate(-' + a + 'deg)');

	// store the arc for later use if compass is available
	$(this).attr('data-arc',a);
}

In the variable d we have the distance between the current location and the location of the mountain peak based on the Haversine Formula. The variable a is the arc, which has a value from 0 to 359.99. This will be useful later if we have compass support. Given these two values we have a distance and a heading to style the HTML.

The next thing we want to do is check to see if the device has a compass and then get access to the the current heading. As we’ll see, there are several ways to do this, some of which work on certain devices but not others. The W3C geolocation spec says that, along with the coordinates, there are several other attributes: accuracy; altitude; and heading. Heading is the direction to true north, which is different than magnetic north! WebKit and Windows return NULL for the heading value, but WebKit has an experimental method to fetch the heading. If you get into accessing these sensors, you’ll have to try to catch a few of these methods to finally get a value. Assuming you do, we can move on to the more interesting display opportunities.

In an ideal world, this would succeed and set a variable we’ll call compassHeading to get a value between 0 and 359.99 degrees. Now we know which direction north is, we also know the direction relative to north of the path to our destination, so we can can subtract the two values to get an arrow to display on the screen. But we’re not finished yet: we also need to get the device’s orientation (landscape or portrait) and subtract the correct amount from the angle for the arrow. Once we have a value, we can use CSS to rotate the arrow the correct number of degrees.

-webkit-transform: rotate(-180deg)

Not all devices support a standard way to access compass information, so in the meantime we need to use a work around. On iOS, you can use the experimental event method e.webkitCompassHeading. We want the compass to update in real time as the device is moved around, so we’ll put this inside an event listener.

window.addEventListener('deviceorientation', function(e) {
	// Loop through all the locations on the page
	$('.geo').each(function(){
		// get the arc value from north we computed and stored earlier
		destination_arc = parseInt($(this).attr('data-arc'))
		compassHeading = e.webkitCompassHeading + window.orientation + destination_arc;
		// find the arrow element and rotate it accordingly
		$(this).find('.direction').css('-webkit-transform','rotate(-' + compassHeading + 'deg)');		
	}
}

As the device is rotated, the compass arrow will constantly be updated. If you want to see an example, you can have a look at this page which shows the distances to all the peaks on each continent.

With progressive enhancement, we slowly layer on additional functionality as we go. The reader will first see the list of locations with a latitude and longitude. If the device is capable and permissions allow, it will then compute the distance. If a compass is available, with the correct permissions it will then add the final layer which is direction.

You should consider this code a stub for your projects. If you are making a hyperlocal webpage with restaurant locations, for example, then consider adding these features. Knowing not only how far away a place is, but also the direction can be hugely important, and since the compass is always active, it acts as a guide to the location.

Future developments

Improvements to this could include setting a timer and recalling the navigator.geolocation.getCurrentPosition() function and updating the distances. I chose very distant mountains so kilometres made sense, but you can divide again by 1,000 to convert to metres if you are dealing with much nearer places. Walking or driving would change the distances so the ability to refresh would be important.

It is outside the scope of this article, but if you manage to get this HTML to work offline, then you can make a nice web app which sits on your devices’ homescreens and works even without an internet connection. This could be ideal for travellers in an unknown city looking for your destination. Just with offline storage, base64 encoding and data URIs, it is possible to embed plenty of design and functionality into a small offline webpage.

Now you know how to use JavaScript to look up a destination’s location and figure out the distance and direction – never get lost again.

About the author

Brian Suda is a master informatician working to make the web a better place little by little everyday. Since discovering the Internet in the mid-90s, Brian Suda has spent a good portion of each day connected to it. His own little patch of Internet is http://suda.co.uk, where many of his past projects and crazy ideas can be found.

Photo: Jeremy Keith

More articles by Brian

Comments