Flowing Data Charts Using Dundas/Microsoft Charts

Flowing Data Charts Using Dundas/Microsoft Charts

March 10th, 2012  |  Published in Programming, Visualization

Nathan Yau’s site Flowing Data is really one of my favorite sites on visualization. His recent book Visualize This is pretty much a required book if you do a lot of work with data, charts and visualization. I’ve always liked the approach Yau has taken with his charts as it has a slightly different feel than something you’d see Edward TufteStephen Few or Dona Wong (all of whom I love) would do.

Anyway I’ve been slowly changing my charts to have more of a Flowing Data type layout and thought I would share some code. The goal here is to get as close to Nathan’s style using the Dundas / Microsoft Charting libraries as possible using ASP.NET MVC Framework.

Here is a typical Flowing Data chart [scanned from Visualize This]:

Here is my result:

So lets take a look at some of the code!

My  MVC Charts class creates a generic chart object that gets us fairly down the road to Nathan’s layout.

using System.Drawing;
using Dundas.Charting.WebControl;

namespace FlowingData.Models
{
    public class MVCCharts
    {
        ///
<summary> /// Creates the flowing data chart.
 /// </summary>
        ///The width.
        ///The height.
        ///The title.
        ///The nar.
        ///The ind.
        ///The sour.
        ///
        public Chart CreateFlowingDataChart(int width, int height, string title, string nar, string ind, string sour)
        {
            // Inital Setup
            var chart1 = new Chart
                             {
                                 Width = width,
                                 Height = height,
                                 RenderType = RenderType.ImageTag,
                                 Palette = ChartColorPalette.Dundas,
                                 BorderColor = ColorTranslator.FromHtml("#E8E8E8"),
                                 BorderWidth = 1,
                                 BorderLineStyle = ChartDashStyle.Solid
                             };

            // Add title
            var t = new Title(title, Docking.Top, new Font("Georgia", 16, FontStyle.Bold), Color.FromArgb(68, 65, 61));
            chart1.Titles.Add(t);
            chart1.Titles[0].Alignment = ContentAlignment.TopLeft;

            // Add Narrative
            var narrative = new Title(nar, Docking.Top, new Font("Georgia", 10, FontStyle.Regular),
                                      Color.FromArgb(68, 65, 61));
            chart1.Titles.Add(narrative);
            chart1.Titles[1].Alignment = ContentAlignment.TopLeft;

            // Add Indicator
            var indicator = new Title(ind, Docking.Bottom, new Font("Georgia", 10, FontStyle.Italic),
                                      Color.FromArgb(68, 65, 61));
            chart1.Titles.Add(indicator);
            chart1.Titles[2].Alignment = ContentAlignment.BottomLeft;
            chart1.Titles[2].Position.Y = 97;
            chart1.Titles[2].Position.X = 2;
            chart1.Titles[2].Position.Width = 40;

            // Add Source
            var source = new Title(sour, Docking.Bottom, new Font("Georgia", 10, FontStyle.Italic),
                                   Color.FromArgb(68, 65, 61));
            chart1.Titles.Add(source);
            chart1.Titles[3].Alignment = ContentAlignment.BottomRight;

            // Add Chart Area
            chart1.ChartAreas.Add("Area1");

            // Add Legends
            chart1.Legends.Add("Legend1");
            chart1.Legend.Alignment = StringAlignment.Center;
            chart1.Legend.Docking = LegendDocking.Bottom;

            // Add Dashes
            chart1.ChartAreas[0].AxisY.MajorGrid.LineStyle = ChartDashStyle.Dot;
            chart1.ChartAreas[0].AxisY.MajorTickMark.LineStyle = ChartDashStyle.Dot;
            chart1.ChartAreas[0].AxisY.MajorGrid.LineColor = Color.Silver;
            chart1.ChartAreas[0].AxisY.MajorTickMark.LineColor = Color.Silver;
            chart1.ChartAreas[0].AxisY.MajorGrid.LineWidth = 1;
            chart1.ChartAreas[0].AxisY.LineWidth = 0;
            chart1.ChartAreas[0].AxisY.LabelStyle.Format = "#,###";

            // Remove Vertical Lines
            chart1.ChartAreas[0].AxisX.MajorGrid.LineStyle = ChartDashStyle.NotSet;

            return chart1;
        }
    }
}

My chart model essentially adds a series for each country, adds the country to that data, and then some last minute minor chart settings and colors.

using System;
using System.Drawing;
using System.Linq;
using Dundas.Charting.WebControl;

namespace FlowingData.Models
{
    public class ChartModel
    {
        #region Variables

        private readonly FlowingDataDBDataContext _db = new FlowingDataDBDataContext();

        #endregion

        #region Create Chart

        ///
<summary> /// Creates the chart.
 /// </summary>
        ///The chart.
        public void CreateChart(Chart chart)
        {
            try
            {
                // Get Distinct List Of Names
                var names = (from t in _db.Populations
                             select new {t.Country}).Distinct();

                foreach (var country in names)
                {
                    // Get Country Name
                    var country1 = country;

                    // Get Country Data
                    IOrderedQueryable data = (from t in _db.Populations
                                                          where t.Country == country1.Country
                                                          orderby t.Year ascending
                                                          select t);

                    // Add series and set chart type
                    chart.Series.Add(country.Country);
                    chart.Series[country.Country].Type = SeriesChartType.Line;
                    chart.Series[country.Country].BorderWidth = 3;
                    SetColor(chart, country.Country);

                    // Add data points
                    foreach (Population population in data)
                    {
                        chart.Series[population.Country].Points.AddXY(population.Year,
                                                                      population.Population__total__WDI_2011);
                    }

                    // Load Chart Settings
                    ChartSettings(chart);
                }
            }
            catch (Exception ex)
            {
                throw new Exception("Error: " + ex.Message);
            }
        }

        #endregion

        #region Misc

        ///
<summary> /// Charts the settings.
 /// </summary>
        ///The chart.
        private void ChartSettings(Chart chart)
        {
            // X axis settings
            chart.ChartAreas[0].AxisX.Minimum = 1960;
            chart.ChartAreas[0].AxisX.Interval = 10;
        }

        ///
<summary> /// Sets the color.
 /// </summary>
        ///The chart.
        ///Name of the country.
        private void SetColor(Chart chart, string countryName)
        {
            // Color chooser
            switch (countryName)
            {
                case "United States":
                    chart.Series[countryName].Color = ColorTranslator.FromHtml("#08519C");
                    break;
                case "United Kingdom":
                    chart.Series[countryName].Color = ColorTranslator.FromHtml("#6BAED6");
                    break;
                case "Germany":
                    chart.Series[countryName].Color = ColorTranslator.FromHtml("#BDD7E7");
                    break;
                case "France":
                    chart.Series[countryName].Color = ColorTranslator.FromHtml("#EFF3FF");
                    break;
            }
        }

        #endregion
    }
}

In the charts controller class you’d set the charts title, narrative, source, indicator and then return it as a file result.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Dundas.Charting.WebControl;
using FlowingData.Models;

namespace FlowingData.Areas.api.Controllers
{
    public class chartsController : Controller
    {
        #region Variables

        readonly MVCCharts _charts = new MVCCharts();
        readonly ChartModel _model = new ChartModel();

        #endregion

        #region Get Chart

        ///
<summary> /// Getcharts this instance.
 /// </summary>
        ///
        public FileResult getchart()
        {
            // Set Titles
            const string title = "Population Growth of The United States vs. European Countries";
            const string indicator = "Indicator: Population, total";
            const string source = "Source: WDI-2011";
            const string narrative = "The population of various major European countries has remained " +
                                     "fairly stagnant over the last 50 years, yet in the United States " +
                                     "the population has increased at a fairly consistent rate.";

            // Create Chart
            Chart myChart = _charts.CreateFlowingDataChart(800, 450, title, narrative, indicator, source);

            // Load Chart
            _model.CreateChart(myChart);

            // Return File
            var myRand = new Random();
            var imageStream = new MemoryStream();
            myChart.Save(imageStream, ChartImageFormat.Png);
            return File(imageStream.ToArray(), "image/png", myRand.Next() + ".png");
        }

        #endregion
    }
}

…and that is it – easy peasey.

You can download the files (including the ms sql backup file) here. [I had to remove the Dundas library but you should be able to simply substitute the Microsoft version. In the code you would have to change the using Dundas.Charting.WebControl to the Microsoft library but that should be about it.]



Related Posts

ASP.NET MVC Links
Haack MVC Video: Cross-site Request Forgery Attack
Problems with Html.DropDownList
Updating Drop Downs w/ ASP.NET MVC and jQuery

Archives