UNPKG

svg-sankey

Version:

Create SVG Sankey diagrams from the command line

193 lines (145 loc) 6.85 kB
'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _commander = require('commander'); var _commander2 = _interopRequireDefault(_commander); var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs); var _jsdom = require('jsdom'); var _jsdom2 = _interopRequireDefault(_jsdom); var _xmlserializer = require('xmlserializer'); var _xmlserializer2 = _interopRequireDefault(_xmlserializer); var _d3Selection = require('d3-selection'); var _d3Scale = require('d3-scale'); var _d3Format = require('d3-format'); var _d3SankeyDiagram = require('d3-sankey-diagram'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // diagram _commander2.default.arguments('<file>').option('-s, --size <w>,<h>', 'width and height', parseSize).option('-m, --margins <n>[,...]', '1, 2 or 4 margin values', parseMargins).option('-p, --position <xattr>,<yattr>', 'node attributes to set positions (manual layout)', parseAttrs).option('-k, --scale <k>', 'scale (px/value)', Number).option('--font-size <s>', 'font-size (px/value)', Number).option('--node-values <fmt>', 'd3 format string to show node values', parseFormat).action(function (filename) { _fs2.default.readFile(filename, 'utf8', function (err, data) { if (err) throw err; // we'll not consider error handling for now var parsedData = JSON.parse(data); var svg = drawDiagram(parsedData); process.stdout.write(svg); }); }).parse(process.argv); function parseFormat(val) { return (0, _d3Format.format)(val); } function parseMargins(val) { val = val.split(',').map(function (x) { return x.trim(); }).map(Number); if (val.length === 1) { return { top: val[0], right: val[0], bottom: val[0], left: val[0] }; } else if (val.length === 2) { return { top: val[0], right: val[1], bottom: val[0], left: val[1] }; } else if (val.length === 4) { return { top: val[0], right: val[1], bottom: val[2], left: val[3] }; } else { throw new Error('Expected 1, 2 or 4 numbers'); } } function parseSize(val) { val = val.split(',').map(function (x) { return x.trim(); }).map(Number); if (val.length === 1) { return [val[0], val[0]]; } else if (val.length === 2) { return val; } else { throw new Error('Expected 1 or 2'); } } function parseAttrs(val) { val = val.split(',').map(function (x) { return x.trim(); }); if (val.length === 2) { return { xattr: val[0], yattr: val[1] }; } else { throw new Error('Expected 2 attribute names'); } } function alignLinkTypes(layout, align) { return layout.sourceId(function (d) { return { id: _typeof(d.source) === "object" ? d.source.id : d.source, port: align ? d.type : null }; }).targetId(function (d) { return { id: _typeof(d.target) === "object" ? d.target.id : d.target, port: align ? d.type : null }; }); } function nodeTitle(d) { return d.title !== undefined ? d.title.label !== undefined ? d.title.label : d.title : d.id; } function linkTypeTitle(d) { return d.title !== undefined ? d.title : d.type; } var color = (0, _d3Scale.scaleOrdinal)(_d3Scale.schemeCategory20); function linkColor(d) { return d.color !== undefined ? d.color : d.style !== undefined && d.style.color !== undefined ? d.style.color : color(d.type); } var fmt = (0, _d3Format.format)('.3s'); // const linkTitle = sankeyLinkTitle(nodeTitle, linkTypeTitle, fmt); function linkTitle(d) { var parts = []; var sourceTitle = nodeTitle(d.source); var targetTitle = nodeTitle(d.target); var matTitle = linkTypeTitle(d); parts.push(sourceTitle + ' \u2192 ' + targetTitle); if (matTitle) parts.push(matTitle); parts.push(fmt(d.data.value)); return parts.join('\n'); } function drawDiagram(data) { var width = _commander2.default.size ? _commander2.default.size[0] : 800, height = _commander2.default.size ? _commander2.default.size[1] : 600; var color = (0, _d3Scale.scaleOrdinal)(_d3Scale.schemeCategory20); var margins = _commander2.default.margins || { top: 0, bottom: 0, left: 0, right: 0 }; var ordering = data.metadata && data.metadata.layers ? data.metadata.layers : data.order && data.order.length ? data.order : null; var layout = (0, _d3SankeyDiagram.sankey)().linkValue(function (d) { return d.data.value; }).size([width - margins.left - margins.right, height - margins.top - margins.bottom]).ordering(ordering).rankSets(data.rankSets); if (_commander2.default.position) { layout.nodePosition(function (d) { return [d[_commander2.default.position.xattr], d[_commander2.default.position.yattr]]; }); } if (_commander2.default.scale) { layout.scale(_commander2.default.scale); } var diagram = (0, _d3SankeyDiagram.sankeyDiagram)().nodeTitle(nodeTitle).nodeValue(_commander2.default.nodeValues ? function (d) { return _commander2.default.nodeValues(d.value); } : function (d) { return ''; }).linkTitle(linkTitle).linkColor(linkColor).linkMinWidth(function (d) { return 0.1; }).margins(margins).groups(data.groups || []); var document = _jsdom2.default.jsdom(); var el = (0, _d3Selection.select)(document).select('body').append('svg'); el.datum(layout(data)).call(diagram); // put default styles inline el.attr('width', width).attr('height', height).attr('viewBox', '0 0 ' + width + ' ' + height).style('font-size', _commander2.default.fontSize || null).style('font-family', 'Helvetica, Arial, sans-serif'); el.selectAll('.link').style('opacity', 0.8); el.selectAll('line').style('stroke', function (d) { return d.style === 'process' ? '#888' : '#000'; }).style('stroke-width', function (d) { return d.style === 'process' ? '4px' : '1px'; }); el.selectAll('rect').style('fill', 'none'); el.selectAll('.group').select('rect').style('fill', '#eee').style('stroke', '#bbb').style('stroke-width', '0.5'); el.selectAll('.group').select('text').style('fill', '#999'); // add background el.insert('rect', ':first-child').attr('width', width).attr('height', height).style('fill', 'white'); // add title if (data.metadata && data.metadata.title !== undefined) { el.append('text').attr('x', width - 30).attr('y', 30).style('font-size', '200%').style('text-anchor', 'end').text(data.metadata.title); } // create a file blob of our SVG. var svg = serialize(el.node()); return svg; } var serialize = function serialize(node) { return '<?xml version="1.0" standalone="no"?>' + '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' + _xmlserializer2.default.serializeToString(node); };