// Original idea source below. // [Miserables](https://bost.ocks.org/mike/miserables/) // load data set d3.json('zadjacency_dataset.json', function(error, raw) { var dataset = new Dataset(raw); // boundaries and scale // adjacency matrix is assumed a square var margin = { top: 80, right: 0, bottom: 10, left: 80 }; var width = 750; var height = 750; var xScale = d3.scale.ordinal().rangeBands([0, width]); var nel = dataset.nodes.length; var sortOrders = { name: d3.range(nel).sort(function(a, b) { return d3.ascending(dataset.nodes[a].name, dataset.nodes[b].name); }) }; xScale.domain(sortOrders.name); // draw adjacency matrix var svg = d3.select('#matrix1') .append('svg') .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom) .style('margin-left', -margin.left + 'px') .append('g') .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); svg.append('rect') .attr('class', 'background') .attr('width', width) .attr('height', height) .attr('style', 'fill: #eee;'); var rows = svg.selectAll('.row') .data(dataset.matrix) .enter() .append('g') .attr('transform', function(d, i) { return 'translate(0,' + xScale(i) + ')'; }) .each(drawRow); rows.append('line') .attr('x2', width) .style('stroke', '#fff'); rows.append("text") .attr("x", -6) .attr("y", xScale.rangeBand() / 2) .attr("dy", ".32em") .attr("text-anchor", "end") .text(function(d, i) { return dataset.nodes[i].name; }); var columns = svg.selectAll(".column") .data(dataset.matrix) .enter() .append("g") .attr("class", "column") .attr("transform", function(d, i) { return "translate(" + xScale(i) + ")rotate(-90)"; }); columns.append("line") .attr("x1", -width) .style('stroke', '#fff'); columns.append("text") .attr("x", 6) .attr("y", xScale.rangeBand() / 2) .attr("dy", ".32em") .attr("text-anchor", "start") .text(function(d, i) { return dataset.nodes[i].name; }); function drawRow(row) { // select "this group" which has no .cell var cell = d3.select(this) .selectAll('.cell') // retain only adjacent (connected) states // states with a distance >= 1 .data(row.filter(function(d) { return d.z != Number.MAX_VALUE; })) .enter() .append('rect') .attr('class', 'cell') // we can use 0 through nel - 1 for positioning // thanks to d3 scales .attr('x', function(d) { return xScale(d.x); }) .attr('width', xScale.rangeBand()) .attr('height', xScale.rangeBand()) .style('fill', function(d) { if (d.z > 0) { return 'red'; } else { return 'blue'; } }) .style('stroke', 'none'); } }); // ----------------------------------------------------------------------------- // the same plot using shortest path data d3.json('zadjacency_dataset.json', function(error, raw) { var dataset = new Dataset(raw); var shortest = dataset.computeShortestPaths( dataset.matrix ); // boundaries and scale // adjacency matrix is assumed a square var margin = { top: 80, right: 0, bottom: 10, left: 80 }; var width = 750; var height = 750; var xScale = d3.scale.ordinal().rangeBands([0, width]); var nel = dataset.nodes.length; var sortOrders = { name: d3.range(nel).sort(function(a, b) { return d3.ascending(dataset.nodes[a].name, dataset.nodes[b].name); }), connections: d3.range(nel).sort(function(a, b) { return d3.ascending(dataset.nodes[a].connections, dataset.nodes[b].connections) }) }; xScale.domain(sortOrders.name); // highlight 1 distances colors[1] = '#ff0000'; // draw adjacency matrix var svg = d3.select('#matrix2') .append('svg') .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom) .style('margin-left', -margin.left + 'px') .append('g') .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); svg.append('rect') .attr('class', 'background') .attr('width', width) .attr('height', height) .attr('style', 'fill: #eee;'); var rows = svg.selectAll('.row') .data(shortest) .enter() .append('g') .attr('class', 'row') .attr('transform', function(d, i) { return 'translate(0,' + xScale(i) + ')'; }) .each(drawRow); rows.append('line') .attr('x2', width) .style('stroke', '#fff'); // for labeling strategy refer to // https://bost.ocks.org/mike/miserables/ rows.append('text') .attr("x", -6) .attr("y", xScale.rangeBand() / 2) .attr("dy", ".32em") .attr("text-anchor", "end") .text(function(d, i) { return dataset.nodes[i].name; }); var columns = svg.selectAll(".column") .data(dataset.matrix) .enter() .append("g") .attr("class", "column") .attr("transform", function(d, i) { return "translate(" + xScale(i) + ")rotate(-90)"; }); columns.append("line") .attr("x1", -width) .style('stroke', '#fff'); columns.append("text") .attr("x", 6) .attr("y", xScale.rangeBand() / 2) .attr("dy", ".32em") .attr("text-anchor", "start") .text(function(d, i) { return dataset.nodes[i].name; }); function drawRow(row) { // select "this group" which has no .cell var cell = d3.select(this) .selectAll('.cell') // retain only adjacent (connected) states // states with a distance >= 1 .data(row.filter(function(d) { return d.z != Number.MAX_VALUE; })) .enter() .append('rect') .attr('class', 'cell') // we can use 0 through nel - 1 for positioning // thanks to d3 scales .attr('x', function(d) { return xScale(d.x); }) .attr('width', xScale.rangeBand()) .attr('height', xScale.rangeBand()) .style('fill', function(d) { return colors[d.z]; }) .style('stroke', 'none') .attr('data', function(d) { return d.z; }); } d3.select('#inputs') .append('input') .attr('type', 'submit') .attr('value', 'Sort By Name') .attr('margin', '5px') .on('click', function() { reorder('name'); }); d3.select('#inputs') .append('input') .attr('type', 'submit') .attr('value', 'Sort By Number Of Direct Connections') .attr('margin', '5px') .on('click', function() { console.log('reorder connections'); reorder('connections'); }); // for reordering strategy refer to // https://bost.ocks.org/mike/miserables/ function reorder(value) { xScale.domain(sortOrders[value]); var transition = svg.transition().duration(2500); transition.selectAll('.row') .delay(function(d, i) { return xScale(i) * 4; }) .attr('transform', function(d, i) { return 'translate(0,' + xScale(i) + ')'; }) .selectAll('.cell') .delay(function(d) { return xScale(d.x) * 4; }) .attr('x', function(d) { return xScale(d.x); } ); transition.selectAll('.column') .delay(function(d, i) { return xScale(i) * 4; }) .attr('transform', function(d, i) { return 'translate(' + xScale(i) + ')rotate(-90)'; }); } });