all files / src/visualizations/ chord.jsx

11.9% Statements 5/42
0% Branches 0/2
0% Functions 0/14
12.82% Lines 5/39
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102                                                                                                                                                                                                 
/* eslint-disable no-param-reassign */
import d3 from 'd3';
import { getColorFromScheme } from '../modules/colors';
import './chord.css';
 
function chordViz(slice, json) {
  slice.container.html('');
 
  const div = d3.select(slice.selector);
  const nodes = json.data.nodes;
  const fd = slice.formData;
  const f = d3.format(fd.y_axis_format);
 
  const width = slice.width();
  const height = slice.height();
 
  const outerRadius = Math.min(width, height) / 2 - 10;
  const innerRadius = outerRadius - 24;
 
  let chord;
 
  const arc = d3.svg.arc()
  .innerRadius(innerRadius)
  .outerRadius(outerRadius);
 
  const layout = d3.layout.chord()
  .padding(0.04)
  .sortSubgroups(d3.descending)
  .sortChords(d3.descending);
 
  const path = d3.svg.chord()
  .radius(innerRadius);
 
  const svg = div.append('svg')
  .attr('width', width)
  .attr('height', height)
  .on('mouseout', () => chord.classed('fade', false))
  .append('g')
  .attr('id', 'circle')
  .attr('transform', `translate(${width / 2}, ${height / 2})`);
 
  svg.append('circle')
  .attr('r', outerRadius);
 
  // Compute the chord layout.
  layout.matrix(json.data.matrix);
 
  const group = svg.selectAll('.group')
  .data(layout.groups)
  .enter().append('g')
  .attr('class', 'group')
  .on('mouseover', (d, i) => {
    chord.classed('fade', p => p.source.index !== i && p.target.index !== i);
  });
 
  // Add a mouseover title.
  group.append('title').text((d, i) => `${nodes[i]}: ${f(d.value)}`);
 
  // Add the group arc.
  const groupPath = group.append('path')
  .attr('id', (d, i) => 'group' + i)
  .attr('d', arc)
  .style('fill', (d, i) => getColorFromScheme(nodes[i], slice.formData.color_scheme));
 
  // Add a text label.
  const groupText = group.append('text')
  .attr('x', 6)
  .attr('dy', 15);
 
  groupText.append('textPath')
  .attr('xlink:href', (d, i) => `#group${i}`)
  .text((d, i) => nodes[i]);
  // Remove the labels that don't fit. :(
  groupText.filter(function (d, i) {
    return groupPath[0][i].getTotalLength() / 2 - 16 < this.getComputedTextLength();
  })
  .remove();
 
  // Add the chords.
  chord = svg.selectAll('.chord')
  .data(layout.chords)
  .enter().append('path')
  .attr('class', 'chord')
  .on('mouseover', (d) => {
    chord.classed('fade', p => p !== d);
  })
  .style('fill', d => getColorFromScheme(nodes[d.source.index], slice.formData.color_scheme))
  .attr('d', path);
 
  // Add an elaborate mouseover title for each chord.
  chord.append('title').text(function (d) {
    return nodes[d.source.index]
    + ' → ' + nodes[d.target.index]
    + ': ' + f(d.source.value)
    + '\n' + nodes[d.target.index]
    + ' → ' + nodes[d.source.index]
    + ': ' + f(d.target.value);
  });
}
 
module.exports = chordViz;