How to deal with multiple data on the data chart (how to create dashboards)

[Infragistics] Mihail Mateev / Sunday, February 1, 2015

Ignite UI is the Infragistics jQuery & HTML5 based framework. With Ignite UI, developers can create next generation, browser based, touch-enabled tablet, mobile & desktop experiences. In this article we will look at how we create dashboards applications using this data visualization components framework. The samples will be mainly focused on Ignite UI Data Chart (igDataChart) and different options to use different data source ( big amount of data,  public OData sources ) and how to arrange these charts in your layout. .

In the near future cloud systems will have an increasing impact on our lives. Developers will focus more on building applications in the cloud.

Let's start with how we can create WEB application with charts and and different data sources:

 

This article  will cover several main points related to how to create web dashboards using Ignite UI Data Chart (igDataChart) component:

  • Prerequisites
  • Composite charts
  • Create composite layouts
  • Using Ignite UI Data Chart with high volume of data
  • Using Infragistics jQuery chart with live OData services
  • Composite dashboards with different controls ( igDataChart, igGrid, igMap )
  • Demo ( different Ignite UI Charts )

 

  • Prerequisites:

 

To add igDataChart to your web application, you need the required JavaScript and CSS files referenced on the HTML page.

The links below show the required resources:



 

Composite charts:

Composite charts plot at least two data series that either use different axis ranges or are visualized by two different types of graphs. That means that, for example, one function spans between 1 and 100, and the other between 5 and 500, or even has a logarithmic scale, or one function is depicted by columns and the other by line.

 

$("#chart").igDataChart({
	dataSource: everyThreeYears,
	width: "450px",
	height: "450px",
	title: "Energy Production Per Country",
	subtitle: "The top three Total Primary Energy producers",
	horizontalZoomable: true,
	verticalZoomable: true,
	axes: [{
		type: "categoryX",
		name: "xAxis",
		label: "Year",
		strokeThickness: 5,
		title: "Year"
	}, {
		type: "numericY",
		name: "prodPower",
		minimumValue: 0,
		maximumValue: 100,
		strokeThickness: 5,
		title: "Quadrillion Btu"
	}, {
		type: "numericY",
		name: "totalPower",
		minimumValue: 50,
		maximumValue: 250,
		labelLocation: "outsideRight",
		title: "Quadrillion Btu",
	}],
	series: [{
		type: "column",
		isHighlightingEnabled: true,
		isTransitionInEnabled: true,
		name: "China",
		title: "China",
		xAxis: "xAxis",
		yAxis: "prodPower",
		valueMemberPath: "China",
		showTooltip: true,
		tooltipTemplate: "tooltipChina",
		legend: { element: "legend" }
	}, {
		type: "column",
		isHighlightingEnabled: true,
		isTransitionInEnabled: true,
		name: "UnitedStates",
		title: "US",
		xAxis: "xAxis",
		yAxis: "prodPower",
		valueMemberPath: "UnitedStates",
		showTooltip: true,
		tooltipTemplate: "tooltipUS",
		legend: { element: "legend" }
	}, {
		type: "column",
		isHighlightingEnabled: true,
		isTransitionInEnabled: true,
		name: "Russia",
		title: "Russia",
		xAxis: "xAxis",
		yAxis: "prodPower",
		valueMemberPath: "Russia",
		showTooltip: true,
		tooltipTemplate: "tooltipRussia",
		legend: { element: "legend" }
	}, {
		type: "line",
		isHighlightingEnabled: true,
		isTransitionInEnabled: true,
		name: "Total",
		title: "Total Energy",
		xAxis: "xAxis",
		yAxis: "totalPower",
		valueMemberPath: "Total",
		brush: "Orange",
		showTooltip: true,
		tooltipTemplate: "tooltipTotal",
		legend: { element: "legend2" }
	}],
	leftMargin: 5,
	topMargin: 15
});

 

You can see the final chart, based on the code above:

 

 

    Create composite layouts

      One of the most important when you are going to create dashboards is how to arrange your components in the page layout.

      The last two sections of this article demonstrate several layouts.

       

      Ignite UI Data Chart by default uses div element:

       

      It is also possible to use different div from your DOM for other igDataChart elements like legend:

      Using Ignite UI Data Chart with  high volume of data

      igDataChart works properly with big amount elements in the series.

       

      You can find more information in the Ignute UI website

       

      The image below demonstrates line series with high volume data:

       

       

       
      Using Infragistics jQuery chart with live OData services

      You can bind to the OData services, that returns data in json format , but often you need to maintain this data before to bind it to the igDataChart.

      The code below demonstrates how to manage Northwind data from www.odata.org website.

      Infragistics jQuery Chart expects a collection for its series. In this case collection is under the value option:

       var horthwndAPI = "http://services.odata.org/V4/Northwind/Northwind.svc/Products";
      
      $.getJSON(horthwndAPI, function (json) {
      
      	// Set the variables from the results array
      	var datavalues = json.value;
      	
      	$("#chart").igDataChart({
      		width: "100%",
      		height: "220px",
      		dataSource: datavalues,
      		//dataSource: "http://services.odata.org/V4/Northwind/Northwind.svc/Products",
      		axes: [{ name: "xAxis", type: "categoryX", label: "OrderID", labelVisibility: "visible" },
      				{ name: "yAxis", type: "numericY", labelVisibility: "visible" }],
      		series: [
      			{
      				name: "series",
      				title: "Product",
      				type: "line",
      				xAxis: "xAxis",
      				yAxis: "yAxis",
      				valueMemberPath: "UnitPrice", trendLineThickness: 6, thickness: 4,
      				trendLineBrush: "cyan",
      				transitionDuration: 1500,
      				trendLineType: "exponentialAverage"
      			}],
      		horizontalZoomable: true,
      		verticalZoomable: true,
      		windowResponse: "immediate",
      		overviewPlusDetailPaneVisibility: "visible"
      	});	
      
      });
      

       

      Composite dashboards with different controls (igDataChart, igGrid, igMap)

       

      Often you need to arrange different components in your dashboard and to let them to interact:
      for example if you select a record from the igGrid, to update your data for igDataChart and igMap,

       

      First you need to consider how to arrange components in the page layout:

       

      Highlight Color:

      Then you need to implement interaction between components:

      The Ignite UI Data Chart datasource is updated based of the selected record in igGrid:

       

      $("#chart").igDataChart({
      	width: "100%",
      	height: "220px",
      	dataSource: "/Home2/Orders",
      	axes: [{ name: "xAxis", type: "categoryX", label: "OrderID", labelVisibility: "visible" },
      			{ name: "yAxis", type: "numericY", labelVisibility: "visible"}],
      	series: [
      		{ name: "series",
      			title: "Order Freight Series",
      			type: "line",
      			xAxis: "xAxis",
      			yAxis: "yAxis",
      			valueMemberPath: "Freight", trendLineThickness: 6, thickness: 4,
      			trendLineBrush: "cyan",
      			transitionDuration: 1500,
      			trendLineType: "exponentialAverage"
      		}],
      	horizontalZoomable: true,
      	verticalZoomable: true,
      	windowResponse: "immediate",
      	overviewPlusDetailPaneVisibility: "visible"
      });
      
       $("#chart").igDataChart({
      	 dataSource: "/Home2/Orders?userID=" + args.row.element[0].cells[0].textContent
       });

       

      The full JavaScript source is available below:

       

      $("#map").igMap({
      	width: "100%",
      	panModifier: "control",
      	horizontalZoomable: true,
      	verticalZoomable: true,
      	windowResponse: "immediate",
      	overviewPlusDetailPaneVisibility: "visible",
      	windowRect: 
      	  {
      		  left: 0,
      		  top: 0,
      		  width: 0.5,
      		  height: 0.5
      	  },
      	seriesMouseLeftButtonUp: function (ui, args) {
      		var tets = args;
      	}
      });
      
      $("#chart").igDataChart({
      	width: "100%",
      	height: "220px",
      	dataSource: "/Home2/Orders",
      	axes: [{ name: "xAxis", type: "categoryX", label: "OrderID", labelVisibility: "visible" },
      			{ name: "yAxis", type: "numericY", labelVisibility: "visible"}],
      	series: [
      		{ name: "series",
      			title: "Order Freight Series",
      			type: "line",
      			xAxis: "xAxis",
      			yAxis: "yAxis",
      			valueMemberPath: "Freight", trendLineThickness: 6, thickness: 4,
      			trendLineBrush: "cyan",
      			transitionDuration: 1500,
      			trendLineType: "exponentialAverage"
      		}],
      	horizontalZoomable: true,
      	verticalZoomable: true,
      	windowResponse: "immediate",
      	overviewPlusDetailPaneVisibility: "visible"
      });
      
      $('#grid').igGrid({
      	virtualization: false, height: 280, width: "100%",
      	dataSource: "/Home2/Customers",
      	autoGenerateColumns: false,
      	columns: [
      		{ headerText: "Customer ID", key: "CustomerID", width: "120px", dataType: "string" },
      		{ headerText: "Country", key: "Country", width: "150px", dataType: "string" },
      		{ headerText: "City", key: "City", dataType: "string" },
      		{ headerText: "Contact", key: "ContactName", dataType: "string" },
      		{headerText: "Phone", key: "Phone", dataType: "string" }
      	],
      	features: [
      
      	 {
      		 name: 'Selection',
      		 mode: 'row',
      		 multipleSelection: false,
      		 rowSelectionChanged: function (ui, args) {
      			 $("#chart").igDataChart({
      				 dataSource: "/Home2/Orders?userID=" + args.row.element[0].cells[0].textContent
      			 });
      
      			 selected = args.row.element[0].cells[0].textContent; //keep track of selected user
      			 var countryUrl = "/Home2/CountryByName?countryName=" + args.row.element[0].cells[1].textContent;
      
      			$.getJSON(countryUrl,
      			   function (json, text) {
      				   $.each(json, function (index, value) {
      					   var country = value;
      					   var extend = country["Extend"];                                   
      
      					   var shapeData = country["ShapeData"];
      
      					   selectedColor = $('#color1')[0].value;
      
      					   var zoom = $("#map").igMap("getZoomFromGeographic", extend);
      					   $("#map").igMap("option", "windowRect", zoom);
      					  
      					   $("#map").igMap("option", "series", [{
      						   name: "series1",
      						   type: "geographicShape",
      						   markerType: "none",
      						   dataSource: shapeData,
      						   shapeMemberPath: "Points",
      						   shapeStyle: {
      							   fill: selectedColor,
      							   stroke: "black",
      							   thickness: 8.0
      						   }
      
      					   }]
      					 );
      					 
      					   var center = country["Center"];
      					   var name, lat, lon, centerData;
      
      					   name = country["CountryName"];
      
      					   lat = parseFloat(center.y);
      					   lon = parseFloat(center.x);
      
      					   centerData = [{ Name: name, Latitude: lat, Longitude: lon }];
      
      					   $("#map").igMap("option", "series", [{
      						   name: "Countries", 
      						   type: "geographicSymbol",
      						   longitudeMemberPath: "Longitude",
      						   latitudeMemberPath: "Latitude",
      
      						   /*
      						  The provided object should have properties called render and optionally measure.
      						  These are functions which will be called that will be called to handle the user specified custom rendering.
      						  */
      						   markerTemplate: {
      							   render: function (renderInfo) {
      								   var ctx = renderInfo.context; //2d canvas context
      								   var x = renderInfo.xPosition;
      								   var y = renderInfo.yPosition;
      
      								   if (renderInfo.isHitTestRender) {
      									   //  This is called for tooltip hit test only
      									   //  Rough marker rectangle size calculation
      									   ctx.fillStyle = 'yellow';
      									   ctx.fillRect(x, y, renderInfo.availableWidth, renderInfo.availableHeight);
      								   } else {
      									   //actual marker drawing is here:
      									   var markerData = renderInfo.data;
      									   var name = markerData.item()["Name"];
      									   //set font or measure will be for the default one
      									   ctx.font = '10pt Segoe UI';
      									   var textWidth = ctx.measureText(name).width;
      
      									   //Move the path point to the desired coordinates:
      									   ctx.moveTo(x, y);
      									   //Draw lines:
      									   ctx.beginPath();
      									   ctx.lineTo(x - (textWidth / 2) - 5, y + 5);
      									   ctx.lineTo(x - (textWidth / 2) - 5, y + 40); // 35width rect.
      									   ctx.lineTo(x + (textWidth / 2) + 5, y + 40); // full textWidth line plus 5 margin
      									   ctx.lineTo(x + (textWidth / 2) + 5, y + 5); // 35 up
      									   ctx.lineTo(x, y);
      									   //finish the shape
      									   ctx.closePath();
      									   ctx.fillStyle = 'rgba(78,183,226,0.7)';
      									   ctx.fill();
      									   ctx.lineWidth = 0.5;
      									   ctx.strokeStyle = '#185170';
      									   ctx.stroke();
      									   //add a point at the start
      									   ctx.beginPath();
      									   ctx.fillStyle = 'black';
      									   ctx.arc(x, y, 1.5, 0, 2 * Math.PI, true);
      									   ctx.fill();
      
      									   //  Draw text
      									   ctx.textBaseline = 'top';
      									   ctx.fillStyle = 'black';
      									   ctx.textAlign = 'center';
      									   ctx.fillText(selected, x, y + 8);
      									   ctx.fillText(name, x, y + 20);
      								   }
      							   }
      						   },
      
      						   dataSource: centerData
      					   }]
      					); //MM2
      
      				   });
      			 });
      
      		 }
      	 }
      	,
      
      	{
      		name: 'Sorting',
      		type: 'remote'
      	},
      	{
      		name: 'Paging',
      		type: 'local',
      		pageSize: 10,
      		//pageSizeDropDownLocation: 'inpager',
      		pageCountLimit : 1
      	}]
      	,
      	rendered: function (ui, args) {
      		//set up on-load selection 
      		$('#grid').igGridSelection('selectRow', 0);
      		//another way to get cell value independant of event parameters
      		var id = $('#grid').igGrid('getCellValue', 0, 'CustomerID');
      		$('#chart').igDataChart({
      			dataSource: '/Home2/Orders?userID=' + id
      		});
      
      
      	}
      });

       

      The final result is like the on the picture below:

       

       

      If can see the live demo, hosted in Microsoft Azure here:

       

       

        Demo (different Ignite UI Charts)

           

          Ignite UI dashboard demo includes several igDataChart instances and one igDoughnutChart widget.

           

          The Doughnut Chart , mentioned in my previous post: “How to Customize Ignite UI Doughnut Chart Control” is also added in the right side of the layout:

           

          Ignite UI Doughnut Chart supports ( in the same way like igDataChart ) multiple data sources that can be set to each series.

          The snippet demonstrates how you can set data:

           

          series:
          	[
          		{
          			name: "Budget",
          			labelMemberPath: "Label",
          			valueMemberPath: "Budget",
          			dataSource: data2,
          			labelsPosition: "bestFit",
          			brushes: ["#B284BE", "#5D8AA8", "#C9FFE5", "#7CB9E8", "#F19CBB", "#FAEBD7"],
          			legend: { element: "legend1" },
          			selectedStyle: { fill: "lightpink", stroke: "black" },
          			showTooltip: true,
          			tooltipTemplate: "budgetTooltipTemplate"
          		}
          	]

           

          You can see the whole code for igDoughnutChart chart below:

           

          $(function () {
          
                  var data = [
                                  { "savings": 210, "Budget": 600, "Label": "George" },
                                  { "savings": 100, "Budget": 400, "Label": "Sam" },
                                  { "savings": 250, "Budget": 550, "Label": "John" },
                                  { "savings": 29, "Budget": 300, "Label": "Mike" },
                                  { "savings": 130, "Budget": 650, "Label": "David" },
                                  { "savings": 54, "Budget": 200, "Label": "Sally" }
                  ];
          
                  var array = new Array();
                  $("#chart").igDoughnutChart({
                      width: "450px",
                      height: "450px",
                      innerExtent: 10,
                      allowSliceExplosion: true,
                      allowSliceSelection: true,
                      sliceClick: function (e, ui) {
                          ui.slice.isSelected = !ui.slice.isSelected;
                          ui.slice.isExploded = !ui.slice.isExploded;
                          array.push(ui.slice);
                          $("#selectedItem").empty();
                          for (var i = 0; i < array.length; i++) {
                              if (array[i].isSelected) {
                                  $("#selectedItem").append("

          " + array[i].item.Label + " recieves " + array[i].item.Budget + "$ and manage to saves " + array[i].item.savings + "$.

          ");
                              }
                              if (!(array[i].isSelected)){
                                  $("#" + array[i].item.Label).remove();
                                  
                              }
                          }
                      },
                      series:
                          [
                              {
                                  name: "Budget",
                                  labelMemberPath: "Label",
                                  valueMemberPath: "Budget",
                                  dataSource: data,
                                  labelsPosition: "bestFit",
                                  brushes: ["#B284BE", "#5D8AA8", "#C9FFE5", "#7CB9E8", "#F19CBB", "#FAEBD7"],
                                  legend: { element: "legend1" },
                                  selectedStyle: { fill: "lightpink", stroke: "black" },
                                  showTooltip: true,
                                  tooltipTemplate: "budgetTooltipTemplate"
                              },
                              {
                                  name: "savings",
                                  labelMemberPath: "Label",
                                  valueMemberPath: "savings",
                                  dataSource: data,
                                  labelsPosition: "bestFit",
                                  brushes: ["#7FFFD4", "#FF91AF", "#BCD4E6", "#FAE7B5", "#A2A2D0", "#FBEC5D"],
                                  selectedStyle: { fill: "#FF6A6F", stroke: "black" },
                                  showTooltip: true,
                                  tooltipTemplate: "savings Template",
                                  legend: { element: "legend2" }
                              }
                          ]
          
                  });
          
              });

           

          It looks in this way:

           

           

          You can see the whole JavaScript source code below:

           

          $(function () {
          	vara currData, currDataSource, doGeneration, startTime = null;
          			var sliderValue = 50000;
          
          			function doGeneration() {
          				var num = sliderValue, data = [], curr = 10;
          
          				for (var i = 0; i < num; i++) {
          					if (Math.random() > .5) {
          						curr += Math.random() * 2.0;
          					} else {
          						curr -= Math.random() * 2.0;
          					}
          					var val1 = Math.round(curr * 1000.0) / 1000.0;
          					data[i] = { Label: i.toString(), Value: val1 };
          				}
          				return data;
          			}
          
          
          	currData  = doGeneration();
          		
          	var data2 = [
          							{ "savings": 210, "Budget": 600, "Label": "George" },
          							{ "savings": 100, "Budget": 400, "Label": "Sam" },
          							{ "savings": 250, "Budget": 550, "Label": "John" },
          							{ "savings": 29, "Budget": 300, "Label": "Mike" },
          							{ "savings": 130, "Budget": 650, "Label": "David" },
          							{ "savings": 54, "Budget": 200, "Label": "Sally" }
          			];
          		
          		var array = new Array();
          
          	var array = new Array();
          	$("#doughnutChart").igDoughnutChart({
          		width: "100%",
          		height: "250px",
          		innerExtent: 10,
          		allowSliceExplosion: true,
          		allowSliceSelection: true,
          		sliceClick: function (e, ui) {
          			ui.slice.isSelected = !ui.slice.isSelected;
          			ui.slice.isExploded = !ui.slice.isExploded;
          			array.push(ui.slice);
          			$("#selectedItem").empty();
          			for (var i = 0; i < array.length; i++) {
          				if (array[i].isSelected) {
          					$("#selectedItem").append("

          " + array[i].item.Label + " recieves " + array[i].item.Budget + "$ and manage to saves " + array[i].item.savings + "$.

          ");
          				}
          				if (!(array[i].isSelected)){
          					$("#" + array[i].item.Label).remove();
          					
          				}
          			}
          		},
          		series:
          			[
          				{
          					name: "Budget",
          					labelMemberPath: "Label",
          					valueMemberPath: "Budget",
          					dataSource: data2,
          					labelsPosition: "bestFit",
          					brushes: ["#B284BE", "#5D8AA8", "#C9FFE5", "#7CB9E8", "#F19CBB", "#FAEBD7"],
          					legend: { element: "legend1" },
          					selectedStyle: { fill: "lightpink", stroke: "black" },
          					showTooltip: true,
          					tooltipTemplate: "budgetTooltipTemplate"
          				},
          				{
          					name: "savings",
          					labelMemberPath: "Label",
          					valueMemberPath: "savings",
          					dataSource: data2,
          					labelsPosition: "bestFit",
          					brushes: ["#7FFFD4", "#FF91AF", "#BCD4E6", "#FAE7B5", "#A2A2D0", "#FBEC5D"],
          					selectedStyle: { fill: "#FF6A6F", stroke: "black" },
          					showTooltip: true,
          					tooltipTemplate: "savings Template",
          					legend: { element: "legend2" }
          				}
          			]
          
          	});  
          		
          
          	$("#datachart").igDataChart({
          					width: "100%",
          					height: "200px",
          					title: "Random Generated Data",
          					dataSource: currData,
          					axes: [{
          						name: "xAxis",
          						type: "categoryX",
          						label: "Label"
          					}, {
          						name: "yAxis",
          						type: "numericY"
          					}],
          					series: [{
          						name: "series1",
          						title: "Test Series",
          						type: "line",
          						xAxis: "xAxis",
          						yAxis: "yAxis",
          						valueMemberPath: "Value",
          						showTooltip: true,
          						isTransitionInEnabled: true,
          						isHighlightingEnabled: true,
          						tooltipTemplate: "tooltipTemplate"
          					}],
          					horizontalZoomable: true,
          					verticalZoomable: true,
          					windowResponse: "immediate"
          				});
          	   
          	 var horthwndAPI = "http://services.odata.org/V4/Northwind/Northwind.svc/Products";
          
          	$.getJSON(horthwndAPI, function (json) {
          
          		// Set the variables from the results array
          		var datavalues = json.value;
          		
          		$("#chart").igDataChart({
          			width: "100%",
          			height: "220px",
          			dataSource: datavalues,
          			//dataSource: "http://services.odata.org/V4/Northwind/Northwind.svc/Products",
          			axes: [{ name: "xAxis", type: "categoryX", label: "OrderID", labelVisibility: "visible" },
          					{ name: "yAxis", type: "numericY", labelVisibility: "visible" }],
          			series: [
          				{
          					name: "series",
          					title: "Product",
          					type: "line",
          					xAxis: "xAxis",
          					yAxis: "yAxis",
          					valueMemberPath: "UnitPrice", trendLineThickness: 6, thickness: 4,
          					trendLineBrush: "cyan",
          					transitionDuration: 1500,
          					trendLineType: "exponentialAverage"
          				}],
          			horizontalZoomable: true,
          			verticalZoomable: true,
          			windowResponse: "immediate",
          			overviewPlusDetailPaneVisibility: "visible"
          		});	
          
          	});
              
          });

           

          The dashboard looks like this one below:

           

           

           

           

          The whole dashboard example you can find in JSFIDDLE here:

           

          The Infragistics jQuery Data Visualization controls offer  a wide range of options to create almost any dashboard that you need. In this post we use Ignite UI Data Chart and Doughnut Chart, but you can see sample how to use any of data visualization components on the Ignite UI website.  You can find a detailed information about the control and its features and how to configure its separate parts in the API documentation as we as the http://www.igniteui.com/help/api/2014.2/.

          To play around with the Ignite UI dashboards on your own, be sure to get Infragistics Ultimate and see the chart in action in our latest sample application by clicking the banner below!