var CircularNetwork = function module() { var opts = { width: 200, height: 200, margins: {top:30, right:30, bottom:30, left:30}, fillColorList: [ "#3182bd", "#6baed6", "#9ecae1", "#c6dbef", "#e6550d", "#fd8d3c", "#fdae6b", "#fdd0a2", "#31a354", "#74c476", "#a1d99b", "#c7e9c0", "#756bb1", "#9e9ac8", "#bcbddc", "#dadaeb", "#636363", "#969696", "#bdbdbd", "#d9d9d9" ], enableTooltips: true, enableBringToFront: true, labelOffset: 5 }; function exports(_selection) { _selection.each(function (_dataset) { //________________________________________________ // Data //________________________________________________ var dataset = d3.transpose(_dataset); var start = dataset[0]; var end = dataset[1]; var weight = (dataset[2] == undefined) ? start.map(function(d, i){return 1;}) : dataset[2].map(function(d){return parseInt(d);}); //________________________________________________ // Data transform //________________________________________________ var transformedData = d3.transpose([start, end, weight]); // list of unique values to use as index var unique = []; transformedData.forEach(function(d, i){ if(unique.indexOf(d[0]) == -1) unique.push(d[0]); if(unique.indexOf(d[1]) == -1) unique.push(d[1]); }); // init square matrix var matrix = d3.range(unique.length).map(function(){ return d3.range(unique.length).map(function(){return 0;}); }); // compute matrix transformedData.forEach(function(d, i){ var row = unique.indexOf(d[1]); var col = unique.indexOf(d[0]); matrix[col][row] = d[2]; }); //________________________________________________ // DOM selection //________________________________________________ var chartW = Math.max(opts.width - opts.margins.left - opts.margins.right, 0.1); var chartH = Math.max(opts.height - opts.margins.top - opts.margins.bottom, 0.1); var svg = d3.select(this).selectAll("svg").data([0]); svg.enter().append("svg").attr({width: opts.width, height: opts.height}) .append("g").attr({class: "vis-group", transform: "translate(" + (opts.margins.left + chartW/2) + "," + (opts.margins.top + chartH/2) + ")"}); var chartSVG = d3.select("g.vis-group"); //________________________________________________ // Circular network //________________________________________________ var fill = function(i) { return opts.fillColorList[i % opts.fillColorList.length]; } var chord = d3.layout.chord().padding(.05).matrix(matrix); var r1 = Math.min(chartW, chartH) / 2; var r0 = r1 * 0.9; // draw arcs var groupSVG = chartSVG.selectAll("path.arc") .data(chord.groups); groupSVG.enter().append("path") .attr("class", "arc") groupSVG.attr("d", d3.svg.arc() .innerRadius(r0) .outerRadius(r1)) .style("fill", function(d){ return fill(d.index);}); groupSVG.exit().remove(); // draw chords var chordSVG = chartSVG.selectAll("path.link") .data(chord.chords); chordSVG.enter() .append("path") .attr("class", "link") .call(highlight); chordSVG.attr("d", d3.svg.chord().radius(r0)) .style("fill",function (d) {return fill(d.target.index);}); chordSVG.exit().remove(); if(opts.enableBringToFront) chordSVG.call(bringToFront); function highlight(_selection){ _selection .on("mouseover.highlight", function(d, i) { d3.select(this).classed({highlighted: true}); }) .on("mouseout.highlight", function(d, i){ d3.select(this).classed({highlighted: false}); }); } function bringToFront(_selection){ _selection.on("mouseover.toFront", function() { var dragTarget = event.target; dragTarget.parentNode.appendChild( dragTarget ); }); } //________________________________________________ // Labels //________________________________________________ var labelSVG = chartSVG.selectAll("text.label") .data(chord.groups); labelSVG.enter().append("text") .attr("class", "label"); labelSVG.each(function(d){ d.angle = (d.startAngle + d.endAngle) / 2; }) .attr("text-anchor", function(d){ return d.angle > Math.PI ? "end" : null; }) .attr("transform", function(d){ return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")" + "translate(" + (r1 + opts.labelOffset) + ")" + (d.angle > Math.PI ? "rotate(180)" : ""); }) .text(function(d, i){ return unique[i];}); labelSVG.exit().remove(); //________________________________________________ // Tooltips //________________________________________________ if(opts.enableTooltips){ var groupTooltip = tooltip() .accessor(function(d, i){ return "start: " + unique[i]; }); var chordTooltip = tooltip() .accessor(function(d, i){ var tipText = "start: " + unique[d.source.index] + "
" + "weight: " + d.source.value + "
" + "to
" + "end: " + unique[d.target.index] + "
" + "weight: " + d.target.value; return tipText; }); groupSVG.call(groupTooltip); chordSVG.call(chordTooltip); } }); } exports.opts = opts; createAccessors(exports); return exports; }; var tooltip = function module(){ var opts = { accessor: function(d, i){return d;} }; function exports(selection){ var tooltipDiv; var body = d3.select("body"); selection.on("mouseover.tooltip", function(d, i){ d3.select("body").selectAll("div.tooltip").remove(); tooltipDiv = body.append("div").attr("class", "tooltip"); var absoluteMousePos = d3.mouse(body.node()); tooltipDiv.style("left", (absoluteMousePos[0] + 10)+"px") .style("top", (absoluteMousePos[1] - 15)+"px") .style("position", "absolute") .style("z-index", 1001); var tooltipText = opts.accessor(d, i) || ""; tooltipDiv.html(tooltipText); }) .on("mousemove.tooltip", function(d, i) { var absoluteMousePos = d3.mouse(body.node()); tooltipDiv.style("left", (absoluteMousePos[0] + 10)+"px") .style("top", (absoluteMousePos[1] - 15)+"px"); }) .on("mouseout.tooltip", function(d, i){ tooltipDiv.remove(); }); }; exports.opts = opts; createAccessors(exports); return exports; }; function createAccessors(visExport) { for (var n in visExport.opts) { if (!visExport.opts.hasOwnProperty(n)) continue; visExport[n] = (function(n) { return function(v) { return arguments.length ? (visExport.opts[n] = v, this) : visExport.opts[n]; } })(n); } };