Building JavaScript Visualizations Part 2

Building JavaScript Visualizations Part 2

A5b424d4146905962a24acd6815aeb84?s=128

Stephen Thomas

October 18, 2014
Tweet

Transcript

  1. Building JavaScript Visualizations Part 2: You Might Want D3.js Stephen

    Thomas (stephen@sathomas.me)
  2. Network Graph Easy with sigmajs but limited

  3. D3.js Gives Us Full Control

  4. 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
  5. Buying a House with sigmajs

  6. Buying a House with D3.js

  7. HTML Skeleton <!DOCTYPE html> <html> <head> <meta charset='utf-8'> <title>Jazz Connections</title>

    </head> <body> <div id='container'> <div id='graph'></div> <div id='notes'></div> </div> <script src='http://d3js.org/d3.v3.min.js'></script> </body> </html>
  8. 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...
  9. Retrieve via AJAX d3.json('data.json', function(error, data) { // data contains

    the JavaScript object // (in this case an array)
  10. 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; });
  11. 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 }); } } }); });
  12. 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] }); } });
  13. 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);
  14. 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...
  15. Draw the Nodes - Associating Data with DOM Elements var

    selection = svg.selectAll("circle") .data(nodes); This looks really weird ... at first ...
  16. What if the Circles Existed? svg.selectAll("circle").data(nodes);

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

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

  19. 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");
  20. Draw the Edges var edgeSelection = svg.selectAll("line") .data(edges) .enter().append("line"); (Actually,

    we should draw the edges before the nodes.)
  21. 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; });
  22. Houston, We Have a Graph

  23. 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
  24. D3.js Force Layout Initialize the layout var force = d3.layout.force()

    .size([width, height]) .nodes(nodes) .links(edges) .linkDistance(40) .force(-500);
  25. 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; }); });
  26. D3.js Force Layout force.start();

  27. Interactivity D3.js event handling nodeSelection.on('click', function(d) { d3.select(this) .classed('selected', true)

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

    .attr('r', 1.5*nodeRadius); });
  29. 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