Sunday 15 May 2011

Python and Javascript: using the Flot plugin, part 5

Using the Flot plugin

In article 5 of this series we look at how we may actually configure the Flot plugin to show the data.

The Javascript code: minimalweather.js

Let's have a look at a rather minimal implementation of the client side of our app. It will only show temperature and wind speed but it is a rather good example of what is possible. The Javascript code is encapsulated in a jQuery $(document).ready() function. This way we ensure that we only convert HTML elements to jQuery widget when we're absolutely sure they are present.

The first step we take is setting some general AJAX parameters. We set cache to false which will instruct jQuery to add a _ (underscore) parameter to every AJAX call. This parameter will have a random value and this will make the URL different each time, thereby preventing the browser to cache result. After all we are not interested in stale weather data. We also set async to false. AJAX calls normally return immediately but signal completion to a function that is given to them as a parameter. However, because our graphs consist of multiple dataseries we want to retrieve the data from more than one datasource. To draw the complete graph we need all data to be available, so by setting async to false we don't start doing anything else unless the last AJAX call is finished. This is a bit against the grain (after all the first A in AJAX stands for asynchronous) but it does make out code simpler.

The second task is to set a default for the reporting period and level of detail in the graphs. The period might be a day, a week or a month and the default level of detail is a average value per hour. PyWWS compatible weather stations are normally capable of logging in a much finer detail and that is what we retrieve if detail is set to raw. (My weather station can log a value every five minutes).

Next we define two functions: hourly(), that will issue two getJSON() calls to retrieve temperature and windspeed averages over the last hour and raw() that will do the same but for 5 minute intervals. Because we designed the server side of the application to produce JSON serialized data all the hard work of converting this data to arrays of timestamp/value pairs in a safe way is done by the getJSON() function which will pass the result as the data argument to the function it calls on completion.

$(document).ready(function(){
  $.ajaxSetup({cache:false,async:false});

  var temp_out;
  var wind_ave;
  
  var period="day";
  var detail="hourly";
   
  function hourly(){
   $.getJSON('./hourly/temp_out' ,{"period":period},
   function(data){ temp_out = data; });
   $.getJSON('./hourly/wind_ave' ,{"period":period},
   function(data){ wind_ave = data; });
   }
  
  function raw(){
   $.getJSON('./raw/temp_out' ,{"period":period},
   function(data){  temp_out = data; });
   $.getJSON('./raw/wind_ave' ,{"period":period},
   function(data){  wind_ave = data; });
  }

Now that we have functions in place to retrieve data we need something to convert these two data sets (temperature and wind speed) to an object that can be passed as an argument to the Flot plugin. That is what the setdata() function does: it creates an object that defines two dataseries with an appropriate label. It also defines a second y-axis to use by the wind speed dataseries. We initialize the da variable to hourly data.

  var da;
  
  function setdata(){
   da = [
    {data:temp_out,label:"temperature"},
    {data:wind_ave,label:"wind",yaxis:2}
    ];
  };
   
  hourly();
  setdata();

With the data present and a data argument ready we can finally convert the div with the tempandwind id to a graph. The flot plugin is a little different from most plugins as it does not provide a member function on any jQuery selection (unlike for example the button plugin which can be called as $("#mybutton").button() ). The Flot plugin just provides a plot() function which is passed a jQuery selection as its first argument. The second argument is an object which describes the data series and the final argument is an option object. Here we configure all series to show lines as well as points and configure the x-axis to behave as a time axis with a maximum of twelve tick marks. If it decides to show hours as ticks we inform it to use a 24 hour clock (it might show days as well, it decides that automatically although this may be set explicitly). Note that the width and height of the HTML element that we want to convert to a graph must be set explicitly beforehand. We have take care of that in the HTML contained in the basepage.html file.

  
  var p = $.plot($("#tempandwind")  ,da ,{
    series: { lines: { show: true }, points: { show: true } },
    xaxis : { mode:"time",ticks:12,twelveHourClock:false},
    y2axis: { position:"right"}
  });

We will also configure some buttons to let the user interact with the graph so we must define some way to reload and redisplay data. We therefore define a replot() function which will retrieve either hourly data or detailed data based on the contents of the detail variable and then create a new data description object. We then use the setData() method of the Flot plugin to indicate we have new data and its setupGrid() method to recalculate things like axes and ticks. The graph is then redrawn by calling the draw() method.

  function replot(){
 if (detail == "hourly") {
  hourly();
 }else{
  raw();
 }
 setdata(); 
 p.setData(da);
 p.setupGrid();
 p.draw();
  };

Interaction with the graph is now a simple matter of binding functions to click events. These functions set either the detail or the period variable to a suitable value and then call the replot() function.

  
 $("#d2").click(function(){
  detail="raw";
  replot();
 });

 $("#d1").click(function(){
  detail="hourly";
  replot();
 });

 $("#p1").click(function(){
  period="day";
  replot();
 });
 
 $("#p2").click(function(){
  period="week";
  replot();
 });
 
 $("#p3").click(function(){
  period="month";
  replot();
 });

What is left (although we could have done it much earlier) is to style the tabs and buttons with regular jQueryUI widgets:

 
 $("#tabs").tabs();
 $("#detail").buttonset();
 $("#period").buttonset();
});

The result of all this work is a functional web application that shows temperature and wind speed on its first tab:

And it is interactive of course: Clicking the week button for example results in this overview:

Of course we there is a lot more we can do but that is covered in coming articles.

Other parts of this series

No comments:

Post a Comment