svg-sankey
Version:
Create SVG Sankey diagrams from the command line
193 lines (145 loc) • 6.85 kB
JavaScript
;
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);
};