Sankey diagram in javascript
Asked Answered
O

7

16

I want to draw a Sankey diagram using Javascript. Can anyone provide some direction regarding the algorithms or libraries that are available for this?

Overtop answered 28/12, 2010 at 10:9 Comment(1)
Related question: stats.stackexchange.com/questions/24074/…Victor
O
11

This is a basic Sankey diagram using raphaeljs

function Sankey(x0, y0, height, losses) {
    var initialcolor = Raphael.getColor();
    var start = x0 + 200;
    var level = y0 + height;
    var heightunit = height / 100;
    var remaining = 100 * heightunit;

    function drawloss(start, level, loss) {
        var thecolor = Raphael.getColor();
        paper.path("M" + (start - 100) + "," + (level - loss) + "L" + start + "," + (level - loss)).attr({stroke: thecolor});
        paper.path("M" + (start - 100) + "," + level + "L" + start + "," + level).attr({stroke: thecolor});
        paper.path("M " + start + "," + level + " Q" + (start + 100) + "," + level + " " + (start + 100) + "," + (level + 100)).attr({stroke: thecolor});
        paper.path("M " + start + "," + (level - loss) + " Q" + (start + 100 + loss) + "," + (level - loss) + " " + (start + 100 + loss) + "," + (level + 100)).attr({stroke: thecolor});
        paper.path("M " + (start + 100) + "," + (level + 100) + " L " + (start - 10 + 100) + "," + (level + 100) + " L " + (start + loss / 2 + 100) + "," + (level + 110) + " L " + (start + loss + 10 + 100) + "," + (level + 100) + " L " + (start + loss + 100) + ", " + (level + 100)).attr({stroke: thecolor});
    }

    function drawremaining(start, level, loss) {
        paper.path("M 100," + y0 + "L" + (start + 100) + "," + y0).attr({stroke: initialcolor});
        paper.path("M" + (start - 100) + "," + level + "L" + (start + 100) + "," + level).attr({stroke: initialcolor});
        paper.path("M " + (start + 100) + " " + y0 + " L " + (start + 100) + " " + (y0 - 10) + " L " + (start + 110) + " " + (y0 + loss / 2) + " L " + (start + 100) + " " + (level + 10) + " L " + (start + 100) + " " + level).attr({stroke: initialcolor});
    }

    function drawstart(x0, y0, width, height) {
        paper.path("M " + x0 + "," + y0 + "L" + (x0 + width) + "," + y0).attr({stroke: initialcolor});
        paper.path("M " + x0 + "," + (y0 + height) + "L" + (x0 + width) + "," + y0 + height)).attr({stroke:  initialcolor});
        paper.path("M " + x0 + "," + y0 + "L" + x0 + "," + (y0 + height)).attr({stroke: initialcolor});
    }

    drawstart(x0, y0, 100, height);

    for (var i in losses) {
        drawloss(start, level, losses[i] * heightunit);
        remaining -= losses[i] * heightunit;
        level -= losses[i] * heightunit;
        start += 100;
    }
}

And I use it like this:

<div id="notepad" style="height:1000px; width:1000px; background: #eee"></div>
<script type="text/javascript">
    var paper = Raphael(document.getElementById("notepad"), 1020, 1000);
    var losses=[50, 30, 5];
    Sankey(10, 100, 200, losses);
</script>
Overtop answered 28/12, 2010 at 23:52 Comment(0)
C
11

In case helpful to others: I've extracted my javascript sankey diagram drawing code here:

http://tamc.github.com/Sankey/

The original usage is on this UK government site:

http://2050-calculator-tool.decc.gov.uk/pathways/2022222122222103332220023211022330220130233022012/sankey

Crybaby answered 14/7, 2011 at 14:1 Comment(0)
O
7

D3.js uses a plugin to create sankey diagrams pretty well.

http://bost.ocks.org/mike/sankey/

Oza answered 17/10, 2012 at 18:0 Comment(0)
C
2

Here is a fairly detailed explanation of how Mike Bostock's D3-based Sankey DIagram code works: http://www.d3noob.org/2013/02/sankey-diagrams-description-of-d3js-code.html

I have implemented this on a Grails-based app server and it works.

Carmoncarmona answered 5/8, 2013 at 22:27 Comment(0)
H
2

Google Charts includes the Sankey Diagram: https://developers.google.com/chart/interactive/docs/gallery/sankey

Haftarah answered 27/1, 2015 at 1:48 Comment(0)
A
1

Thanks to zenify for starting me on the path, I had to rejig some of the copied code above to get it to work but it definitely gives a good starting point. The code below can be copied into a .htm file and you just need to have raphael-min.js in the same directory for it to work.

Regards / Colm

<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" class="JS">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Raphael makes Sankey</title>
<script type="text/javascript" src="raphael-min.js"></script>
<script type="text/javascript">
function Sankey(x0,y0,height,losses){
    initialcolor= Raphael.getColor();
    var start=x0+200;
    var level=y0+height;    
    var heightunit=height/100;
    var remaining=100*heightunit;

function drawloss(start,level,loss){
    var thecolor=Raphael.getColor();
    paper.path("M"+(start-100)+","+(level-loss)+"L"+start+","+(level-loss)).attr({stroke: thecolor});
    paper.path("M"+(start-100)+","+(level)+"L"+start+","+(level)).attr({stroke: thecolor});
    paper.path("M "+start+","+level+" Q"+(start+100)+","+level+" "+(start+100)+","+(level+100)).attr({stroke: thecolor});
    paper.path("M "+start+","+(level-loss)+" Q"+(start+100+loss)+","+(level-loss)+" "+(start+100+loss)+","+(level+100)).attr({stroke: thecolor});
    paper.path("M "+(start+100)+","+(level+100)+" L "+(start-10+100)+","+(level+100)+" L "+(start+(loss/2)+100)+","+(level+110)+" L "+(start+(loss)+10+100)+","+(level+100)+" L "+(start+(loss)+100)+", "+(level+100)).attr({stroke: thecolor});
}

function drawremaining(start,level,loss){
    paper.path("M 100,"+y0+"L"+(start+100)+","+y0).attr({stroke: initialcolor});
    paper.path("M"+(start-100)+","+(level)+"L"+(start+100)+","+(level)).attr({stroke: initialcolor});
    paper.path("M "+(start+100)+" "+y0+" L "+(start+100)+" "+(y0-10)+" L "+(start+110)+" "+(y0+(loss/2))+" L "+(start+100)+" "+(level+10)+" L "+(start+100)+" "+(level)).attr({stroke: initialcolor});
}

function drawstart(x0, y0, width, height){
    paper.path("M "+x0+","+y0+"L"+(x0+width)+","+y0+"").attr({stroke: initialcolor});
    paper.path("M "+x0+","+(y0+height)+"L"+(x0+width)+","+y0+height+"").attr({stroke:  initialcolor});
    paper.path("M "+x0+","+y0+"L"+x0+","+(y0+height)+"").attr({stroke: initialcolor});
}

    drawstart(x0,y0,100,height);
    for (var i in losses){
      drawloss(start,level,losses[i]*heightunit);
      remaining-=losses[i]*heightunit;
      level-=losses[i]*heightunit;
      start+=100;
    }
    drawremaining(start, level, remaining);
}
</script>
</head>
<body id="blog">
    <div id="notepad" style="height:1000px; width:1000px; background: #eee"></div>
    <script type="text/javascript">
    var paper = Raphael(document.getElementById("notepad"), 1020, 1000);
    var losses=[50, 30, 5];
    Sankey(10, 100, 200, losses);
    </script>
</body>
</html>
Abettor answered 30/12, 2010 at 12:20 Comment(2)
That's great! Now do you have any idea about how the created diagrams might be exported to an image format that might be saved by the user?Overtop
Hmm, that is beyond me for the moment. At the moment I'm thinking of the idea of a jquery plugin that acts on a html table of data where you input the table id, the data column and the title column to the plugin. The dream is that it generates the sankey and also extends the table to allow you to dynamically reorder the different values, set the angle/length of the arrow and a million other things.Liebig
M
1

Update 2020:

For anyone struggling to bring D3 Sankey examples to life, I found this supereasy video tutorial. Worked like a charm for me :)

https://reactviz.holiday/sankey/

Also, in case you can't make this one work either, react-google-charts have a pretty nice looking alternative which couldn't be easier to work with (at least implementing the example was just copy-pasting the whole component from here https://react-google-charts.com/sankey-diagram):

import Chart from "react-google-charts";


<Chart
  width={600}
  height={'300px'}
  chartType="Sankey"
  loader={<div>Loading Chart</div>}
  data={[
    ['From', 'To', 'Weight'],
    ['A', 'X', 5],
    ['A', 'Y', 7],
    ['A', 'Z', 6],
    ['B', 'X', 2],
    ['B', 'Y', 9],
    ['B', 'Z', 4],
  ]}
  rootProps={{ 'data-testid': '1' }}
/>
Midship answered 21/6, 2020 at 12:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.