Slide 1

Slide 1 text

Building JavaScript Visualizations Part 2: You Might Want D3.js Stephen Thomas (stephen@sathomas.me)

Slide 2

Slide 2 text

Network Graph Easy with sigmajs but limited

Slide 3

Slide 3 text

D3.js Gives Us Full Control

Slide 4

Slide 4 text

D3.js Philosophy • D3.js is not really a visualization library; it does not draw visualizations • D3 = "Data Driven Documents"; it primarily associates data with DOM elements and manages the results • D3.js also provides tools that you can use to draw visualizations

Slide 5

Slide 5 text

Buying a House with sigmajs

Slide 6

Slide 6 text

Buying a House with D3.js

Slide 7

Slide 7 text

HTML Skeleton Jazz Connections

Slide 8

Slide 8 text

Data in JSON Format [ { artist: "Miles Davis", title: "Kind of Blue", itunes: "https://itunes.apple.com/...", cover: "http://a4.mzstatic.com/...", color: "#47738C", text: "#0A0606", musicians: [ "Cannonball Adderley", "Paul Chambers", "Jimmy Cobb", "John Coltrane", "Miles Davis", "Bill Evans" ] },{ artist: "John Coltrane", title: "A Love Supreme", itunes: "https://itunes.apple.com/...", cover: "http://a5.mzstatic.com/...", color: "#747C7B", text: "#343437", musicians: [ "John Coltrane", "Jimmy Garrison", "Elvin Jones", "McCoy Tyner" ] }, // Data set continues...

Slide 9

Slide 9 text

Retrieve via AJAX d3.json('data.json', function(error, data) { // data contains the JavaScript object // (in this case an array)

Slide 10

Slide 10 text

Create an Array of Nodes var nodes = data.map(function(datum, idx, list) { var node = {}; node.title = datum.title; node.subtitle = datum.artist; node.image = datum.cover; node.url = datum.itunes; node.color = datum.color; node.text = datum.text; node.links = datum.musicians.slice(0); var radius = 0.4 * Math.min(height,width); var theta = 2 * Math.PI * idx / list.length; node.x = (width/2) + radius*Math.sin(theta); node.y = (height/2) + radius*Math.cos(theta); return node; });

Slide 11

Slide 11 text

Find All Links Between Nodes var links = []; data.forEach(function(srcNode, srcIdx, srcList) { srcNode.musicians.forEach(function(srcLink) { for (var tgtIdx = srcIdx + 1; tgtIdx < srcList.length; tgtIdx++) { var tgtNode = srcList[tgtIdx]; if (tgtNode.musicians.some(function(tgtLink){ return tgtLink === srcLink; })) { links.push({ source: srcIdx, target: tgtIdx, link: srcLink }); } } }); });

Slide 12

Slide 12 text

Eliminate Duplicate Links var edges = []; links.forEach(function(link) { var existingEdge = false; for (var idx = 0; idx < edges.length; idx++) { if ((link.source === edges[idx].source) && (link.target === edges[idx].target)) { existingEdge = edges[idx]; break; } } if (existingEdge) { existingEdge.links.push(link.link); } else { edges.push({ source: link.source, target: link.target, links: [link.link] }); } });

Slide 13

Slide 13 text

DOM Selection & Manipulation Creating the Stage var graph = d3.select('#graph'); var notes = d3.select('#notes'); var svg = graph.append('svg') .attr('width', width) .attr('height', height);

Slide 14

Slide 14 text

Draw the Nodes - First Attempt Although the following code does add 25 circles to the page, it's not taking advantage of D3.js nodes.forEach(function(node) { svg.append("circle"); }); There's a better approach...

Slide 15

Slide 15 text

Draw the Nodes - Associating Data with DOM Elements var selection = svg.selectAll("circle") .data(nodes); This looks really weird ... at first ...

Slide 16

Slide 16 text

What if the Circles Existed? svg.selectAll("circle").data(nodes);

Slide 17

Slide 17 text

Only Some Circles? svg.selectAll("circle").data(nodes);

Slide 18

Slide 18 text

Even No Circles? svg.selectAll("circle").data(nodes);

Slide 19

Slide 19 text

Draw the Missing Circles var selection = svg.selectAll("circle") .data(nodes); selection.enter().append("circle"); Or, more concisely var nodeSelection = svg.selectAll("circle") .data(nodes) .enter().append("circle");

Slide 20

Slide 20 text

Draw the Edges var edgeSelection = svg.selectAll("line") .data(edges) .enter().append("line"); (Actually, we should draw the edges before the nodes.)

Slide 21

Slide 21 text

Position the Elements nodeSelection .attr('r', 10) .attr('cx', function(dataValue) { return dataValue.x; }) .attr('cy', function(dataValue) { return dataValue.y; }); edgeSelection .attr('x1', function(d) { return nodes[d.source].x; }) .attr('y1', function(d) { return nodes[d.source].y; }) .attr('x2', function(d) { return nodes[d.target].x; }) .attr('y2', function(d) { return nodes[d.target].y; });

Slide 22

Slide 22 text

Houston, We Have a Graph

Slide 23

Slide 23 text

Force Direction Algorithmic approach to positioning graph elements • Simulates a physical world with forces such as gravity and electromagnetism • Elements given properties that correspond to mass and charge • Positions elements in reaction to simulated physical forces

Slide 24

Slide 24 text

D3.js Force Layout Initialize the layout var force = d3.layout.force() .size([width, height]) .nodes(nodes) .links(edges) .linkDistance(40) .force(-500);

Slide 25

Slide 25 text

D3.js Force Layout Update the positions force.on('tick', function() { nodeSelection .attr('cx', function(d) { return d.x; }) .attr('cy', function(d) { return d.y; }); edgeSelection .attr('x1', function(d) { return d.source.x; }) .attr('y1', function(d) { return d.source.y; }) .attr('x2', function(d) { return d.target.x; }) .attr('y2', function(d) { return d.target.y; }); });

Slide 26

Slide 26 text

D3.js Force Layout force.start();

Slide 27

Slide 27 text

Interactivity D3.js event handling nodeSelection.on('click', function(d) { d3.select(this) .classed('selected', true) .attr('r', 1.5*nodeRadius); });

Slide 28

Slide 28 text

Animations D3.js transitions nodeSelection.on('click', function(d) { d3.select(this) .classed('selected', true) .transition() .attr('r', 1.5*nodeRadius); });

Slide 29

Slide 29 text

More Information Source at http://bl.ocks.org/sathomas/ a7b0062211af69981ff3 Web Site: jsDataV.is Book: Data Visualization with JavaScript (No Starch Press, available early 2015) Full content available for free on web site