London Buses and The Javascript Geolocation API

The wonderful people at Transport For London (TFL) recently released (but didn’t seem to publicise) a new page on their site that would give you a countdown listing of buses due to arrive at any given stop in London.

This is the physical one (which only appears on some bus stops):

And this is the website one, as found at


Before I continue with the technical blithering, I’d like quantify how useful this information is by way of a use case: you’re in a pub/bar/club, a little worse for wear, the tubes have stopped running, no cash for a cab, it’s raining, no jacket. You can see a bus stop from a window, but you’ve no idea how long you’d have to wait in the rain before your cheap ride home arrived. IF ONLY this information were freely available online so you can check if you have time for another drink/comfort break/say your goodbyes before a short stroll to hail the arriving transport.

With this in mind I decided to create a mobile friendly version of the page.

If you visit the tfl site (above) and fire up fiddler you can see that the request for stops near you hits one webservice which returns json data,


and then when you select a stop there’s another call to another endpoint which returns json data for the buses due at that stop:


Seems easy enough. However, the structure of the requests which follow on from a search for, say, the postcode starting with “W6” is a bit tricky:

That doesn’t say something easy like “the postcode W6”, does it? It says “these exact coordinates on the planet Earth”.


Have you ever visited a page or opened an app on your phone and saw a popup asking for your permission to share your location with the page/app? Something like:

Or in your browser:


This is quite possibly the app attempting to utilise the javascript geolocation API in order to try and work out your latitude and longitudinal position.

This information can be easily accessed by browsers which support the javascript navigator.geolocation API. Even though the API spec is only a year old, diveintohtml5 point out it’s actually currently supported on quite a few browsers, including the main mobile ones.

The lat and long can be gleaned from the method


which just takes a callback function as a parameter passing a “position” object e.g.


function show_map(position) {
      var latitude = position.coords.latitude;
      var longitude = position.coords.longitude;
      // let's show a map or do something interesting!

Using something similar to this we can pad the single position to create a small area instead, which we pass to the first endpoint, retrieve a listing of bus stops within that area, allow the user to select one, pass that stop ID as a parameter to the second endpoint to retrieve a list of the buses due at that stop, and display them to the user.

My implementation is:

$(document).ready(function() {
 // get lat long
 if (navigator.geolocation){
	.getCurrentPosition(function (position) {
	} else {
		alert('could not get your location');

Where getStopListingForLocation is just

function getStopListingForLocation(lat, lng){
 var swLat, swLng, neLat, neLng;
 swLat = lat - 0.01;
 swLng = lng - 0.01;
 neLat = lat + 0.01;
 neLng = lng + 0.01;

 var endpoint = 
  '' + 
  '/swLat/' + swLat + 
  '/swLng/' + swLng + 
  '/neLat/' + neLat + 
  '/neLng/' + neLng + '/';

  type: 'POST',
  url: 'Proxy.asmx/getMeTheDataFrom',
  data: "{'here':'"+endpoint+"'}",
  contentType: "application/json; charset=utf-8",
  dataType: "json",
  success: function(data) { 

The only bit that had me confused for a while was forgetting that browsers don’t like cross browser ajax requests. The data will be returned and is visible in fiddler, but the javascript (or jQuery in my case) will give a very helpful “error” error.

As such, I created the World’s Simplest Proxy:

public class Proxy: System.Web.Services.WebService

    public string getMeTheDataFrom(string here)
        using (var response = new System.Net.WebClient())
            return response.DownloadString(here);

All this does, quite obviously, is to forward a request and pass back the response, running on the server – where cross domain requests are just peachy.

Then I have a function to render the json response

function displayStopListing(stopListingData){
var data = $.parseJSON(stopListingData);
	$.each(data.markers, function(i,item){
	  .text( + ' (stop ' + item.stopIndicator + ') to ' + item.towards)
	  .attr("onclick", "getBusListingForStop(" + + ")")
	  .attr("class", "stopListing")

And then retrieve and display the bus listing

function getBusListingForStop(stopId){
var endpoint = '' + stopId + '/';
	$("#" + stopId).attr("onclick","");
		type: 'POST',
		url: 'Proxy.asmx/getMeTheDataFrom',
		data: "{'here':'"+endpoint+"'}",
		contentType: "application/json; charset=utf-8",
		dataType: "json",
		success: function(data) { displayBusListing(data.d, stopId); }
function displayBusListing(busListingData, stopId){
	var data = $.parseJSON(busListingData);
  $("<h2 />").text("Buses Due").appendTo("#" + stopId);
	$.each(data.arrivals, function(i,item){
	  .attr("class", "busListing time")
	  .appendTo("#" + stopId);
	  .text(item.routeName + ' to ' + item.destination)
	  .attr("class", "busListing info")
	  .appendTo("#" + stopId);
	  .appendTo("#" + stopId);

(yes, my jQuery is pants. I’m working on it..)

These just need some very basic HTML to hold the output

<h1>Bus Stops Near You (tap one)</h1> 
<ul id="stopListing"></ul> 

Which ends up looking like

The resultingfull HTML can be found here, the Most Basic Proxy Ever is basically listed above, but also in “full” here. If you want to see this in action head over to

Next up – how this little page was pushed into the cloud in a few seconds with the wonder of AppHarbor and git.


Since creation of this “app” TFL have created a very nice mobile version of their own which is much nicer than my attempt! Bookmark it at :