How to build a jQuery Tornado chart using the data chart control

Marina Stoyanova / Friday, February 21, 2014

tornado chart header imageThe Ignite UI Data Chart is a chart control that uses the Canvas tag in HTML5  to plot different kinds of chart into web applications and pages. The control has numerous series which can be combined and this way you can create even more series such as a tornado chart. The tornado chart is a special type of bar chart, which present values in a horizontal rather than vertical orientation. Those values are usually sorted from the largest bar at the top to the smallest one at the bottom. The final appearance of the chart is in a tornado shape and hence it is called. Tornado charts are useful for comparing relative variables or for sensitive analysis.  The current blog is based on Graham Murray ' s ideas and help for the sample.

 

Step one

As always the first step to configure the widget is to add the required references as well as the JS and CSS files. If you are creating a JavaScript project you will need two div tags – one for the control and one for the legend. After you are done doing that you can start configuring the data chart.  For the purpose of our sample we are going to use movies data from Freebase.

Freebase CC-BYSource: Freebase, licensed under CC-BY. We are going to compare the number of comedy and drama movies that came out in theaters for the years: 2000 to 2010.

JS:

  1. <script src="../../Scripts/jquery-ui-1.8.20.min.js">script>
  2. <script src="../../Scripts/infragistics.core.js">script>
  3. <script src="../../Scripts/infragistics.dv.js">script>

 

  1. <div id="chart">div>
  2. <div id="legend">div>

Data:

  1. var data = [
  2.     { 'Year': 2010, 'Comedy': 449, 'Drama': 780},
  3.     { 'Year': 2009, 'Comedy': 682, 'Drama': 1143 },
  4.     { 'Year': 2008, 'Comedy': 729, 'Drama': 1034 },
  5.     { 'Year': 2007, 'Comedy': 660, 'Drama': 982 },
  6.     { 'Year': 2006, 'Comedy': 765, 'Drama': 1117 },
  7.     { 'Year': 2005, 'Comedy': 686, 'Drama': 1042 },
  8.     { 'Year': 2004, 'Comedy': 639, 'Drama': 998 },
  9.     { 'Year': 2003,  'Comedy': 593,  'Drama': 864},
  10.     { 'Year': 2002,  'Comedy': 543,  'Drama': 850},
  11.     { 'Year': 2001, 'Comedy': 467, 'Drama': 732 },
  12.     { 'Year': 2000,  'Comedy': 417, 'Drama': 653, }
  13. ];

MVC:

  1. @using Infragistics.Web.Mvc
  2. @model IQueryable<TornadoChart.Models.Movies>

Model:

  1. public class Movies
  2. {
  3.     public int Year { get; set; }
  4.     public int Drama { get; set; }
  5.     public int Comedy { get; set; }
  6. }

HomeController:

  1. public ActionResult MVC()
  2. {
  3.     var data = new List<Movies>()
  4.     {
  5.         new Movies{Year=2010,Comedy=449,Drama=780},
  6.         new Movies{Year=2009,Comedy=682,Drama=1143},
  7.         new Movies{Year=2008,Comedy=729,Drama=1034},
  8.         new Movies{Year=2007,Comedy=660,Drama=982},
  9.         new Movies{Year=2006,Comedy=765,Drama=1117},
  10.         new Movies{Year=2005,Comedy=686,Drama=1042},
  11.         new Movies{Year=2004,Comedy=639,Drama=998},
  12.         new Movies{Year=2003,Comedy=593,Drama=864},
  13.         new Movies{Year=2002,Comedy=543,Drama=850},
  14.         new Movies{Year=2001,Comedy=467,Drama=732},
  15.         new Movies{Year=2000,Comedy=417,Drama=653},
  16.  
  17.     };
  18.     return View(data.AsQueryable());
  19. }

Step two

We have everything needed to proceed with the configuration of the widget.  Let’s start with configuring the basic – we need to set height, width and dataSource which we are going to connect with the data array we defined.  Then we need to configure the chart axis options. The axis type determines if this is the category X-axis or a numeric (value) Y-axis, and the axes names are used in data series configuration to refer to which axis to map a particular data series. We will define two Y-axes and one X-axis. The X-axis main role in the case is to determine the referenceValue (the starting point for our bars).By default this value is 0. We have two Y-values: one for each bar. Although you can place as many bars as you need at one Y-axis they will be positioned one above the other. For the tornado chart we will need the opposite  bars to be displayed at the same visual line (to align the series) that is why we need two Y-values. Using the labelVisibility property we can collapse the labels of one of the Y-axes.

The crossing axis property can be used for changing where the X-Axis crosses the Y-Axis. You can set one of the Y-axis to cross the X-axis, and assign to the stroke and strokeThickness properties some values. This way when the tornado chart is ready the bars will be separated visually by a line (the Y-axis).

The last thing to do is configure the series. They are mapped to the corresponding Y-axes because the values in the two data series exist in different value ranges and we want to display them in a different way. We choose the bar type for the series because we want the bars to be displayed horizontally.  We can customize the bars by changing their brush or outline. The valueMemberPath connects the series to the data property we want it to display. If we want a legend we should assigned it with the legend option which points to the div element we created earlier.

JS:

  1. $("#chart").igDataChart({
  2.     dataSource: data,
  3.     width: "500px",
  4.     heigh: "250px",
  5.     title: "Movies",
  6.     subtitle: "Number of comedies and drama movies for the years: 2000-2010",
  7.     axes: [{
  8.         name: "yAxis",
  9.         type: "categoryY",
  10.         label: "Year",
  11.         crossingAxis: "xAxis",
  12.         stroke: "black",
  13.         strokeThickness: 2,
  14.         isInverted: true,
  15.         gap: 10,
  16.         interval: 1
  17.     }, {
  18.         name: "yAxis2",
  19.         type: "categoryY",
  20.         label: "Year",
  21.         isInverted: true,
  22.         labelVisibility: "collapsed",
  23.         gap: 10
  24.     }, {
  25.         name: "xAxis",
  26.         type: "numericX",
  27.         labelExtent: 30,
  28.         formatLabel: function (val) {
  29.             return Math.abs(val);
  30.         }
  31.     }], series: [{
  32.         name: "Comedy",
  33.         type: "bar",
  34.         xAxis: "xAxis",
  35.         yAxis: "yAxis",
  36.         valueMemberPath: "Comedy",
  37.         title: "Comedy",
  38.         brush: "#666699",
  39.         outline: "#666699"
  40.  
  41.     }, {
  42.         name: "Drama",
  43.         type: "bar",
  44.         xAxis: "xAxis",
  45.         yAxis: "yAxis2",
  46.         valueMemberPath: "Drama",
  47.         title: "Drama",
  48.         brush: "#336699",
  49.         outline: "#336699",
  50.  
  51.     }],
  52.     legend: {
  53.         element: "legend"
  54.     }
  55. });

MVC:

  1. @(Html.Infragistics().DataChart(Model)
  2.     .ID("chart")
  3.     .Height("400")
  4.     .Width("500")    
  5.     .Title("Movies")
  6.     .Subtitle("Number of comedies and drama movies for the years: 2000-2010")    
  7.     .Axes(a =>
  8.     {
  9.         a.CategoryY("yAxis")
  10.             .Label(item => item.Year)
  11.             .IsInverted(true)           
  12.             .Gap(10)
  13.             .Interval(1);
  14.         a.CategoryY("yAxis2")
  15.             .Label(item => item.Year)
  16.             .IsInverted(true)
  17.             .LabelVisibility(Visibility.Collapsed)
  18.             .Stroke("#000")
  19.             .Gap(10);
  20.         a.NumericX("xAxis")
  21.             .CrossingAxis("categodyY")
  22.             .ReferenceValue(0)
  23.             .LabelExtent(60)            
  24.             .FormatLabel("function(val){return Math.abs(val);}");
  25.     })
  26.     .Series(s => {
  27.         s.Bar("Drama")
  28.             .XAxis("xAxis")
  29.             .YAxis("yAxis2")
  30.             .ValueMemberPath("Drama")
  31.             .Title("Drama")
  32.             .ShowTooltip(true)
  33.             .TooltipTemplate("tooltipTemplateDrama")
  34.             .IsTransitionInEnabled(false);
  35.         s.Bar("Comedy")
  36.             .XAxis("xAxis")
  37.             .YAxis("yAxis")
  38.             .ValueMemberPath("Comedy")
  39.             .Title("Comedy")
  40.             .ShowTooltip(true)
  41.             .TooltipTemplate("tooltipTemplateComedy")            
  42.             .IsTransitionInEnabled(false)
  43.             .Brush("#666699")
  44.             .Outline("#666699");
  45.     
  46.     })
  47.     .Legend(legend => legend.ID("legend"))
  48.     .DataBind()
  49.     .Render())

Step Three

Building a tornado chart out of the data chart is not that hard. We want the different bars of the chart to be displayed in opposite directions and for that reason we call the convert function which transform one of the data properties’ values into negative integers.  After the call of the converting function we will have negative values in the data and for that reason the labels will have negative values as well which doesn’t fit the data we are presenting. That is why we convert the labels using the formatLabel property and we will display the numbers’ absolute value. As we said to achieve a tornado appearance for the chart we need the series to be aligned that is why we use two Y-axes.

  1. function convert() {
  2.     var i;
  3.     for (i = 0; i < data.length; i++) {
  4.         if (data[i].Drama) {
  5.             data[i].Drama = data[i].Drama * -1;
  6.         }
  7.     }
  8. }
  1. formatLabel: function (val) {
  2.     return Math.abs(val);
  3. }

This was the last step for the creation of the the tornado chart.

Tornado chart using igDataChart

If you want to add tooltips to your control you just have to add the showTooltip property and set it to true. That way you will see the default template.You can create a custom template using the tooltipTemplate property. In our case we will see the negative values that correspond to the drama bars that is why we will have to handle this with the tooltipShown event.

Tooltip templates:

  1. <script id="tooltipTemplateDrama" type="text/x-jquery-tmpl">
  2.   <span>${item.Drama}span>
  3. script>
  4. <script id="tooltipTemplateComedy" type="text/x-jquery-tmpl">
  5.   <span>${item.Comedy}span>
  6. script>

JS:

  1. series: [{
  2.             name: "Comedy",
  3.             type: "bar",
  4.             xAxis: "xAxis",
  5.             yAxis: "yAxis",
  6.             valueMemberPath: "Comedy",
  7.             title: "Comedy",
  8.             showTooltip: true,
  9.             isHighlightingEnabled: true,
  10.             tooltipTemplate: "tooltipTemplateComedy",
  11.             brush: "#666699",
  12.             outline: "#666699"
  13.  
  14.         }, {
  15.             name: "Drama",
  16.             type: "bar",
  17.             xAxis: "xAxis",
  18.             yAxis: "yAxis2",
  19.             valueMemberPath: "Drama",
  20.             showTooltip: true,
  21.             isHighlightingEnabled: true,
  22.             tooltipTemplate: "tooltipTemplateDrama",
  23.             title: "Drama",
  24.             brush: "#336699",
  25.             outline: "#336699",
  26.  
  27.         }],

Event:

  1. tooltipShown: function (evt, ui) {
  2.     if (ui.tempId == "Drama") {
  3.         ui.element.text(parseInt(ui.element.text()) * -1)
  4.     }
  5. },

Image:

jQuery Tornado Chart - tooltips

To conclude

In few simple steps you can easily create a tornado chart using the Ignite UI data chart , displaying comparative values. This chart provides an easy to understand presentation of the data and an attractive addition to any web application or site.

 

Check out the live demo on jsFiddle or download the MVC sample

 

You can follow us on Twitter @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!