Friday, December 1, 2017

Friday Fun LIII - Sankey Plots

Aloha everyone,

Creating charts is really fun...again Thomas Nield(@thomasnield9272) pointed me to a nice chart which is called Sankey chart and again I could not withstand to try my best to implement it in JavaFX.
To give you an idea on what I'm talking about here is an example of such a Sankey plot...


Compared to last weeks Circular plots these plots can be multilevel and after searching the web for some hours I figured out that you can find all sorts of Sankey plots which might look completely different...here another example...


That doesn't make it easier to implement such a chart and so I've started reading about the history of that chart. In the end it turned out that the main purpose of this kind of chart is the visualization of flows where the width of the arrows/lines is shown proportionally to the flow quantity.
So I had to make a decision which style I should follow and I've decided to go with the first visualization of the above pictures.
Lucky me there is also a version of the Sankey plots in the Google charts which I took as a template.
Here is a screenshot of what I've come with...


This is the more colorful version of the chart but it is also possible to create other versions as you can see here...


In this version I've used different parameters for the width of the items and here also the direction of the flow is indicated by arrows (but I only support one direction anyway).

To get nice results you have to keep in mind that in my implementation you have to think about how to order the items in the chart when adding it to the control.
Meaning to say I do not have some hyper smart algorithm that do some fancy automatic sorting of items but you have to use your own brain and think about the chart before you create it. For the example above I've added the items exactly in the order as they appear on the chart which would look like follows...

// Setup chart itemsPlotItem brazil      = new PlotItem("Brazil", Colors.LIGHT_BLUE.get());
PlotItem mexico      = new PlotItem("Mexico", Colors.ORANGE.get());
PlotItem usa         = new PlotItem("USA", Colors.ORANGE.get());
PlotItem canada      = new PlotItem("Canada", Colors.LIGHT_RED.get());

PlotItem germany     = new PlotItem("Germany", Color.web("#FF48C6"));

PlotItem portugal    = new PlotItem("Portugal", Colors.LIGHT_BLUE.get());
PlotItem spain       = new PlotItem("Spain", Colors.LIGHT_GREEN.get());
PlotItem england     = new PlotItem("England", Colors.LIGHT_RED.get());
PlotItem france      = new PlotItem("France", Colors.LIGHT_GREEN.get());

PlotItem southAfrica = new PlotItem("South Africa", Colors.YELLOW.get());
PlotItem angola      = new PlotItem("Angola", Colors.PURPLE.get());
PlotItem morocco     = new PlotItem("Morocco", Colors.YELLOW.get());
PlotItem senegal     = new PlotItem("Senegal", Colors.PURPLE.get());
PlotItem mali        = new PlotItem("Mali", Colors.BLUE.get());

PlotItem china       = new PlotItem("China", Colors.BLUE.get());
PlotItem japan       = new PlotItem("Japan", Colors.GREEN.get());
PlotItem india       = new PlotItem("India", Colors.GREEN.get());

After that is done you have to define the connections between the items by defining only the outgoing streams for each item. Because that's a lot for the chart above I will only show you the ones for the first column which will look as follows...

// Setup flowsbrazil.addToOutgoing(portugal, 5);
brazil.addToOutgoing(france, 1);
brazil.addToOutgoing(spain, 1);
brazil.addToOutgoing(england, 1);
canada.addToOutgoing(portugal, 1);
canada.addToOutgoing(france, 5);
canada.addToOutgoing(england, 1);
mexico.addToOutgoing(portugal, 1);
mexico.addToOutgoing(france, 1);
mexico.addToOutgoing(spain, 5);
mexico.addToOutgoing(england, 1);
usa.addToOutgoing(portugal, 1);
usa.addToOutgoing(france, 1);
usa.addToOutgoing(spain, 1);
usa.addToOutgoing(england, 5);

As mentioned you define the streams that goes from each item to other items with their values.
After that is done you can setup the chart using the SankeyPlotBuilder as follows...

SankeyPlot sankeyPlot = SankeyPlotBuilder.create()
                                         .prefSize(600, 400)
                                         .items(brazil, mexico, usa, canada,germany,
                                                portugal, spain, england, france,
                                                southAfrica, angola, morocco, 
                                                senegal, mali, china, japan, india)
                                         .build();

And that's all it takes to create such a chart. Because the chart is again based on the JavaFX Canvas node there is no interactivity at the moment but I'm already working on a little project that will make it possible to have interactivity in the future...so stay tuned :)

Of course the code is available on github as always.

That's it for today...so keep coding...

1 comment: