Live Polar Clock in Tableau by Chris DeMartini
/It's two nights before Think Data Thursday and we are pretty much wrapped up with our Wargames project. Before the ink can dry I get this link from Allan Walker and the note "on to the next thing!”.
After taking a look at the polar clock examples in both D3 and protovis and having studied Bora Beran's great radial treemap & bar chart post it seemed very possible to do this in Tableau with just a little incremental effort. I started from Bora's workbook and went from there.
For the polar clock each radial bar needs to be based on a different unit of time. See the below graphic for how I went about laying out the radial bars and units of time for this viz (mimicking the D3 and protovis versions).
One of the main challenges we gave ourselves was to make this a "live" tableau public visualization of a clock. In order to do this I leveraged the now() function for all calculations in the workbook. The data itself is just two records for each measure of time (so 14 total records of underlying data as there is a blank bar in the middle so 7 total bars). Here is how the data looks (notice we have 7 bars and 7 levels).
I needed to calculate two main data points. What is the current value and what is the maximum value for each respective unit of time. For example, if it is 1/15/2016 3:45:23 at this moment then the seconds radial bar is currently at value 23 and has a maximum possible value of 59 (not 60). This same concept applies to all the radial bars we will create across the different units of time.
Seconds, minutes, hours and day of the week These are all calculated in the same way leveraging the datepart() function and for simplicity I just hard coded the maximum number as I don't expect those numbers to change anytime soon. Here is the simple datepart() function calculation. Note I created a field “Now” which is just Now(), originally I had a reason for doing this, but seems unnecessary now (pun intended).
Day of the month Day of the month was slightly more involved to calculate than day of the week. The current value was obtained by using day(), simple enough. However, the maximum value is a different story. This value changes with each month. February has 29 days this year and March has 31 we need our maximum value to adjust based on the current month so our day of the month bar will complete the circle. To make this happen I used the following equation and also included a screenshot of each step that is being calculated to walk through the date manipulation.
Day of the year Here we have a slightly dynamic maximum value. Every fourth year there is an extra day during the year. As a result of this we have to be dynamic in how we identify the maximum number of days in the current year. For the current value we have to do some additional calculation work as well. The current value is the number of days that have elapsed since January 1st, plus 1. For the corresponding maximum value, we need the number of days between the first and last day of this year. We again leverage datediff() to make this happen and include some datetrunc() as well. Both equations and accompanying workflows below.
Radial Bars Now that we have all of our date manipulation figured out we just need to adjust Bora's polygon equations to utilize our level, current value and maximum value fields created above. Thus I went through Bora’s equations and updated each of them to the fields I created above. I also added some classifications to the table calculations to default them to run along level and path order (bin). You can see below the fields I adjusted within Bora’s calculations, I left the rest of the calculations as is. The same concept applies to both the X and Y calculations for the bars.
The second piece needed was to refresh the view every second. This was done with just the slightest amount of code and a call to the Tableau JavaScript API. Here is a snippet of the javascript code but you can also just grab it from viewing the source of this webpage (thank you for hosting it Jeff Shaffer!). Allan Waker actually put the code together for this once I got the viz completed within Tableau. He added a number of parameters to the URL to keep the spinner and grey flash from occurring during the refresh process (worth looking at!). The one requirement is that we needed the visualization to update in less than a second which was a non issue with the underlying data only being 14 records so nothing further needed from us on that.
I wanted to call out two pieces of this code. First the clock function (directly below). This code takes the initialization function for the visualization (stored here in viz1) and has a "setTimeout()" applied to it. This two line Javascript function is what drives the "live" aspect of the polar clock you saw at the top of this post. Basically, for each second, the function calls itself and (1) refreshes the data in the visualization and (2) sets up another timeout of one second.
function clock() { viz1.refreshDataAsync(); setTimeout(clock,1000); }
The second piece is in the options that we are using for the viz initiation call. You will notice that height is 112% instead of 100%. This is how we hid the Tableau Public banner in the live view you see at the top if this blog. There are many ways to do this with things like CSS, this is one option and it is reasonably responsive. I have noticed with specific scaling of the parent container's height and width I had to do some manual tweaking (thus this method is not one size fits all).
And the result... a live polar clock in Tableau! Here is the embedded workbook in case you wanted to play around with it.