d3.run_sankey = function(){ console.log("f**k yeah"); const f1 = "sankeygreenhouse.json"; const f2 = "test.json"; const jsonFile = "../json/" + f1; var units = "Widgets";///why is this the unit of the formatNumber result? var margin = {top: 10, right: 10, bottom: 10, left: 10}, width = 800 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var formatNumber = d3.format(",.0f"), // zero decimal places format = function(d) { return formatNumber(d) + " " + units; }, color = d3.scale.category20(); // append the svg canvas to the page var svg = d3.select("#chart").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // Set the sankey diagram properties var sankey = d3.sankey() .nodeWidth(36) .nodePadding(10) .size([width, height]); var path = sankey.link(); // load the data d3.json(jsonFile, function(error, graph) { var nodeMap = {}; graph.nodes.forEach(function(x) { nodeMap[x.name] = x; }); graph.links = graph.links.map(function(x) { return { source: nodeMap[x.source], target: nodeMap[x.target], value: x.value }; }); sankey .nodes(graph.nodes) .links(graph.links) .layout(32); // add in the links var link = svg.append("g").selectAll(".link") .data(graph.links) .enter().append("path") .attr("class", "link") .attr("d", path) .style("stroke-width", function(d) { return Math.max(1, d.dy); }) .sort(function(a, b) { return b.dy - a.dy; }); // add the link titles link.append("title") .text(function(d) { return d.source.name + " → " + d.target.name + "\n" + format(d.value); }); // add in the nodes var node = svg.append("g").selectAll(".node") .data(graph.nodes) .enter().append("g") .attr("class", "node") .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) .call(d3.behavior.drag() .origin(function(d) { return d; }) .on("dragstart", function() { this.parentNode.appendChild(this); }) .on("drag", dragmove)); // add the rectangles for the nodes node.append("rect") .attr("height", function(d) { return d.dy; }) .attr("width", sankey.nodeWidth()) .style("fill", function(d) { return d.color = color(d.name.replace(/ .*/, "")); }) .style("stroke", function(d) { return d3.rgb(d.color).darker(2); }) .append("title") .text(function(d) { return d.name + "\n" + format(d.value); }); // add in the title for the nodes node.append("text") .attr("x", -6) .attr("y", function(d) { return d.dy / 2; }) .attr("dy", ".35em") .attr("text-anchor", "end") .attr("transform", null) .text(function(d) { return d.name; }) .filter(function(d) { return d.x < width / 2; }) .attr("x", 6 + sankey.nodeWidth()) .attr("text-anchor", "start"); // the function for moving the nodes function dragmove(d) { d3.select(this).attr("transform", "translate(" + ( d.x = Math.max(0, Math.min(width - d.dx, d3.event.x)) ) + "," + ( d.y = Math.max(0, Math.min(height - d.dy, d3.event.y)) ) + ")"); sankey.relayout(); link.attr("d", path); } }); }
function sankeyVis(slice, payload) { const div = d3.select(slice.selector); const margin = { top: 5, right: 5, bottom: 5, left: 5, }; const width = slice.width() - margin.left - margin.right; const height = slice.height() - margin.top - margin.bottom; const formatNumber = d3.format(',.2f'); div.selectAll('*').remove(); const svg = div.append('svg') .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom) .append('g') .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); const tooltip = div.append('div') .attr('class', 'sankey-tooltip') .style('opacity', 0); const sankey = d3.sankey() .nodeWidth(15) .nodePadding(10) .size([width, height]); const path = sankey.link(); let nodes = {}; // Compute the distinct nodes from the links. const links = payload.data.map(function (row) { const link = Object.assign({}, row); link.source = nodes[link.source] || (nodes[link.source] = { name: link.source }); link.target = nodes[link.target] || (nodes[link.target] = { name: link.target }); link.value = Number(link.value); return link; }); nodes = d3.values(nodes); sankey .nodes(nodes) .links(links) .layout(32); function getTooltipHtml(d) { let html; if (d.sourceLinks) { // is node html = d.name + " Value: <span class='emph'>" + formatNumber(d.value) + '</span>'; } else { const val = formatNumber(d.value); const sourcePercent = d3.round((d.value / d.source.value) * 100, 1); const targetPercent = d3.round((d.value / d.target.value) * 100, 1); html = [ "<div class=''>Path Value: <span class='emph'>", val, '</span></div>', "<div class='percents'>", "<span class='emph'>", (isFinite(sourcePercent) ? sourcePercent : '100'), '%</span> of ', d.source.name, '<br/>', "<span class='emph'>" + (isFinite(targetPercent) ? targetPercent : '--') + '%</span> of ', d.target.name, 'target', '</div>', ].join(''); } return html; } function onmouseover(d) { tooltip .html(function () { return getTooltipHtml(d); }) .transition() .duration(200) .style('left', (d3.event.offsetX + 10) + 'px') .style('top', (d3.event.offsetY + 10) + 'px') .style('opacity', 0.95); } function onmouseout() { tooltip.transition() .duration(100) .style('opacity', 0); } const link = svg.append('g').selectAll('.link') .data(links) .enter() .append('path') .attr('class', 'link') .attr('d', path) .style('stroke-width', d => Math.max(1, d.dy)) .sort((a, b) => b.dy - a.dy) .on('mouseover', onmouseover) .on('mouseout', onmouseout); function dragmove(d) { d3.select(this) .attr( 'transform', `translate(${d.x},${(d.y = Math.max(0, Math.min(height - d.dy, d3.event.y)))})`, ); sankey.relayout(); link.attr('d', path); } const node = svg.append('g').selectAll('.node') .data(nodes) .enter() .append('g') .attr('class', 'node') .attr('transform', function (d) { return 'translate(' + d.x + ',' + d.y + ')'; }) .call(d3.behavior.drag() .origin(function (d) { return d; }) .on('dragstart', function () { this.parentNode.appendChild(this); }) .on('drag', dragmove), ); const minRectHeight = 5; node.append('rect') .attr('height', d => d.dy > minRectHeight ? d.dy : minRectHeight) .attr('width', sankey.nodeWidth()) .style('fill', function (d) { const name = d.name || 'N/A'; d.color = getColorFromScheme(name.replace(/ .*/, ''), slice.formData.color_scheme); return d.color; }) .style('stroke', function (d) { return d3.rgb(d.color).darker(2); }) .on('mouseover', onmouseover) .on('mouseout', onmouseout); node.append('text') .attr('x', -6) .attr('y', function (d) { return d.dy / 2; }) .attr('dy', '.35em') .attr('text-anchor', 'end') .attr('transform', null) .text(function (d) { return d.name; }) .filter(function (d) { return d.x < width / 2; }) .attr('x', 6 + sankey.nodeWidth()) .attr('text-anchor', 'start'); }
.then((data) => { size.height = data.cells.length * unit; svg.attr('height', size.height + margin.top + margin.bottom); svg.attr('width', size.width); var graph = { nodes: [], links: [] }; var objs = {}; var targetScale = d3.scale.ordinal().range(['#ddd', '#ccc', '#eee', '#bbb']); var currency = data.currency[params.aggregates]; var valueFormat = that.getValueFormatter(currency); _.each(data.cells, (cell) => { var source = _.find(cell.dimensions, {keyField: sourceKey}); var target = _.find(cell.dimensions, {keyField: targetKey}); var measure = _.find(cell.measures, {key: params.aggregates}); var sourceId = source.keyValue; var targetId = target.keyValue; var link = { value: measure.value, number: valueFormat(measure.value), isLink: true }; if (link.value == 0 || !sourceId || !targetId) { return; } sourceId = 'source-' + sourceKey + sourceId; targetId = 'target-' + targetKey + targetId; if (!objs[sourceId]) { graph.nodes.push({ key: source.keyValue, name: source.nameValue, color: colorScale(sourceId), isSource: true }); objs[sourceId] = {idx: graph.nodes.length - 1}; } link.source = objs[sourceId].idx; if (!objs[targetId]) { graph.nodes.push({ key: target.keyValue, name: target.nameValue, color: targetScale(targetId), isTarget: true }); objs[targetId] = { idx: graph.nodes.length - 1 }; } link.target = objs[targetId].idx; graph.links.push(link); }); that.sankey = d3.sankey() .nodeWidth(unit) .nodePadding(unit * 0.6) .size([size.width, size.height]); var sankey = that.sankey; var path = sankey.link(); sankey .nodes(graph.nodes) .links(graph.links) .layout(32); group.selectAll('g').remove(); var link = group.append('g').selectAll('.link') .data(graph.links) .enter().append('path') .attr('class', 'link') .attr('d', path) .style('stroke-width', function(d) { return Math.max(1, d.dy); }) .style('stroke', function(d) { return d.source.color; }) .sort(function(a, b) { return b.dy - a.dy; }) .on('click', (d) => { that.emit('click', that, d); }); link.append('title') .text(function(d) { return d.source.name + ' → ' + d.target.name + '\n' + d.number; }); var node = group.append('g').selectAll('.node') .data(graph.nodes) .enter().append('g') .attr('class', 'node') .attr('transform', function(d) { return 'translate(' + d.x + ',' + d.y + ')'; }) .on('click', (d) => { that.emit('click', that, d); }); node.append('rect') .attr('height', function(d) { return d.dy; }) .attr('width', sankey.nodeWidth()) .style('fill', function(d) { return d.color; }) .style('stroke', function(d) { return d.color; }) .append('title') .text(function(d) { return d.name + '\n' + valueFormat(d.value); }); // Add values inside nodes node.filter(function(d) { // Ignore small nodes var estimatedTextWidth = (valueFormat(d.value).length * that.CHAR_WIDTH); return d.dy > estimatedTextWidth; }) .append('text') .attr('class', 'nodeValue') .text(function (d) { return valueFormat(d.value); }) .attr('text-anchor', 'middle') .attr('transform', function (d) { return 'rotate(-90) translate(' + (-d.dy / 2) + ', ' + (sankey.nodeWidth() / 2 + 5) + ')'; }); // Add labels inside links node.append('text') .attr('x', -6) .attr('y', function(d) { return d.dy / 2; }) .attr('dy', '.35em') .attr('text-anchor', 'end') .attr('transform', null) .text(function(d) { return d.name; }) .filter(function(d) { return d.x < size.width / 2; }) .attr('x', 6 + sankey.nodeWidth()) .attr('text-anchor', 'start'); that.emit('loaded', that, data); that.emit('ready', that, data, null); })
render() { // ======================================================================== // Set units, margin, sizes // ======================================================================== var margin = { top: 10, right: 0, bottom: 10, left: 0 }; var width = 690 - margin.left - margin.right; var height = 400 - margin.top - margin.bottom; var format = (d) => formatNumber(d); var formatNumber = d3.format(",.0f"); // zero decimal places // ======================================================================== // Set the sankey diagram properties // ======================================================================== var sankey = d3.sankey() .size([width, height]) .nodeWidth(15) .nodePadding(10); var path = sankey.link(); var graph = { nodes: _.cloneDeep(this.state.nodes), links: _.cloneDeep(this.state.links) }; sankey.nodes(graph.nodes) .links(graph.links) .layout(32); // ======================================================================== // Initialize and append the svg canvas to faux-DOM // ======================================================================== var svgNode = ReactFauxDOM.createElement('div'); var svg = d3.select(svgNode).append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // ======================================================================== // Add links // ======================================================================== var link = svg.append("g").selectAll(".link") .data(graph.links) .enter().append("path") .attr("class", "link") .on('click', this.props.openModal) // register eventListener .attr("d", path) .style("stroke-width", (d) => Math.max(1, d.dy)) // add link titles link.append("title") .text((d) => d.source.name + " → " + d.target.name + "\n Weight: " + format(d.value)); // ======================================================================== // Add nodes // ======================================================================== var node = svg.append("g").selectAll(".node") .data(graph.nodes) .enter().append("g") .attr("class", "node") .on('click', this.props.openModal) // register eventListener .attr("transform", (d) => "translate(" + d.x + "," + d.y + ")") // add nodes rect node.append("rect") .attr("height", (d) => d.dy) .attr("width", sankey.nodeWidth()) .append("title") .text((d) => d.name + "\n" + format(d.value)); // add nodes text node.append("text") .attr("x", -6) .attr("y", (d) => d.dy / 2) .attr("dy", ".35em") .attr("text-anchor", "end") .text((d) => d.name) .filter((d) => d.x < width / 2) .attr("x", 6 + sankey.nodeWidth()) .attr("text-anchor", "start"); // Above D3 manipaluation equal to following jsx if didn't rely on faux-dom // ------------------------------------------------------------------------ // var links = graph.links.map((link, i) => { // return ( // <g> // <path key={i} className="link" onClick={()=>{this.props.openModal(link)}} d={path(link)} style={{strokeWidth: Math.max(1, link.dy)}}> // <title>{link.source.name + " → " + link.target.name + "\n Weight: " + format(link.value)}</title> // </path> // </g> // ); // }); // var nodes = graph.nodes.map((node, i) => { // return ( // <g key={i} className="node" onClick={()=>{this.props.openModal(node)}} transform={"translate(" + node.x + "," + node.y + ")"}> // <rect height={node.dy} width={sankey.nodeWidth()}> // <title>{node.name + "\n" + format(node.value)}</title> // </rect> // { (node.x >= width / 2) ? // <text x={-6} y={node.dy / 2} dy={".35em"} textAnchor={"end"} >{node.name}</text> : // <text x={6 + sankey.nodeWidth()} y={node.dy / 2} dy={".35em"} textAnchor={"start"} >{node.name}</text> // } // </g> // ); // }); // ======================================================================== // Render the faux-DOM to React elements // ======================================================================== return svgNode.toReact(); // JSX rendering return if didn't rely on faux-dom // ------------------------------------------------------------------------ // return ( // <svg width={width + margin.left + margin.right} height={height + margin.top + margin.bottom}> // <g transform={"translate(" + margin.left + "," + margin.top + ")"}> // {links} // {nodes} // </g> // </svg> // ); }
function drawSankey(data){ d3.select('#sankey svg').remove(); var parentSize = d3.select('#sankey').node().getBoundingClientRect(); var height = 540; var width = parentSize.width; var margin = {top:30,left:0,bottom:40,right:0}; var chartHeight = height - (margin.top + margin.bottom); var chartWidth = width - (margin.left + margin.right); var nodePadding = 20; var sankey = d3.sankey() .size([chartWidth, chartHeight]) .nodeWidth(nodeWidth) .nodePadding(nodePadding) .nodes(data.nodes) .links(data.links) .layout(32); var path = sankey.link(); var svg = d3.select('#sankey').append('svg') .attr({ width:width, height:height, class: 'sankey' }); var gradients = svg.append('defs').selectAll('linearGradient').data(data.links) .enter() .append('linearGradient') .attr({ id:gradientName, x1:'0%', x2:'100%', y1:'0%', y2:'0%' }); gradients.append('stop') .attr({ offset:'0%' }) .style('stop-color',function(d){ return partyColour(d.source.name); }) .style('stop-opacity',1); gradients.append('stop') .attr({ offset:'100%' }) .style('stop-color',function(d){ return partyColour(d.target.name); }) .style('stop-opacity',1); svg.selectAll('text.axis-label').data(['Current','Predicted']) .enter() .append('text') .attr({ 'class':'axis-label', 'text-anchor':function(d,i){ if(i===0) return 'start'; return 'end'; }, 'transform':function(d,i){ var xPos = (width-margin.right)*i; var yPos = margin.top - 8; return 'translate('+xPos+','+yPos+')'; } }).text(function(d){ return d; }); svg = svg.append('g') .attr('transform','translate('+margin.left+','+margin.top+')'); var link = svg.append('g').selectAll('.link') .data(data.links) .enter() .append('g') .attr('class','link-container'); link.append('path') .attr({ 'class':function(d){ return 'link ' + linkClass(d); }, 'data-from':function(d){ return toClass(d.source.name); }, 'data-to':function(d){ return toClass(d.target.name); }, 'd':path }) .style('stroke-width', function(d) { return Math.max(1, d.dy); }); //start value of each link link.append('text') .attr({ 'x':nodeWidth +6, 'y':function(d) { return d.source.y + d.sy + (d.dy/2); }, 'data-from':function(d){ return toClass(d.source.name); }, 'data-to':function(d){ return toClass(d.target.name); }, 'class':'source link-label inactive', 'dy':'.35em' }) .text(function(d){ return partyShortName(d.source.name) +': '+ d.value; }); //end value of each link link.append('text') .attr({ 'x':chartWidth - (nodeWidth +6), 'y':function(d) { return d.target.y + d.ty + (d.dy/2); }, 'data-from':function(d){ return toClass(d.source.name); }, 'data-to':function(d){ return toClass(d.target.name); }, 'dy':'.35em', 'class':'target link-label inactive', 'text-anchor':'end' }) .text(function(d){ return partyShortName(d.target.name) +': '+ d.value; }); var node = svg.append('g').selectAll('.node') .data(data.nodes) .enter() .append('g') .attr('class', 'node') .attr('transform', function(d) { return 'translate(' + d.x + ',' + d.y + ')'; }); node.append('rect') .attr('height', function(d) { return Math.max(1, d.dy); }) .attr('width', sankey.nodeWidth()) .attr('class',function(d){ return 'node ' + toClass(d.name); }) .append('title') .text(function(d) { return d.name + " " + d.value; }); node.append('rect') .attr('height', function(d) { return d.dy + nodePadding; }) .attr('y', function(d) { return -nodePadding/2; }) .attr('x', function(d){ if(d.x>chartWidth/2) return -chartWidth/2 + sankey.nodeWidth(); return 0; }) .attr('width', chartWidth/2) .attr('class','selection-rect') .append('title') .text(function(d) { return d.name + " " + d.value; }); node.append('text') .attr({ 'x':-6, 'y':function(d) { return d.dy / 2; }, 'dy':'.35em', 'text-anchor':'end', 'transform':null, 'class':'target node-label', 'data-party':function(d){ return toClass(d.name); } }) .text(function(d) { return partyShortName(d.name) + ': '+d.value ; }) //TODO .filter(function(d) { return d.x < width / 2; }) .attr({ 'x':6 + sankey.nodeWidth(), 'class':'source node-label', 'text-anchor':'start' }); d3.selectAll('.node') .on('click',function(d){ clearSelections(); var selectionList = buildSelectionList(d); selectLink( selectionList.list.join(', ') ); activateLabels(selectionList.direction, selectionList.party); }) .on('mouseover',function(d){ clearHint(); pathHint( buildSelectionList(d).list.join(', ') ); }) .on('mouseout', function(){ clearHint(); }); d3.selectAll('path') .on('click',function(d){ clearSelections(); selectLink( '.'+linkClass(d) ); activateLabels('both', d.source.name, d.target.name); }); var logoGroup = svg.append('g'); logoGroup.call(logo); logoGroup.attr('transform','translate(' + (width-32) + ',' + (height - 52) + ')'); var sourceGroup = svg.append('g'); sourceGroup.attr('transform','translate(0,' + (height-34) + ')'); sourceGroup.append('text').attr({ 'id':'svg-source', }).text('Source: electionforecast.co.uk, ' + updateString); }
var _buildVis = function (data) { var energy = data.slices; div = d3.select(svgRoot); if (!energy.nodes.length) return; console.log($scope.vis); svg = div.append('svg') .attr('width', width) .attr('height', height + margin) .append('g') .attr('transform', 'translate(0, 0)'); var sankey = d3.sankey() .nodeWidth(15) .nodePadding(10) .size([width, height]); var path = sankey.link(); sankey .nodes(energy.nodes) .links(energy.links) .layout(32); var link = svg.append('g').selectAll('.link') .data(energy.links) .enter().append('path') .attr('class', 'link') .attr('d', path) .style('stroke-width', function (d) { return Math.max(1, d.dy); }) .sort(function (a, b) { return b.dy - a.dy; }); link.append('title') .text(function (d) { return d.source.name + ' → ' + d.target.name + '\n' + format(d.value); }); var node = svg.append('g').selectAll('.node') .data(energy.nodes) .enter().append('g') .attr('class', 'node') .attr('transform', function (d) { return 'translate(' + d.x + ',' + d.y + ')'; }) .call(d3.behavior.drag() .origin(function (d) { return d; }) .on('dragstart', function () { this.parentNode.appendChild(this); }) .on('drag', dragmove)); node.append('rect') .attr('height', function (d) { return d.dy; }) .attr('width', sankey.nodeWidth()) .style('fill', function (d) { return d.color = color(d.name); }) .style('stroke', function (d) { return d3.rgb(d.color).darker(2); }) .append('title') .text(function (d) { return d.name + '\n' + format(d.value); }); node.append('text') .attr('x', -6) .attr('y', function (d) { return d.dy / 2; }) .attr('dy', '.35em') .attr('text-anchor', 'end') .attr('transform', null) .text(function (d) { return d.name; }) .filter(function (d) { return d.x < width / 2; }) .attr('x', 6 + sankey.nodeWidth()) .attr('text-anchor', 'start'); function dragmove(d) { d3.select(svgRoot).attr('transform', 'translate(' + d.x + ',' + (d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))) + ')'); sankey.relayout(); link.attr('d', path); } };