Fun with D3.js: Data Visualization Eye Candy with Streaming JSON

Tomomi Imura, @girlie_mac

Data Visualization Eye Candy with Streaming JSON

Tomomi Imura

candies

D3.js

Bubble Chart

1. Create a Static Bubble Chart with D3

1.1. Use D3's Graphical Layout

Create a pack layout with d3.layout.pack() object


var svg = d3.select('#chart').append('svg')
   .attr('width', 600).attr('height', 600);

var bubble = d3.layout.pack()
   .size([diameter, diameter])
   // new data will be loaded to bubble layout
   .value(function(d) {return d.size;})

1.2. Work with JSON Data


var data = {"countries_msg_vol": {
   "CA": 170, 
   "US": 393, 
   "CU": 9, 
   "BR": 89,
   "MX": 192, 
   ..., 
   "Other": 254
}};

Tweak Raw JSON for D3 Pack Layout

{children: [an array of objects]}

...cont'd

function processData(data) {
   var obj = data.countries_msg_vol;

   var newDataSet = [];

   for(var prop in obj) {
      newDataSet.push({name: prop,
         className: prop.toLowerCase(), size: obj[prop]});
   }
   return {children: newDataSet};
}

Define Fill Colors with CSS


.ca, .us { 
	fill: #DF4949;
}
.uc, .br, .mx {
	fill: #E27A3F;
}
.other {
	fill: #45B29D;
}
...

1.3. Enter Data into the Layout

Load the tailored data into the layout object's nodes() function


var nodes = bubble.nodes(processData(data))
   // filter out the outer bubble
   .filter(function(d) { return !d.children; });

Display in SVG

Use the generated layout calculations to display in SVG

var g = svg.append('g');
var vis = svg.selectAll('circle')
   .data(nodes, function(d) { return d.name; });

vis.enter().append('circle')
   .attr('transform', function(d) { return 'translate('
      + d.x + ',' + d.y + ')'; })
   .attr('r', function(d) { return d.r; })
   .attr('class', function(d) { return d.className; });

2. Make It Dynamic with Streaming JSON

Use PubNub API


<script src="//cdn.pubnub.com/pubnub.min.js"></script>
		
var channel = 'my-data-channel';

var pubnub = PUBNUB.init({
  subscribe_key: my_subscription_key_here
});
		

2.1. Subscribe the Live Data

To retrieve your data stream, use subscribe() API


pubnub.subscribe({
  channel: channel,
  callback: drawBubbles(message) {
  	// place the code from Step 1.3
  }
});
		

Oopsie: Overlapping Bubbles

New set of data comes in, new bubbles are displayed on top

3. Live-Update and Animate the Bubbles!

3.1. Assign Each Node with a Unique Name

To make the node updateable, you need to assign a name to each node. D3 takes a key function as a 2nd argument to the data():


var vis = svg.selectAll('circle')
   .data(nodes, function(d) { return d.name; });
  	

3.2. Create Chained Transitions

To enter new data to the existing nodes, we are going to update them. This way, each assigned bubble circle updates its size and position correctly, instead of creating a new one with new data.

D3 Data Life Cycle: Enter

Update

Exit (as new data enter)

Smooth Transitions

Create the transition on the updating elements before the entering elements because enter().append() merges entering elements into the update selection

Update

// update - This only applies to updating nodes
vis.transition()
  .duration(duration) 
  .delay(function(d, i) {delay = i * 7; return delay;})
  .attr('transform', function(d) { 
    return 'translate(' + d.x + ',' + d.y + ')'; })
  .attr('r', function(d) { return d.r; })
		

Enter

// enter    
vis.enter().append('circle')
  .attr('transform', function(d) { 
    return 'translate(' + d.x + ',' + d.y + ')'; })
  .attr('r', function(d) { return d.r; })
  .attr('class', function(d) { return d.className; })
  .style('opacity', 0) 
  .transition()
  .duration(duration * 1.2)
    

Exit

// exit
vis.exit()
  .transition()
  .duration(duration + delay)  
  .style('opacity', 0)
  .remove();
    

Demo: http://pubnub.github.io/d3-bubble/

Full Article

pubnub.com/blog/fun-with-d3js-data-visualization-eye-candy-with-streaming-json/

Thank you!

Tomomi Imura @ PubNub

pubnub.com
@pubnub
@girlie_mac
github.com/pubnub

Photo Credit