Friday, 21 December 2012

on{X} Traffic Incident Monitoring

A couple of weeks ago I blogged how I'd used on{X} to automate a couple of common tasks based on geo-location (text my Wife when I left work and turn on WiFi at home).  I've also used on{X} to solve another problem in my life, traffic jams!

After suffering a spate of traffic jams (including one that caused my to go on a country lane rally drive) I vowed that I'd use technology to avoid this in future.  You can always check websites before you travel and listen to radio bulletins but this is hit and miss, (i.e. remembering to check the website before you leave).

In the UK, the highways agency publish a bunch of XML feeds showing things like current roadworks, what's showing on the overhead matrix signs and planned roadworks.  See here for the full list.  I chose to use the "unplanned events" feed which uses their road sensor network to warn of incidents.

I've put the full code at the bottom of this posting but in simple terms it just:

1)Detects that I've unlocked the screen of my handset (which I'm doing all the time).
2)Checks that the time is either 6,7,8 in the morning or 4,5,6 in the evening (when I commute).
3)Does a HTTP GET to download the XML feed
4)Parses the feed to pick out a set of roads and place names I care about.  This is controlled by the array:
var MyTrafficArray = ["Birmingham","Wolverhampton",">A34<",">M6<",">M5<"];

So you can just edit the terms in the array to change the set of roads.  The "> <" for the road names means you get an exact match as this is how they're composed in the XML tags, e.g. avoid getting "M6" and "M60" mixed up.

Here's the tool in action.  Firstly, after a screen unlock it warned me that there was a problem on one of the roads:



Then I could just look at the Highways Agency website to get more information:

This warns of the "A34 southbound..." so I could make a judgement as to whether it would impact me.  You can event look at the raw XML, (what the on{X} app parsed} to see more information:


So quite a simple example but there's plenty more tinkering to be had here:
  • Adding a timer rather than relying on a screen unlock.
  • Mixing location/geo-fencing the XML feed to always be warned wherever I am.
  • Playing with some of the other traffic feeds, (variable message signs looks fun).
  • Using some of the other UK Government APIs (e.g. weather and crime). 

Here's the full code listing:

// Every day from 0600 until 0900 and 1600 until 1900, when the screen is unlocked, parse the highways agency XML feed for key words.  Then 
// if you find this, show a notification together with which keyword you found. I then use this to trigger lookign at the AA or similar.

//Variables, here's an array of terms to search for
var HighwaysXMLURL = "http://hatrafficinfo.dft.gov.uk/feeds/datex/England/UnplannedEvent/content.xml";
var MyTrafficArray = ["Birmingham","Wolverhampton",">A34<",">M6<",">M5<"];
var HTTPErrorString = "Error in HTTP";


// Triggers the function when the screen unlocks
device.screen.on("unlock", function(){
//console.log('I have been unlocked');
    //Check whether to call the function.  Based upon time of day
        
    //Call the function to get the XML.  This also further calls a function to parse the XML due to the interesting way this all works...
    if (DetermineWhetherToCheck()) 
      {
      GetTheXML();        
      }
    else
      {
      //Just in for test.  Comment out if all is dandy
      //var MyNotification = device.notifications.createNotification('Not time to check now dude');
      //MyNotification.show();      
      }
});

//Sends for the XML
function GetTheXML(){
    device.ajax(
    {    
      url: HighwaysXMLURL,
      type: 'GET',
      headers: {
         //'Content-Type': 'application/xml'
      }  //End of headers
    },  //End of Ajax  
    function onSuccess(body, textStatus, response) {
      //Set up the response XML
      ParseXMLResponse(body);
      //Log to console that all was good      
      //console.info('successfully received http response!');
      },  //onSuccess
    function onError(textStatus, response) {
      var error = {};
      error.message = textStatus;
      error.statusCode = response.status;
      console.error('error: ',error);
      var MeNotification = device.notifications.createNotification('Highway XML Check had HTTP Error');
        MeNotification.show();
    });    
}  //End of GetTheXML

//Parses the XML response
function ParseXMLResponse(InText){
    //Variables
    var SearchPos;         //Position in the string we are searching
    var MyNotification;    //For when we show a notification
    var HitCount = 0;      //Increases if we find a search term
    
    //Loop through our search term array, seeking a key term
    for (var i = 0; i < MyTrafficArray.length; i++) {
      //See if the search string exists in the XML
      SearchPos = InText.search(MyTrafficArray[i]);
      if (SearchPos > -1){   //-1 means not found, otherwise it's the position
        MyNotification = device.notifications.createNotification('Highway XML Check has spotted: ' + MyTrafficArray[i]);
        MyNotification.show();
        //console.log('Highway XML Check has spotted: ' + MyTrafficArray[i]);
        //Increment our hit count counter
        HitCount++;
      } //End of If 
    }  //End of for loop
    
    //See if we had no hits.  Notify just to be sure
    if (HitCount === 0){
        MyNotification = device.notifications.createNotification('Highway XML checked but found nowt');
        MyNotification.show();
    }
}  //End of function

function DetermineWhetherToCheck()
  {
  //Get the current hour value.  Only check if it's 6,7 or 8 or 16, 17 or 18'          
  //The variable to use for the time
  var currentTime = new Date();
    
  //Form the hours part and check it
  var hours = currentTime.getHours();
  
  if (hours == 6 || hours == 7 || hours == 8 || hours == 16 || hours == 17 || hours == 18)
    {
    return true;    
    }
  else
    {
    return false;    
    }
  }  //End of function