CETIS Widget Bash

On returning home from a productive and enjoyable two days in Bolton I’m rather pleased to see that our PC Availability widget displays the free PCs in the MMU drop-ins in a different order now I’m in South Manchester. Our widget became location-aware at 13:47 GMT on March 24, and popular culture would counsel against pulling the plug, so instead, I’ll try and share how we moved things on from the version we described in our previous post that is currently running in WordPress to the right of this article.

On the first day of the CETIS event, wookie champion and JISC OSS Watch Service Manager Ross Gardler described how widgets could be enhanced using open source javascript libraries, such as geo-location-javascript. While Ross was presenting I added a reference to the geo-location-javascript library to the html and some sample geo code to the pc.js file in our PC Availability widget and found that most browsers would disclose latitude and longitude after checking first with the user.

// Updated the Controller.init function
init:function() {
	if(geo_position_js.init()){ 	
           geo_position_js.getCurrentPosition(Controller.success_callback,Controller.error_callback,{enableHighAccuracy:true});
	}
	else {
		Controller.update();
	}
}
 
...
// Added Controller.success_callback
success_callback:function(p) {
		alert('latitude='+p.coords.latitude.toFixed(2)+'&longitude='+p.coords.longitude.toFixed(2));
		Controller.update();
}
 
//Added Controller.error_callback
error_callback:function(p) {
	alert('error='+p.code);
}

Having found that I could get the latitude and longitude of a mobile device, I was keen to see if these values could be used to find the nearest available drop-in PCs. Whilst the processing could be done client side, I decided it would be more flexible for the future if our PC Availability web-service were able to receive latitude and longitude as query parameters and order its results based on proximity.

We already had geo-location data for our drop-in facilities from our work with oMbiel’s CampusM mobile app, so Kieron kindly extended the table that holds the list of drop-in facilities (an MS SQL Server known as stu_services) to include two extra fields: lat and long. I then needed to modify the C# code for our web-service to:

  1. Modify the Windows Communication Framework web-service definition to take latitude and longitude params
  2. Set the default sort order as alphabetic by location
  3. Test if the service had been called with valid latitude and longitude values, and (if it had) set the sort value based on a pythagoras calculation (which I know is not as accurate as the Haversine formula, but should be adequate for our purpose)
  4. Modify the SQL query to incorporate the sort criteria

The C# for step 1 was:

[OperationContract, WebGet(UriTemplate = "getPcAvailability?latitude={latitude}&longitude={longitude}", ResponseFormat = WebMessageFormat.Xml)]
        PcAvailability getPcAvailability(string latitude, string longitude);

The C# code I used within the getPcAvailability method for steps 2 and 3 was:

double dblLatitude = 0;
double dblLongitude = 0;
string sort = ", location ";
 
if (Double.TryParse(latitude, out dblLatitude) && Double.TryParse(longitude, out dblLongitude))
{
   sort = ", SQRT(POWER(CAST(longitude AS REAL) - CAST('" + longitude + "' AS REAL),2) + " +
            " POWER(CAST(latitude AS REAL) - CAST('" + latitude + "' AS REAL),2)) ";
}

And the modified SQL for Step 4 was:

SqlCommand myCommand = new SqlCommand("" +
                "SELECT location " +
                ", 	rid " +
                ", 	info " +
                ",  	latitude " +
                ",  	longitude " + sort +
                ", 	SUM(free) as 'free' " +
                ", 	SUM(pool) as 'pool' " +
                "FROM " +
                "( " +
                "SELECT usage.rid as rid " +
                ",	stu_services.location as location " +
                ",	stu_services.info as info " +
                ",  	stu_services.lat as latitude " +
                ",  	stu_services.long as longitude " +
                ", 	count(*) as 'free' " +
                ", 	0 as 'pool' " +
                "FROM 	usage " +
                "INNER JOIN stu_services " +
                "ON usage.rid = stu_services.rid " +
                "WHERE InUse='NO' " +
                "GROUP BY usage.rid " +
                ",	stu_services.location " +
                ",	stu_services.info " +
                ",  	stu_services.lat " +
                ",  	stu_services.long " +
                "UNION " +
                "SELECT usage.rid as rid " +
                ",	stu_services.location as location " +
                ",	stu_services.info as info " +
                ",  	stu_services.lat as latitude " +
                ",  	stu_services.long as longitude " +
                ", 	0 as 'free' " +
                ", 	count(*) as 'pool' " +
                "FROM 	usage " +
                "INNER JOIN stu_services " +
                "ON usage.rid = stu_services.rid " +
                "GROUP BY usage.rid " +
                ",	stu_services.location " +
                ",	stu_services.info " +
                ",  	stu_services.lat " +
                ",  	stu_services.long " +
               ") status " +
                "GROUP BY location " +
                ",	rid " +
                ",	info " +
                ",  	latitude " +
                ",  	longitude " + sort +
                "ORDER BY " +
                " 6,1 " +
                "", myConnection);

… and it worked (on our test box at least)!
http://testapis.ad.mmu.ac.uk/icts/Service1.svc/getPcAvailability?latitude=53.47067&longitude=-2.238943

Now to modify the pc.js file of our widget to append the latitude and longitude query string parameters to the web-service URL if the device can provide them. After experiencing the benefits of the Firebug FireFox add-in as a javascript debugger, I eventually ended up with a re-worked pc.js file that worked when zipped as a widget and deployed to wookie:

var Controller = {
 
	coords:"",
 
	init:function() {
		if(geo_position_js.init()){
			geo_position_js.getCurrentPosition(Controller.success_callback,Controller.error_callback,{enableHighAccuracy:true});
		}
		else{
			Controller.update();
		}
	},
 
	update:function() {
 
		var loc = Widget.proxify("http://testapis.ad.mmu.ac.uk/icts/Service1.svc/getPcAvailability" + Controller.coords);
 
		$.ajax({
		        type: "GET",
			url: loc,
			dataType: "xml",
			timeout: 1000,
			complete: Controller.parseResponse
		});
	},
 
	parseResponse:function(response) {
 
		var rooms = $("#rooms-listview");
		rooms.empty();
		$(response.responseXML).find("room").each(function () {
			rooms.append($("<li/>").text($(this).attr("location") + ": " 
			+ $(this).attr("free") + "/" 
			+ $(this).attr("seats") + " free"));
		});
		rooms.listview("refresh");
	},
 
	success_callback:function(p) {
		Controller.coords = '?latitude='+p.coords.latitude.toFixed(2)+'&longitude='+p.coords.longitude.toFixed(2);
		Controller.update();
	},
 
	error_callback:function(p) {
	}
}

This code needs tidying – the web-service URL should be read from a properties file, etc – but hopefully this quick post will help maintain the excellent spirit of community development that everyone enjoyed at the CETIS Widget Bash. Thanks to Sheila, Li, Sarah, Ross and Scott for organizing and to all who attended for making it such a valuable event.

This entry was posted in Uncategorized. Bookmark the permalink.

One Response to CETIS Widget Bash

  1. Sheila MacNeill says:

    Hi Mark

    Sorry for not commenting sooner – but thanks to you and the rest of the guys for coming along and sharing your code, ideas, widgets. It was great to see such a lot of ‘real’ activity and hopefully we can do it all again later in the year.

    Sheila

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>