Archives

Tag: D3.js

  • d3.nest


    While working on the 51 Line Charts for my Opioid Crisis data visualization, I ran into an issue with generating all 51 charts at the same time: The data was either in 51 rows stacked and I couldn’t access the dates effectively, or 306 rows unstacked and I had 6x as many objects as I needed when I did a join.

    The solution was to use D3.nest. Nesting allows elements in an array to be grouped into a hierarchical tree structure; think of it like the GROUP BY operator in SQL, except you can have multiple levels of grouping, and the resulting output is a tree rather than a flat table. The levels in the tree are specified by key functions.

    When I used the state as a key, it grouped each state’s data together in a tree.

    Input:

    state,code,year,deaths,adjrate Alabama,1,2010,547,11.8 Alabama,1,2011,552,11.8 Alabama,1,2012,562,12.1 Alabama,1,2013,598,12.7 Alabama,1,2014,723,15.2 Alabama,1,2015,736,15.7 Alaska,2,2010,83,11.6 Alaska,2,2011,107,14.2 Alaska,2,2012,129,17.4 Alaska,2,2013,105,14.4 Alaska,2,2014,124,16.8 Alaska,2,2015,122,16 Arizona,4,2010,1098,17.5 Arizona,4,2011,1071,16.9 Arizona,4,2012,1134,17.7 Arizona,4,2013,1222,18.7 Arizona,4,2014,1211,18.2 Arizona,4,2015,1274,19 Arkansas,5,2010,350,12.5 Arkansas,5,2011,355,12.6 Arkansas,5,2012,373,13.1 Arkansas,5,2013,319,11.1 Arkansas,5,2014,356,12.6 Arkansas,5,2015,392,13.8

    D3 code:

    d3.nest()   .key(function(d) { return d.state; })   .entries(data);

    Output:

    [   {     "key": "Alabama",     "values": [       {         "state": "Alabama",         "code": "1",         "year": "2010",         "deaths": "547",         "adjrate": "11.8"       },       {         "state": "Alabama",         "code": "1",         "year": "2011",         "deaths": "552",         "adjrate": "11.8"       },       {         "state": "Alabama",         "code": "1",         "year": "2012",         "deaths": "562",         "adjrate": "12.1"       },       {         "state": "Alabama",         "code": "1",         "year": "2013",         "deaths": "598",         "adjrate": "12.7"       },       {         "state": "Alabama",         "code": "1",         "year": "2014",         "deaths": "723",         "adjrate": "15.2"       },       {         "state": "Alabama",         "code": "1",         "year": "2015",         "deaths": "736",         "adjrate": "15.7"       }     ]   },   {     "key": "Alaska",     "values": [       {         "state": "Alaska",         "code": "2",         "year": "2010",         "deaths": "83",         "adjrate": "11.6"       },       {         "state": "Alaska",         "code": "2",         "year": "2011",         "deaths": "107",         "adjrate": "14.2"       },       {         "state": "Alaska",         "code": "2",         "year": "2012",         "deaths": "129",         "adjrate": "17.4"       },       {         "state": "Alaska",         "code": "2",         "year": "2013",         "deaths": "105",         "adjrate": "14.4"       },       {         "state": "Alaska",         "code": "2",         "year": "2014",         "deaths": "124",         "adjrate": "16.8"       },       {         "state": "Alaska",         "code": "2",         "year": "2015",         "deaths": "122",         "adjrate": "16"       }     ]   },   {     "key": "Arizona",     "values": [       {         "state": "Arizona",         "code": "4",         "year": "2010",         "deaths": "1098",         "adjrate": "17.5"       },       {         "state": "Arizona",         "code": "4",         "year": "2011",         "deaths": "1071",         "adjrate": "16.9"       },       {         "state": "Arizona",         "code": "4",         "year": "2012",         "deaths": "1134",         "adjrate": "17.7"       },       {         "state": "Arizona",         "code": "4",         "year": "2013",         "deaths": "1222",         "adjrate": "18.7"       },       {         "state": "Arizona",         "code": "4",         "year": "2014",         "deaths": "1211",         "adjrate": "18.2"       },       {         "state": "Arizona",         "code": "4",         "year": "2015",         "deaths": "1274",         "adjrate": "19"       }     ]   } ]

    A few useful tools and resources for understanding d3.nest:

  • Sol LeWitt’s Wall Drawings 852 and 853 with D3.js


    Aren’t familiar with Sol LeWitt or his art? Read this.

    I had an insight while going through the D3.js documentation to find a solution to another piece: d3.area() with a highly curved fit line isn’t that different from wall drawings 852 and 853! By the way, 852 and 853 are actually distinct pieces, though they are often grouped together into one display like the one at MASSMoCA. I worked on the solution to 852 first, then modified it to make 853:

    Wall Drawing 852: A wall divided from the upper left to the lower right by a curvy line; left: glossy yellow; right: glossy purple.

    Wall Drawing 853: A wall bordered and divided vertically into two parts by a flat black band. Left part: a square is divided vertically by a curvy line. Left: glossy red; right: glossy green; Right part: a square is divided horizontally by a curvy line. Top: glossy blue; bottom: glossy orange.

    Here are previews of 852 and 853 respectively:

    Open my D3 implementation of Wall Drawing 852 in a new window →

    Open my D3 implementation of Wall Drawing 853 in a new window →

    They are also available as blocks: 852 853

    The underlying data set

    So much of implementing these drawings with D3.js rely on making a solid, usable data set to join elements with. (After all, D3 stands for data-driven-documents.) Creating these data sets is where I spend most of my time. Once I’ve created them, everything else follows pretty quickly.

    Since d3.area() makes an area graph with given points, I had to create a data set of points. I wanted the start and end points to be defined with random (but constrained) points in the middle. Here is what I came up with for 852:

    var	ww = window.innerWidth, 	wh = window.innerHeight;  function getRandomArbitrary(min, max) {   return Math.random() * (max - min) + min; }  function lineData() { 	var data = new Array(); 		for (var point = 0; point < 5; point++) { 			var x = getRandomArbitrary(ww/10, ww-ww/10); // Constrains within the middle 80% 			var y = getRandomArbitrary(wh/10, wh-wh/10);		 				data.push({ 				x: x, 				y: y 			}) 		} 		// Starting point upper left 		data.push({ 			x: 0, 			y: 0 		}); 		// End point lower right 		data.push({ 			x: ww, 			y: wh 		}) 	 	return data; }

    For 853, things were a little trickier since the left is oriented vertically:

    var ww = window.innerWidth, 	wh = window.innerHeight - 80, 	halfwidth = ww/2 - 60;	  function getRandomArbitrary(min, max) {   return Math.random() * (max - min) + min; }  function fillLeft() { 	var data = new Array(); 		for (var point = 0; point < 3; point++) { 			var x = getRandomArbitrary(halfwidth/10, halfwidth-halfwidth/10); // Keeps within the middle 80% 			var y = getRandomArbitrary(wh/10, wh-wh/10);		 				data.push({ 				x: x, 				y: y 			}) 		} 		data.push({ 			x: halfwidth/2, 			y: 0 		}); 		data.push({ 			x: halfwidth/2, 			y: wh 		}) 	 	return data; }   function fillRight() { 	var data = new Array(); 		for (var point = 0; point < 3; point++) { 			var x = getRandomArbitrary(halfwidth/10, halfwidth-halfwidth/10); // Keeps within the middle 80% 			var y = getRandomArbitrary(wh/10, wh-wh/10);		 				data.push({ 				x: x, 				y: y 			}) 		} 		data.push({ 			x: 0, 			y: wh/2 		}); 		data.push({ 			x: halfwidth, 			y: wh/2 		}) 	 	return data; } 

    d3.area()

    One of the most important things to note about d3.area() is that you must sort your data sets (by x if the chart is horizonal, y if vertical) before passing them to the function. Otherwise you end up with something like this because the values are out of order:

    Out of order values!

    D3 has selection.sort() built in, but you also need to pass a function telling it how to sort:

    .sort(function(a,b) { return +a.x - +b.x; }) // Sorting by x ascending .sort(function(a,b) { return +a.y - +b.y; }) // Sorting by y ascending

    Most area charts tend to be horizontal, and nearly all examples you can find are structured in this way. You need to pass .x, .y0, and .y1:

    var area = d3.area()     .x(function(d) { return d.x; })     .y0(window.innerHeight)     .y1(function(d) { return d.y; })     .curve(d3.curveBasis);

    If you are switching to a vertical orientation, you instead need to pass .y, .x0, and .x1:

    var areaLeft = d3.area() 	.x0(0)     .x1(function(d) { return d.x; })     .y(function(d) { return d.y; })     .curve(d3.curveBasis);

    Curves and Points

    I played with a number of curve functions and a number of points to see how they compared to traditional interpretations of Sol LeWitt’s pieces:

    • curveCatmullRom
    • curveCardinal
    • curveBasis
    • curveNatural
    • curveLinear

    I eventually settled on curveBasis for both pieces. It creates the smoothest curving line with the given random points, which produces results similar to the curved lines in Sol’s earlier work. curveCatmullRom was my second choice, but occasionally produced harsh concave regions.

    For the larger standalone piece (852) I used 7 total points, including the end points. For 853, which consists of two different curves, I used 5 points each since the widths were smaller.

    Rebuilding on screen resize

    When you resize the screen, the divs gets destroyed and then rebuilt based on the new screen size. Here is the function I use to handle that. It is the same thing I used on 86 and on my Jekyll posts heatmap calendar. I wrapped the D3 instructions in its own function, called that function on first load, then wrote a function to destroy the node divs when the screen is resized and reexecute the D3 instructions after the screen resize ends. I’m sure it can be done with regular javascript, but jQuery makes this kind of thing fast and easy:

    // run on first load sol852();  $(window).resize(function() {     if(this.resizeTO) clearTimeout(this.resizeTO);     this.resizeTO = setTimeout(function() {         $(this).trigger('resizeEnd');     }, 500); });  //resize on resizeEnd function $(window).bind('resizeEnd', function() { 	 d3.selectAll("div.node").remove(); 	 sol852(); });

    Tools Used

    • D3.js – The D3.js library is well-suited to Sol LeWitt’s works. D3’s foundational principle of joining data to elements is easy to apply to LeWitt’s drawings.
    • jQuery – I’m using jQuery to detect changes in the window size and trigger removing & rebuilding the visualization.

    Inspiration and References

    Detail shot of the version on display at MASSMoCA:

    Detail shot of the version on display at MASSMoCA:

  • Make Your Own Sol LeWitt 614!


    It is also available as a block.

    Background

    Here are Sol’s original instructions for Wall Drawing 614:

    Wall Drawing 614: Rectangles formed by 3-inch (8 cm) wide India ink bands, meeting at right angles.

    Technical Details

    For the technical details of making the drawing itself, check out my post on it. This post focuses on rebuilding the tool based on user input.

    The Inputs

    I used a form, a range input, an output, and some in-line JavaScript to power the sliders and display their values in real-time:

    Black band width: type="range" name="paddingInput" id="paddingInputId" value="15" min="1" max="50" oninput="paddingOutputId.value = paddingInputId.value"> name="paddingOutput" id="paddingOutputId">15px /> Number of rectangles: type="range" name="rectInput" id="rectInputId" value="40" min="5" max="300" oninput="rectOutputId.value = rectInputId.value"> name="rectOutput" id="rectOutputId">40

    Live example:

    Black band width: 15px
    Number of rectangles: 40

    Basing my functions on the values of the inputs

    A set a variable to the element I wanted to access, then I called the .value of that element in my functions. (See the post on 614 for more info about these functions.)

    var padding = document.getElementById("paddingInputId"); var rectNumber = document.getElementById("rectInputId");  function treeData() {   var obj = {name: "rect"};   var children = [];   for (var row = 0; row < rectNumber.value; row++) {     children.push({id: row, value: getRandomArbitrary(60, 1000)})   }   obj['children'] = children;   return obj; }  var treemap = d3.treemap()     .size([width, height])     .padding(padding.value)     .round(true)     .tile(d3.treemapBinary);

    Rebuilding after inputs are changed

    Similar to rebuilding on screen resize in the original version, I detect when there is a change in one of the inputs and then call a function to remove the divs and rebuild the treemap based on the inputs. jQuery makes this kind of thing fast and easy:

    $( "#paddingInputId" ).change(function() {   	d3.selectAll("div.node").remove(); 	sol614(); });  $( "#rectInputId" ).change(function() {   	d3.selectAll("div.node").remove(); 	sol614(); });

    Want to dig in a little further? Check out my implementation and view source. All of the specs are there.

    Tools Used

    • D3.js – The D3.js library is well-suited to Sol LeWitt’s works. D3’s foundational principle of joining data to elements is easy to apply to LeWitt’s drawings.
    • jQuery – I’m using jQuery to detect changes in the window size and trigger removing & rebuilding the visualization.
  • Sol LeWitt’s Wall Drawing 614 with D3.js Treemap and Randomization


    Aren’t familiar with Sol LeWitt or his art? Read this.

    Wall Drawing 614: Rectangles formed by 3-inch (8 cm) wide India ink bands, meeting at right angles.

    Here is a preview:

    Open my D3 implementation of Wall Drawing 614 in a new window →

    It is also available as a block.

    Design Details

    • LeWitt’s instructions are pretty open. The drafter can choose the number of rectangles. I chose 40.
    • Since screens work in pixels instead of inches, I chose to make the bands 15px wide. I tried various configurations and I think 15px
    • I chose to conform this to the size of the viewer’s window and have each display randomly generated, so each viewer will have a unique experience.
    • Just as the size of the wall this piece was implemented on determined the density of the lines, the size of the window will determine the density. You can try out different densities by resizing the screen. The illustration will rebuild after you resize the window.
    • If you like this, I also made a version that you can customize.

    Technical Details

    • The placement and size of the rectangles are handled by d3.treemap().
    • I tried a few different tiling methods and decided to use d3.treemapBinary. I liked the look of it the best.
    • Each viewer gets a unique experience because the size of each rectangle is set at random on each page load.

    The underlying data set

    So much of implementing these drawings with D3.js rely on making a solid, usable data set to join elements with. (After all, D3 stands for data-driven-documents.) Creating these data sets is where I spend most of my time. Once I’ve created them, everything else follows pretty quickly. Here is the function that creates the underlying data set on this piece. I owe major props to Eric Davis on helping me figure this out. I was getting close, but nothing I made was accepted by treemap(). He helped me identify my issues, which included making this data set and then passing it to d3.hierarchy before passing it to treemap.

    function getRandomArbitrary(min, max) { 	  return Math.random() * (max - min) + min; }  function treeData() {   var obj = {name: "rect"};   var children = [];   for (var row = 0; row < 40; row++) {     children.push({id: row, value: getRandomArbitrary(60, 1000)})   }   obj['children'] = children;   return obj; }

    Working with treemap

    One of the difficulties of D3.js transitioning from v3 to v4 recently is that most of the tutorials you can find on the web no longer work. Thankfully Mike Bostock created a great example of a treemap with v4 that I referenced quite a bit.

    After a few passes through the documentation and a call on my friend Eric Davis, we figured out how to pass data to d3.treemap():

    1. Create a data set that has a clear parent-child structure with unique parents
    2. Pass this to d3.hierarchy
    3. Call .sum() on d3.hierarchy and sum the values before passing it to treemap
    4. Pass it to treemap

    See below:

    var width = window.innerWidth + 30, 	height = window.innerHeight + 30; var format = d3.format(",d");  var treemap = d3.treemap()     .size([width, height])     .padding(15)     .round(true)     .tile(d3.treemapBinary);  var tree = d3.hierarchy(tree);  tree.sum(function(d) { return d.value; }); // always log for debugging! console.log(tree);  treemap(tree);  d3.select("body") 	.selectAll(".node") 	.data(tree.leaves()) 	.enter().append("div") 	  .attr("class", "node") 	  .attr("title", function(d) { return d.data.id; }) 	  .style("left", function(d) { return d.x0 + "px"; }) 	  .style("top", function(d) { return d.y0 + "px"; }) 	  .style("width", function(d) { return d.x1 - d.x0 + "px"; }) 	  .style("height", function(d) { return d.y1 - d.y0 + "px"; }) 	  .style("background", "#fff");

    Rebuilding on screen resize

    When you resize the screen, the divs gets destroyed and then rebuilt based on the new screen size. Here is the function I use to handle that. It is the same thing I used on 86 and on my Jekyll posts heatmap calendar. I wrapped the D3 instructions in its own function, called that function on first load, then wrote a function to destroy the node divs when the screen is resized and reexecute the D3 instructions after the screen resize ends. I’m sure it can be done with regular javascript, but jQuery makes this kind of thing fast and easy:

    // run on first load sol614();  $(window).resize(function() {     if(this.resizeTO) clearTimeout(this.resizeTO);     this.resizeTO = setTimeout(function() {         $(this).trigger('resizeEnd');     }, 500); });  //resize on resizeEnd function $(window).bind('resizeEnd', function() { 	 d3.selectAll("div.node").remove(); 	 sol614(); });

    Want to dig in a little further? Check out my implementation and view source. All of the specs are there.

    Tools Used

    • D3.js – The D3.js library is well-suited to Sol LeWitt’s works. D3’s foundational principle of joining data to elements is easy to apply to LeWitt’s drawings.
    • jQuery – I’m using jQuery to detect changes in the window size and trigger removing & rebuilding the visualization.

    Inspiration and References

    Detail shot of the version on display at MASSMoCA:

    Detail shot of the version on display at MASSMoCA:

    Another shot of the version on display at MASSMoCA:

    Detail shot of the version on display at MASSMoCA:

    If you like this, I also made a version that you can customize. Check it out.

  • Sol LeWitt’s Wall Drawing 289 with D3.js Transitions


    Aren’t familiar with Sol LeWitt or his art? Read this.

    Wall Drawing 289: A 6-inch (15 cm) grid covering each of the four black walls. White lines to points on the grids. Fourth wall: twenty-four lines from the center, twelve lines from the midpoint of each of the sides, twelve lines from each corner. (The length of the lines and their placement are determined by the drafter.)

    Here is a gfycat preview:

    Open my D3 implementation of Wall Drawing 289 in a new window →

    It is also available as a block.

    Design Details

    • LeWitt’s instructions dictate that the lines are to go to a point on a 6-inch grid. I didn’t lay out a grid first, but I did round the random line ending points to they land on whole numbers.
    • I chose to conform this to the size of the viewer’s window and have each display randomly generated, so each viewer will have a unique experience.
    • Just as the size of the wall this piece was implemented on determined the density of the lines, the size of the window will determine the density. You can try out different densities by resizing the screen. The illustration will rebuild after you resize the window.
    • I tried drawing each line individually in time like I did with number 86, but it looked so much cooler with all lines coming in at one from their starting points. The viewer gets the understanding of where each line starts and the randomly-generated end points fulfill the requirement of the length and location of the lines being determined by the drafter.

    Technical Details

    • The midpoints are determined by the size of the window.
    • Each viewer gets a unique experience because the length of the lines is set at random on each page load.

    The underlying data set

    So much of implementing these drawings with D3.js rely on making a solid, usable data set to join elements with. (After all, D3 stands for data-driven-documents.) Creating these data sets is where I spend most of my time. Once I’ve created them, everything else follows pretty quickly. Here is the function I wrote to create the underlying data set on this piece:

    function getRandomArbitrary(min, max) {   return Math.round(Math.random() * (max - min) + min); }  function lineData() { 	var data = new Array(); 	var id = 1;	 	var ww = window.innerWidth; 	var wh = window.innerHeight; 	var numLines = 12; 	// iterate for cells/columns inside rows 		for (var center = 0; center < numLines * 2; center++) {	 				data.push({ 				id: id, 				class: "center", 				x1: ww/2, 				y1: wh/2, 				x2: getRandomArbitrary(0, ww), 				y2: getRandomArbitrary(0, wh) 			}) 			id++; 		} 				 		for (var topleft = 0; topleft < numLines; topleft++) {	 				data.push({ 				id: id, 				class: "topleft", 				x1: 0, 				y1: 0, 				x2: getRandomArbitrary(0, ww), 				y2: getRandomArbitrary(0, wh) 			}) 			id++; 		}  		for (var topright = 0; topright < numLines; topright++) {	 				data.push({ 				id: id, 				class: "topright", 				x1: ww, 				y1: 0, 				x2: getRandomArbitrary(0, ww), 				y2: getRandomArbitrary(0, wh) 			}) 			id++; 		}  		for (var bottomright = 0; bottomright < numLines; bottomright++) {		 				data.push({ 				id: id, 				class: "bottomright", 				x1: ww, 				y1: wh, 				x2: getRandomArbitrary(0, ww), 				y2: getRandomArbitrary(0, wh) 			}) 			id++; 		}  		for (var bottomleft = 0; bottomleft < numLines; bottomleft++) {		 				data.push({ 				id: id, 				class: "bottomleft", 				x1: 0, 				y1: wh, 				x2: getRandomArbitrary(0, ww), 				y2: getRandomArbitrary(0, wh) 			}) 			id++; 		}  		for (var middleleft = 0; middleleft < numLines; middleleft++) {		 				data.push({ 				id: id, 				class: "middleleft", 				x1: 0, 				y1: wh/2, 				x2: getRandomArbitrary(0, ww), 				y2: getRandomArbitrary(0, wh) 			}) 			id++; 		}  		for (var middleright = 0; middleright < numLines; middleright++) {		 				data.push({ 				id: id, 				class: "middleright", 				x1: ww, 				y1: wh/2, 				x2: getRandomArbitrary(0, ww), 				y2: getRandomArbitrary(0, wh) 			}) 			id++; 		}   		for (var middletop = 0; middletop < numLines; middletop++) {		 				data.push({ 				id: id, 				class: "middletop", 				x1: ww/2, 				y1: 0, 				x2: getRandomArbitrary(0, ww), 				y2: getRandomArbitrary(0, wh) 			}) 			id++; 		}  		for (var middlebottom = 0; middlebottom < numLines; middlebottom++) {		 				data.push({ 				id: id, 				class: "middlebottom", 				x1: ww/2, 				y1: wh, 				x2: getRandomArbitrary(0, ww), 				y2: getRandomArbitrary(0, wh) 			}) 			id++; 		} 	return data; } 

    Transition

    I handled the transition by first defining the lines as starting and ending at the same set of coordinates, then doing a delayed transition to their real end points:

    var line = svg.selectAll(".rand") 	.data(lineData) 	.enter().append('line') 	.attr("class", function(d) { return d.class; }) 	.attr("id", function(d) { return d.id; }) 	.attr("x1", function(d) { return d.x1; } ) 	.attr("y1", function(d) { return d.y1; }) 	.attr("x2", function(d) { return d.x1; } ) 	.attr("y2", function(d) { return d.y1; }).transition().duration(3000) 	.attr("x2", function(d) { return d.x2; }) 	.attr("y2", function(d) { return d.y2; });

    Rebuilding on screen resize

    When you resize the screen, the svg gets destroyed and then rebuilt based on the new screen size. New midpoints are calculated and new ending points for each other lines are calculated. Here is the function I use to handle that. It is the same thing I used on 86 and on my Jekyll posts heatmap calendar. I wrapped the D3 instructions in its own function, called that function on first load, then wrote a function to destroy the svg when the screen is resized and reexecute the D3 instructions after the screen resize ends. I’m sure it can be done with regular javascript, but jQuery makes this kind of thing fast and easy:

    // run on first load sol289();  $(window).resize(function() {     if(this.resizeTO) clearTimeout(this.resizeTO);     this.resizeTO = setTimeout(function() {         $(this).trigger('resizeEnd');     }, 500); });  //resize on resizeEnd function $(window).bind('resizeEnd', function() { 	 d3.selectAll("svg").remove(); 	 sol289(); });

    Want to dig in a little further? Check out my implementation and view source. All of the specs are there.

    Tools Used

    • D3.js – The D3.js library is well-suited to Sol LeWitt’s early works. D3’s foundational principle of joining data to elements is easy to apply to LeWitt’s symmetrical objects.
    • jQuery – I’m using jQuery to detect changes in the window size and trigger removing & rebuilding the visualization.

    Inspiration and References

    Detail shot of the version on display at MASSMoCA: Detail shot of the version on display at MASSMoCA:

  • Sol LeWitt’s Wall Drawing 86 with D3.js Transitions


    Aren’t familiar with Sol LeWitt or his art? Read this.

    Wall Drawing 86: Ten thousand lines about 10 inches (25 cm) long, covering the wall evenly.

    Here is a gfycat preview:

    Open my D3 implementation of Wall Drawing 86 in a new window →

    It is also available as a block.

    Design Details

    • LeWitt’s instructions dictate 10,000 10-inch lines on a wall. Since computer screens are much smaller, I chose 1,000 lines that are roughly 141 pixels long (100*sqrt(2)).
    • I chose to conform this to the size of the viewer’s window and have each display randomly generated, so each viewer will have a unique experience.
    • Just as the size of the wall this piece was implemented on determined the density of the lines, the size of the window will determine the density. You can try out different densities by resizing the screen. The illustration will rebuild after you resize the window.
    • LeWitt’s instructions indicate that the wall should be evenly covered. Due to randomly generating the starting and ending values of the points, I couldn’t figure out a way to have even distribution without having some lines go outside of the edges of the window. If I kept every point inside the window, a space around the edges emerges where the lines are not as evenly distributed. I decided it was better to have lines going over the edge than an uneven distribution.

    Technical Details

    Making equal length line segments

    Making equal length line segments turned out to be a little more difficult than I anticipated. Lines are drawn by picking two sets of (x,y) coordinates and connecting them. If you leave this 100% random, you’ll get lines of all different lengths and slopes. If you want lines of equal lengths and different slopes, however, you need to be a little more crafty.

    I came up with two ways of doing it: 1) Pick a random slope and fixed length, then solve a series of rearranged quadratic equations derived from the point-slope form of a line and the distance formulaArchived Link. 2) Derive the second set of coordinates based off of a fixed formula with the first coordinates. This will result in lines having all the same slope initially. Then apply a transform to rotate the lines about their midpoints by a random angle.

    Number two was faster and simpler for me to implement, so I went that route.

    The underlying data set

    So much of implementing these drawings with D3.js rely on making a solid, usable data set to join elements with. (After all, D3 stands for data-driven-documents.) Creating these data sets is where I spend most of my time. Once I’ve created them, everything else follows pretty quickly. Here is the function I wrote to create the underlying data set on this piece:

    function lineData() { 	function getRandomArbitrary(min, max) { 	  return Math.random() * (max - min) + min; 	} 	var data = new Array(); 	var id = 1;	 	var ww = window.innerWidth; // Width of the window viewing area 	var wh = window.innerHeight; // Height of the window viewing area 	// iterate for cells/columns inside rows 		for (var line = 0; line  1000; line++) {  // 1000 lines 			var x1 = getRandomArbitrary(-100, ww); // initial points can start 100px off the screen to make even distribution work 			var y1 = getRandomArbitrary(-100, wh);		 				data.push({ 				id: id, // For identification and debugging 				x1: x1, 				y1: y1, 				x2: x1 + 100, // Move 100 to the right 				y2: y1 + 100, // Move 100 up 				rotate: getRandomArbitrary(0, 360) // Pick a random angle between 0 and 360 			}) 			id++; // Increment the ID 		} 	return data; } 

    Rotation

    To make the rotation work without affecting the even distribution of the lines, I needed to rotate them around their midpoints. Otherwise they’d be rotated around (0,0), which puts lines out of the viewing area at large angles. I essentially used the midpoint formula to calculate the midpoints. I simplified since I know the length of each line. Here is the tranform attribute I applied:

    .attr("transform", function(d) { return "rotate(" + d.rotate + " " + (d.x1 + 50) + " " + (d.y1 + 50) + ")";})

    Transition

    I handled the transition by first defining the lines as starting and ending at the same set of coordinates, then doing a delayed transition to their real end points. I applied the rotation before the transition so that the lines would appear to grow, but not rotate. To create the effect of the lines being drawn one by one in real-time, I added a delay function with an index. With a 20 millisecond delay and 1000 lines, it takes about 20 seconds to complete:

    var line = svg.selectAll("line") 	.data(lineData) 	.enter().append('line') 	.attr("id", function(d) { return d.id; }) 	.attr("x1", function(d) { return d.x1; }) 	.attr("y1", function(d) { return d.y1; }) 	.attr("transform", function(d) { return "rotate(" + d.rotate + " " + (d.x1 + 50) + " " + (d.y1 + 50) + ")";}) 	.attr("x2", function(d) { return d.x1; }) 	.attr("y2", function(d) { return d.y1; }).transition().delay(function(d,i){ return 20*i; }).duration(750) 	.attr("x2", function(d) { return d.x2; }) 	.attr("y2", function(d) { return d.y2; });

    Want to dig in a little further? Check out my implementation and view source. All of the specs are there.

    Tools Used

    • D3.js – The D3.js library is well-suited to Sol LeWitt’s early works. D3’s foundational principle of joining data to elements is easy to apply to LeWitt’s symmetrical objects.
    • jQuery – I’m using jQuery to detect changes in the window size and trigger removing & rebuilding the visualization.

    Inspiration and References

    Detail shot of the version on display at MASSMoCA: Detail shot of the version on display at MASSMoCA:

  • Sol LeWitt’s Wall Drawing 87 with D3.js Transitions


    Aren’t familiar with Sol LeWitt or his art? Read this.

    Wall Drawing 87: A square divided horizontally and vertically into four equal parts, each with lines and colors in four directions superimposed progressively.

    Since this is so close to 56 (which I’ve already done), I wanted to add an additional element to challenge myself and learn something: Transitions. The difference between the two is the separate color for each line direction, which ended up lending itself to transitions nicely.

    Adding the color and transitions into this one made me change my mental model for how I’m building these implementations. I was able to complete the first three I picked (56, 11, and 391) with only four elements inside the SVG: One for each quadrant. This is because I was able to make a single pattern for each quadrant (even though it appears that there are four sets lines are going in different directions) and have the patterns match up with the other quadrants. Textures.js doesn’t support making line-based patterns with more than one line color, so I needed to find a different approach.

    The solution turned out to solve my mental block for both the colors and the transitions. I started to doodle this drawing in a journal and realized that I did each line direction one whole layer at a time, layering each one on top of the others. Yet, I had been trying to cram all four directions into a single layer here on the web. So, I modified my D3 code to join more elements with different classes to the same underlying grid array to simulate the effect of layering the different colors.

    Then, I added a transition() and a delay() to each “layer” so that the viewer can also experience and take notice of the intentional layering of this piece.

    There is definitely more I can do with transitions. This is just the beginning for me. I achieved what I wanted with this piece, so I’m going to move on to others and continue exploring and learning how to implement transitions.

    Here is an image preview. Click on it to see the D3.js version:

    Open my D3 implementation of Wall Drawing 87 in a new window →

    It is also available as a block.

    Design Details

    • While any interpretation of Sol LeWitt’s instructions is technically valid as long as each statement is accounted for, I feel a responsibility to make my implementation beautiful, clean, and symmetrical.
    • When he says “four directions”, the common interpretation is 0 degrees, 90 degrees, 45 degrees, and 135 degrees.
    • On a wall, the dimensions are dictated by the physical size of the wall itself. On the web, since windows change size so easily and the proportions of the art are constant, I chose to define a fixed size for this piece.

    Color Palette

    Unlike the earlier colored versions that used grey, yellow, red, and blue, this uses Sol Lewitt’s second color palette of choice: grey, yellow, tan, and reddish-brown.

    Concord

    cmyk:
    44 36 33 1 1.00
    rgba:
    124 124 124 1.00
    hex:
    #7C7C7C

    Download color

    Sweet Corn

    cmyk:
    3 4 45 0 1.00
    rgba:
    254 238 136 1.00
    hex:
    #FEEE88

    Download color

    Jaffa

    cmyk:
    2 48 67 1 1.00
    rgba:
    222 123 74 1.00
    hex:
    #DE7B4A

    Download color

    Hawaiian Tan

    cmyk:
    7 56 96 19 1.00
    rgba:
    159 82 25 1.00
    hex:
    #9F5219

    Download color

    Tools Used

    • D3.js – The D3.js library is well-suited to Sol LeWitt’s early works. D3’s foundational principle of joining data to elements is easy to apply to LeWitt’s symmetrical objects.
    • Textures.js – I’m using this library to quickly create the background patterns. It plays very well with D3.js, which is why I chose it. It is a great little library that is very easy to use. It has some limitations around the edges, but the alternative is writing my own SVG paths for the patterns, which I didn’t want to do at this time.

    Technical Details

    • I repurposed some of the data-generating code for the squares from my Let’s Make a Grid with D3.js post.
    • One svg element with two groups and 8 rect elements in each group.
    • Basic transition() and delay() functions applied to animate the layers.
    • Want to dig in a little further? Check out my implementation and view source. All of the D3 specs are there.

    Inspiration and References

    Detail shot of the version on display at MASSMoCA: Detail shot of the version on display at MASSMoCA:

    Wide shot of the version on display at MASSMoCA (Poor lighting): Wide shot of the version on display at MASSMoCA:

  • Sol LeWitt’s Wall Drawing 391 with D3.js


    Aren’t familiar with Sol LeWitt or his art? Read this.

    Wall Drawing 391: Two-part drawing. The two walls are each divided horizontally and vertically into four equal parts. First wall: 12-inch (30 cm) bands of lines in four directions, one direction in each part, drawn in black India ink. Second wall: Same, but with four colors drawn in India ink and color ink washes.

    391 is one of the iconic images that people instantly recognize as a Sol LeWitt. After making the simple line drawings, I wanted to try something more comfortable and a little more complex. Now that I’ve created this, I could translate it into #419. Perhaps I will do this in the future.

    Here is an image preview. Click on it to see the D3.js version:

    Open my D3 implementation of Wall Drawing 391 in a new window →

    Design Decisions

    • While any interpretation of Sol LeWitt’s instructions is technically valid as long as each statement is accounted for, I feel a responsibility to make my implementation beautiful, clean, and symmetrical.
    • Besides for this being a commonly recognized piece where it would be noticeable that I deviated from the norm, when I made this I was still unsure of myself. I wanted to see if I could translate what I saw in person to D3.js as closely as possible. I think it turned out pretty well.
    • When Sol LeWitt says “four colors”, the regular interpretation is black/dark gray, yellow, red, orange. When he says “four directions”, the common interpretation is 0 degrees, 90 degrees, 45 degrees, and 135 degrees.
    • Sol LeWitt dictated a 12-inch bands. Since inches are fuzzy on the web and regular screens aren’t as wide as walls, I scaled things down and translated it into pixels. I kept with the spirit and chose a multiple of 12: 36 pixel bands.
    • On a wall, the dimensions are dictated by the physical size of the wall itself. On the web, since windows change size so easily and the proportions of the art are constant, I chose to define a fixed size for this piece.

    Color Palette

    Onyx

    cmyk:
    78 72 66 74 1.00
    rgba:
    17 17 17 1.00
    hex:
    #111111

    Download color

    Sunglow

    cmyk:
    1 14 75 1 1.00
    rgba:
    252 202 66 1.00
    hex:
    #FCCA42

    Download color

    Alizarin Crimson

    cmyk:
    1 87 61 1 1.00
    rgba:
    223 34 62 1.00
    hex:
    #DF223E

    Download color

    Navy Blue

    cmyk:
    71 41 0 0 1.00
    rgba:
    16 105 213 1.00
    hex:
    #1069D5

    Download color

    Tools Used

    • D3.js – The D3.js library is well-suited to Sol LeWitt’s early works. D3’s foundational principle of joining data to elements is easy to apply to LeWitt’s symmetrical objects.
    • Textures.js – I’m using this library to quickly create the background patterns. It plays very well with D3.js, which is why I chose it. It is a great little library that is very easy to use. It has some limitations around the edges, but the alternative is writing my own SVG paths for the patterns, which I didn’t want to do at this time.

    Technical Details

    • I repurposed some of the data-generating code for the squares from my Let’s Make a Grid with D3.js post.
    • This is two SVGs with two groups each (one for each row) and two rect elements in each group (one for each quadrant). The only difference between the left and right SVG squares is the color.
    • The stripes are singular path fills in each square. There is only one pattern per quadrant here because this piece has simple, non-overlapping colors.
    • Want to dig in a little further? Check out my implementation and view source. All of the D3 specs are there.

    What I learned

    • There are tradeoffs to using libraries to generate patters for you. At the width of the squares I’ve specified here, the size of the pattern should be sqrt(2) times the size of the vertical one. When I run those calculations and set the diagonal patterns as such, the right one lines up but the left one is shifted too far up. I ended up finding a middle ground, but the stroke size of the bottom ones aren’t exactly 36 pixels wide. They are 37 pixels wide. That is the price I pay for using a library. I’ll learn to write my own SVG patterns in the future.
    • Many interpretations of Sol LeWitt’s directions (including ones he oversaw the installation of) are very geometric and symmetrical.

    Inspiration and References

    Wide shot of the version on display at MASSMoCA: Wide shot of the version on display at MASSMoCA:

  • Sol LeWitt’s Wall Drawing 11 with D3.js


    Aren’t familiar with Sol LeWitt or his art? Read this.

    Wall Drawing 11: A wall divided horizontally and vertically into four equal parts. Within each part, three of the four kinds of lines are superimposed.

    Wall Drawing 11 is remarkably close to Wall Drawing 56, which is the first one I implemented. I discovered that with only a few changes (width, height, and pattern direction) I could turn my code for 56 into 11.

    Open my D3 implementation of Wall Drawing 11 in a new window →

    Design Decisions

    • While any interpretation of Sol LeWitt’s instructions is technically valid as long as each statement is accounted for, I feel a responsibility to make my implementation beautiful, clean, and symmetrical.
    • There are other valid layouts of this piece (the patterns could be in different directions as long as you use three), but I decided to implement the popularly known configuration currently on display at MASSMoCA. I will stray from this path later on in the project.
    • I reused most of the foundation I laid with Wall Drawing 56. I chose to change the dimensions to a rectangle to better fill the page and to differentiate it. A rectangle also stays closer to the version of 11 at MASSMoCA than a square.

    Tools Used

    • D3.js – The D3.js library is well-suited to Sol LeWitt’s early works. D3’s foundational principle of joining data to elements is easy to apply to LeWitt’s symmetrical objects.

    • Textures.js – I’m using this library to quickly create the background patterns. It plays very well with D3.js, which is why I chose it. It is a great little library that is very easy to use. It has some limitations around the edges, but the alternative is writing my own SVG paths for the patterns, which I didn’t want to do at this time.

    Technical Details

    • I reused most of the foundation I laid with Wall Drawing 56.
    • I changed the width, height, and starting point of each quadrant to make a rectangle instead of a square.
    • Want to dig in a little further? Check out my implementation and view source. All of the D3 specs are there.

    What I learned

    • I really got the hang of setting up patterns in multiple directions here and configuring them such that they line up to create a design where the edges meet.
    • Dimensions matter. I started out with this as a square, but I think it fits normal browser windows much better as a rectangle.
    • Reuse bits of code if you can!

    Inspiration and References

    Detail shot of the version on display at MASSMoCA: Detail shot of the version on display at MASSMoCA

    Wide shot of the version on display at MASSMoCA: Wide shot of the version on display at MASSMoCA:

  • Sol LeWitt’s Wall Drawing 56 with D3.js


    Aren’t familiar with Sol LeWitt or his art? Read this.

    Wall Drawing 56: A square is divided horizontally and vertically into four equal parts, each with lines in four directions superimposed progressively.

    Wall Drawing 56 was the first of Sol LeWitt’s wall drawings I tried to implement here on the web. I picked it for its simplicity: A square with four sets of patterns and two colors. I wasn’t sure exactly what interpreting and implementing these would entail, so I wanted something simple and recognizable at first.

    Open my D3 implementation of Wall Drawing 56 in a new window →

    Design Decisions

    • While any interpretation of Sol LeWitt’s instructions is technically valid as long as each statement is accounted for, I feel a responsibility to make my implementation beautiful, clean, and symmetrical.
    • There are other valid layouts of this piece (the patterns could be in a different order depending on where you chose to start), but I decided to implement the popularly known configuration currently on display at MASSMoCA. I will stray from this path later on in the project.
    • On a wall, the dimensions are dictated by the physical size of the wall itself. On the web, since windows change size so easily and the proportions of the art are constant, I chose to define a fixed size for this piece.

    Tools Used

    • D3.js – The D3.js library is well-suited to Sol LeWitt’s early works. D3’s foundational principle of joining data to elements is easy to apply to LeWitt’s symmetrical objects.

    • Textures.js – I’m using this library to quickly create the background patterns. It plays very well with D3.js, which is why I chose it. It is a great little library that is very easy to use. It has some limitations around the edges, but the alternative is writing my own SVG paths for the patterns, which I didn’t want to do at this time.

    Technical Details

    • I repurposed some of the data-generating code for the squares from my Let’s Make a Grid with D3.js post.
    • I had to do a lot of trial-and-error on the width, height, starting points for each quadrant, and pattern size to make sure the patterns turned out clean and symmetrical. If you dig into the javscript, you’ll see odd variable definitions.
    • This is one SVG with two groups (one for each row) and two rect elements in each group (one for each quadrant).
    • The lines/patterns are singular path fills in each square. There is only one pattern per quadrant here because this piece is in black and white.
    • Want to dig in a little further? Check out my implementation and view source. All of the D3 specs are there.

    What I learned

    • In this first implementation I was getting my feet wet with Textures.js. I got a solid understanding of how to define and create patterns with it.
    • When my first cut was pretty rough around the edges (literally, because the patterns had odd spacing), I noticed how precise and clean the other versions I’ve seen are. I now pay special attention to the fine details when evaluating these pieces.

    Inspiration and References

    Detail shot of the version on display at MASSMoCA: Detail shot of the version on display at MASSMoCA

    Wide shot of the version on display at MASSMoCA: Wide shot of the version on display at MASSMoCA:

  • Smooth Pie Chart Transitions with D3.js


    Hey, so this post is broken. I moved platforms and some of my old tutorials don’t play nicely with WordPress. I’m working on fixing them, but in the meantime you can view the old version here: https://cagrimmett-jekyll.s3.amazonaws.com/til/2016/08/27/d3-transitions.html

    A few days ago we made a pie chart that updates in real time, but it has one issue: The update is jumpy. Let’s fix that.

    I’ll admit, unlike the other tutorials where I was able to figure out most of this on my own, I had to mine other examples to figure this out. I primarily followed Mike Bostock’s Pie Chart Update, II, but his commented Arc Tween example was extremely helpful in understanding what d3.interpolate is doing.

    What to do

    Note: We’re starting with code from my previous Pie Chart Update tutorial.

    First we need to store the beginning angle for each arc. You’ll recall that I’m using two separate arcs, one for the main chart and one for the labels. For about 10 minutes I was trying to figure out why the first update was jumpy but all subsequent ones were smooth. It turns out that we need to store the initial angles for each set of arcs:

    g.append("path") 	.attr("d", arc) 	.style("fill", function(d) { return color(d.data.letter);}) 	.each(function(d) { this._current = d; }); // store the initial angles;  g.append("text") 	.attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; }) 	.text(function(d) { return d.data.letter;}) 	.style("fill", "#fff") 	.each(function(d) { this._current = d; }); // store the initial angles;

    Next we need to write two arcTween functions to transition between the two. I followed Mike Bostock’s example for the first one, then adapted it to the label arc, too:

    function arcTween(a) {   var i = d3.interpolate(this._current, a);   this._current = i(0);   return function(t) {     return arc(i(t));   }; }  function labelarcTween(a) {   var i = d3.interpolate(this._current, a);   this._current = i(0);   return function(t) {     return "translate(" + labelArc.centroid(i(t)) + ")";   }; }

    Last we need to include these arcTween() functions into the change() function we wrote before. I commented out the previous updates so you can compare them. The duration is 1/2 a second:

    function change() { 	var pie = d3.pie() 		.value(function(d) { return d.presses; })(data); 	path = d3.select("#pie").selectAll("path").data(pie); 	//path.attr("d", arc); 	path.transition().duration(500).attrTween("d", arcTween); // Smooth transition with arcTween 	//d3.selectAll("text").data(pie).attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; }); 	d3.selectAll("text").data(pie).transition().duration(500).attrTween("transform", labelarcTween); // Smooth transition with labelarcTween }

    Here is is in action. As always, you can view source to see the fully integrated example:

  • Let’s Update a Pie Chart in Realtime with D3.js


    Last week we made a pie chart with D3.js. Today we are going to update it in realtime when buttons are clicked.

    Here is the basic pie chart again, slightly modified from the original (I only changed the letters):

    The key to making this work is D3’s object constancy. We already baked that into the original design by specifying a key function for the underlying presses count data.

    Setting Up

    First, we need to copy the pie chart we made last week. Make a few updates: change all the presses in data to 1 and change the letters to A, B, and C.

    Next, we need something to click so we can increment the count and update the chart. Three buttons will do nicely:

     class="buttons" style="width: 300px; text-align: center;"> 	 id="a" style="width:50px;">A 	 id="b" style="width:50px;">B 	 id="c" style="width:50px;">C 

    Recomputing the angles

    We ultimately want to call a specific update function when we click each button, so let’s write one. We need to consider what the most critical parts of making the original pie chart were so that we can recreate only the necessary steps:

    • Defining the value function for d3.pie
    • Computing the angles based on that data
    • Putting that information in a path so it can be displayed

    Remember that we have two arcs: The main pie and the one holding the labels. We need to update both!

    function change() { 	var pie = d3.pie() 		.value(function(d) { return d.presses; })(data); 	path = d3.select("#pie").selectAll("path").data(pie); // Compute the new angles 	path.attr("d", arc); // redrawing the path 	d3.selectAll("text").data(pie).attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; }); // recomputing the centroid and translating the text accordingly. }

    Updating the data and chart on click

    Now we need to update the underlying data and the whole chart on click:

    d3.select("button#a") 	.on("click", function() { 		data[0].presses++; 		change(); 	}) d3.select("button#b") 	.on("click", function() { 		data[1].presses++; 		change(); 	}) d3.select("button#c") 	.on("click", function() { 		data[2].presses++; 		change(); 	})

    Here is the result. Click the buttons and watch the chart change!

    Up next: Smooth transitions with d3.interpolate.

  • Let’s Make a Pie Chart with D3.js


    Hey, so this post is broken. I moved platforms and some of my old tutorials don’t play nicely with WordPress. I’m working on fixing them, but in the meantime you can view the old version here: https://cagrimmett-jekyll.s3.amazonaws.com/til/2016/08/19/d3-pie-chart.html

    This is part of my ongoing effort to relearn D3.js. This short tutorial applies what I’ve learned about data joins, arcs, and labels. It varies slightly from other examples like Mike Bostock’s Pie Chart block because I’m using D3.js version 4 and the API for arcs and pies is different from version 3, which most tutorials are based on. I figured this out through some trial and error. Always read the documentation!

    I’m eventually going to use this pie chart as a base to learn how to update charts in real time based on interactions like button pushes or clicks.

    Getting Started

    1. Always include the library:
    2. Make a div to hold the chart:

    3. D3 stands for Data-Driven Documents. So what do we always start with when creating something with D3? Data!

    Since I’m eventually going to figure out how to update this chart in real time based on button presses, I’m going to start with some dummy data for three easy-to-press buttons: q, w, and e. As always, I log it to the console for quick debugging:

    var data = [{"letter":"q","presses":1},{"letter":"w","presses":5},{"letter":"e","presses":2}]; console.log(data);

    We’ll also need some basics like width, height, and radius since we are dealing with a circle here. Make them variables so your code is reusable later:

    var width = 300, 	height = 300, 	// Think back to 5th grade. Radius is 1/2 of the diameter. What is the limiting factor on the diameter? Width or height, whichever is smaller 	radius = Math.min(width, height) / 2;

    Next we need a color scheme. Be referring to the API, we learn that we should use .scaleOrdinal() for this:

    var color = d3.scaleOrdinal() 	.range(["#2C93E8","#838690","#F56C4E"]);

    Setting up the pie and arcs

    We need to set up the pie based on our data. According to the documentation, d3.pie() computes the necessary angles based on data to generate a pie or doughnut chart, but does not make shapes directly. We need to use an arc generator for that.

    var pie = d3.pie() 	.value(function(d) { return d.presses; })(data);

    Before we create the SVG and join data with shapes, let’s define some arguments for the two arcs we want: The main arc (for the chart) and the arc to hold the labels. We need an inner and outer radius for each. If you change the inner radius to any number greater than 0 on the main arc you’ll get a doughnut.

    var arc = d3.arc() 	.outerRadius(radius - 10) 	.innerRadius(0);  var labelArc = d3.arc() 	.outerRadius(radius - 40) 	.innerRadius(radius - 40);

    Making the shapes

    We always start with an SVG. Select the div we created to hold the chart, append an SVG, give the SVG the attributes defined above, and create a group to hold the arcs. Don’t forget to move the center points, or else the chart will be centered in the upper right corner:

    var svg = d3.select("#pie") 	.append("svg") 	.attr("width", width) 	.attr("height", height) 		.append("g") 		.attr("transform", "translate(" + width/2 + "," + height/2 +")"); // Moving the center point. 1/2 the width and 1/2 the height

    Now let’s join the data generated by .pie() with the arcs to generate the necessary groups to hold the upcoming paths. Give them the class “arc”.

    var g = svg.selectAll("arc") 	.data(pie) 	.enter().append("g") 	.attr("class", "arc");

    Now we can append the paths created by the .arc() functions with the variables we defined above. We’re using the color variable we defined above to get the colors we want for the various arcs:

    g.append("path") 	.attr("d", arc) 	.style("fill", function(d) { return color(d.data.letter);});

    Once you save, you should see a chart. Now we’re cooking with data!

    Labels

    Let’s put some labels on it now. We’ll need to append some text tags in each arc, set the position with a transform defined by the labelArc variable we defined earlier, then access the correct letter to add to the label. Then we’ll make it white so it shows up a little better:

    g.append("text") 	.attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; }) 	.text(function(d) { return d.data.letter;}) 	.style("fill", "#fff");

    There you have it! A basic pie chart. Play around with the variables so you understand better what is going on.

  • Let’s Make a Grid with D3.js


    Hey, so this post is broken. I moved platforms and some of my old tutorials don’t play nicely with WordPress. I’m working on fixing them, but in the meantime you can view the old version here: https://cagrimmett-jekyll.s3.amazonaws.com/til/2016/08/17/d3-lets-make-a-grid.html

    I’ve been on a mission to relearn the fundamentals of D3.js from the ground up. This tutorial is a way to apply what I learned about data joins, click events, and selections. Along the way I learned about building arrays.

    I also wrote this up as a block for those interested.

    Basics

    We want to make a 10×10 grid using D3.js. D3’s strength is transforming DOM elements using data. This means we’ll need some data and we’ll want to use SVG and rect elements.

    Data

    We could write an array of data for the grid by hand, but we wouldn’t learn anything then, would we? Let’s generate one with Javascript.

    Picture a grid in your head. It is made up of rows and columns of squares. Since this is ultimately going to be represented by an SVG, let’s think about how an SVG is structured:

     	 		 		 		 	 	 		 		 		 	 	 		 		 		 	 

    What you see here is a basic structure of rows and columns. That means that when we make our data array, we want to make a nested array of rows and cells/columns inside those rows. We’ll need to use iteration to do this. Easy-peasy.

    The other question we’ll have when making these arrays is, “What attributes will this grid need?”. Think about how you’d draw a grid: You start in the upper right corner of a piece of paper, draw a 1×1 square, move over the width of 1 square and draw another, and repeat until you get to the end of the row. Then you’d go back to the first square, draw one underneath it, and repeat the process. Here we’ve described positions, widths, and heights. In SVG world these are x, y, width, and height.

    Here is the function I’m using to create the underlying data for the upcoming grid. It makes an array that holds 10 other arrays, which each hold 10 values:

    function gridData() { 	var data = new Array(); 	var xpos = 1; //starting xpos and ypos at 1 so the stroke will show when we make the grid below 	var ypos = 1; 	var width = 50; 	var height = 50; 	 	// iterate for rows	 	for (var row = 0; row < 10; row++) { 		data.push( new Array() ); 		 		// iterate for cells/columns inside rows 		for (var column = 0; column < 10; column++) { 			data[row].push({ 				x: xpos, 				y: ypos, 				width: width, 				height: height 			}) 			// increment the x position. I.e. move it over by 50 (width variable) 			xpos += width; 		} 		// reset the x position after a row is complete 		xpos = 1; 		// increment the y position for the next row. Move it down 50 (height variable) 		ypos += height;	 	} 	return data; }

    Making a Grid with D3 Data Joins

    We made a cool array above and now we’ll make the data correspond to svg:rect objects to make our grid. First we’ll need to make a div to append everything to (and, of course, don’t forget to include the latest version of D3 in your header):

     id="grid">

    Now we need to assign our data to a variable so we can access it:

    var gridData = gridData();	 // I like to log the data to the console for quick debugging console.log(gridData);

    Next, let’s append an SVG to the div we made and set its width and height attributes:

    var grid = d3.select("#grid") 	.append("svg") 	.attr("width","510px") 	.attr("height","510px");

    Next, we can apply what we learned in Mike Bostock’s Thinking With Joins to make our rows:

    var row = grid.selectAll(".row") 	.data(gridData) 	.enter().append("g") 	.attr("class", "row");

    And finally we make the individual cells/columns. Translating the data is a bit trickier, but the key is understanding that we are doing a selectAll on the rows, which means that any reference to data is to the contents of the single array that is bound to that row. We’ll then use a key function to access the attributes we defined (x, y, width, height):

    var column = row.selectAll(".square") 	.data(function(d) { return d; }) 	.enter().append("rect") 	.attr("class","square") 	.attr("x", function(d) { return d.x; }) 	.attr("y", function(d) { return d.y; }) 	.attr("width", function(d) { return d.width; }) 	.attr("height", function(d) { return d.height; }) 	.style("fill", "#fff") 	.style("stroke", "#222");

    You’ll note that I added style fill and stroke attributes to make the grid visible.

    When we put it all together, here is what we get:

    Note: If you are viewing this on your phone, you might want to switch over to a tablet or desktop. I haven’t optimized this example for mobile because that will needlessly complicate it.

    Cool, huh? Go ahead and inspect the element and marvel at your find handiwork. Then change the fill, stroke, width, and height attributes and see how it changes.

    Adding Click Functions

    Let’s have some fun and add click events to the individual cells. I want to have cells turn blue on the first click, orange on the second, grey on the third, and white again on the fourth. Since D3 is data-driven, we’ll need to add some click data to the arrays and then add functions to change it and set colors based on the number of clicks.

    //add this to the gridData function var click: 0;  //add this to the cell/column iteration data.push click: click  //add this to var column = row.selectAll(".square") .on('click', function(d) {        d.click ++;        if ((d.click)%4 == 0 ) { d3.select(this).style("fill","#fff"); } 	   if ((d.click)%4 == 1 ) { d3.select(this).style("fill","#2C93E8"); } 	   if ((d.click)%4 == 2 ) { d3.select(this).style("fill","#F56C4E"); } 	   if ((d.click)%4 == 3 ) { d3.select(this).style("fill","#838690"); }     })

    Let’s break down that on('click') function:

    • When you click on a cell, it increases the click variable (originally set at 0) by 1.
    • The if statements set the color based on how many times it has been clicked mod 4. This satisfies the UI of only having four states: white, blue, orange, and grey. If you go to your console and call up the data for a certain cell, you’ll see that the full number of clicks is available.

    Here it is. Click away!

    Randomized click counts

    What happens when we randomize click counts when we create the data array?

    //add this to the gridData function var click: 0;  //add this to the cell/column for loop, just above the data.push line click = Math.round(Math.random() * 100);  //add this to var column = row.selectAll(".square") .style("fill", function(d) { 		if ((d.click)%4 == 0 ) { return "#fff"; } 		if ((d.click)%4 == 1 ) { return "#2C93E8"; } 		if ((d.click)%4 == 2 ) { return "#F56C4E"; } 		if ((d.click)%4 == 3 ) { return "#838690"; } 	})

    Note that Math.random() returns a number between 0 and 1, inclusive. Multiple that by 100 if you want a number between 1 and 100.

    It changes when you refresh!

    Mouseovers: Even more fun with a bigger grid

    What happens when we change the click event to a mouseover event and make a bigger grid? It becomes a lot more fun. I’ll leave the implementation as an exercise to the reader. If you’ve been following along and writing this yourself instead of copying and pasting, you probably already know which variables and events to change:

  • Fun with Circles in D3


    These are my outputs from Mike Bostock’s Three Little Circles Tutorial, slightly modified so I could understand how each of these items work. This is a tutorial about selections and basic data joins.

    We start with three basic SVG circles:

     height="50px" width="250px">    cx="40" cy="20" r="10">    cx="80" cy="20" r="10">    cx="120" cy="20" r="10"> 


    Now let’s select them and make them a little bigger and orange!

    var circle = d3.selectAll("svg#orange circle") 	.style("fill", "darkorange") 	.attr("r", 20);


    Now how about making them light blue and having a randomly-generated height that resets every second?

    function jump(){ var circle = d3.selectAll("svg#random-height circle") 	.style("fill", "lightblue") 	.attr("cy", function() { return Math.random() * 150 + 10;}); } jump(); setInterval(jump, 1000);

    That’s cool!


    Now let’s do some data joins. How about making the radius of the circle a function of a data set?

    var circle = d3.selectAll("svg#data-radius circle") 	.data([2, 3, 4]) 	.attr("r", function(d){ return d*d; });

    Looks like the radius is a square of the data. Go ahead and inspect the element to confirm!

    You’ll notice that the function uses the name d to refer to bound data.


    Let’t make these purple for variety and write a linear function to space them out horizontally.

    var circle = d3.selectAll("svg#data-cx circle") 	.style("fill","purple") 	.data([2, 3, 4]) 	.attr("cx", function(d,i){ return i*100 + 70});

    This uses a new function: The index of the element within its selection. The index is often useful for positioning elements sequentially. We call it i by convention.

  • D3 Intro and Joins Notes


    I’m relearning D3.js. Here are notes from different resources I’m reading.

    Notes from the D3js.org Introduction.

    Introduction

    • D3.js is a javascript library for manipulating documents based on data. It uses HTML, SVG, and CSS to do so. It emphasizes web standards so that you can use modern browsers without proprietary frameworks.
    • You can bind data to a DOM (Document Object Model) and apply data-driven transformations to the document.
      • Example: Generate an HTML table from an array of numbers, then use the same data to make an SVG bar chart.

    Selections

    • D3 makes it a lot easier to modify documents than the W3C DOM API. The W3C method relies on verbose names and manual iteration. D3 employs a declarative approach that operates on arbitrary nodes called selections.

    Here is Mike Bostock’s examples for how to change the text color of paragraph elements with the W3C method vs D3 method:

    W3C:

    var paragraphs = document.getElementsByTagName("p"); for (var i = 0; i < paragraphs.length; i++) {   var paragraph = paragraphs.item(i);   paragraph.style.setProperty("color", "white", null); }

    D3:

    d3.selectAll("p").style("color", "white");
    • Elements may be selected using a variety of predicates, including containment, attribute values, class and ID.
    • D3 provides multiple ways to change nodes: Setting attributes and styles, registering event listeners, adding/removing/sorting nodes, and changing HTML or text content.
    • Direct access to the DOM is possible because each D3 selection is a simple array of nodes.

    Dynamic Properties

    • D3 has similar syntax to jQuery, but styles, attributes, and other properties can be specified as functions of data in D3, not just constants.
    • D3 provides built-in reusable functions and function factories, such as graphical primitives for area, line and pie charts

    To alternate shades of gray for even and odd nodes:

    d3.selectAll("p").style("color", function(d, i) {   return i % 2 ? "#fff" : "#eee"; });
    • Computed properties often refer to bound data. Data is specified as an array of values, and each value is passed as the first argument (d) to selection functions. With the default join-by-index, the first element in the data array is passed to the first node in the selection, the second element to the second node, and so on. For example, if you bind an array of numbers to paragraph elements, you can use these numbers to compute dynamic font sizes:
    d3.selectAll("p")   .data([4, 8, 15, 16, 23, 42])     .style("font-size", function(d) { return d + "px"; });
    • Once the data has been bound to the document, you can omit the data operator. D3 will retrieve the previously-bound data. This allows you to recompute properties without rebinding.

    Enter and Exit

    • With D3’s enter and exit selections you can create new nodes for incoming data and remove outgoing nodes that are no longer needed.
    • When data is bound to a selection, each element in the data array is paired with the corresponding node in the selection. If there are fewer nodes than data, the extra data elements form the enter selection, which you can bring to life by using the .enter() selection. Example:
    d3.select("body")   .selectAll("p")   .data([4, 8, 15, 16, 23, 42])   .enter().append("p")     .text(function(d) { return "I’m number " + d + "!"; });
    • Updating nodes are the result of the data operator. If you forget the enter and exit selections, you will automatically select only the elements for which there exists corresponding data.
    • A common pattern is to break the initial selection into three parts: Updating nodes to modify, entering the nodes to add, and exiting the nodes to remove. Example:
    // Update… var p = d3.select("body")   .selectAll("p")   .data([4, 8, 15, 16, 23, 42])     .text(function(d) { return d; });  // Enter… p.enter().append("p")     .text(function(d) { return d; });  // Exit… p.exit().remove();
    • By handling the three cases (update, enter, exit) separately, you control precisely which operations run on which nodes.
    • D3 allows you to transform documents based on data. This includes creating and destroying elements.
    • D3 allows you to change an existing document in response to user interaction, animation over time, or even asynchronous inputs from a third-party. A hybrid approach is also possible, where the document is initially rendered on the server and updated on the client via D3.

    Transformation, not Representation

    • D3 does not introduce a new visual representation. Instead, its graphical marks come from web standards: HTML, SVG, and CSS. If browsers introduce new features tomorrow, you can use them immediately with D3, no update required.
    • D3 is easy to debug with the browser’s built-in element inspector. The nodes D2 manipulates are the same ones the browser uses natively.

    Transitions

    • D3’s transformation focus extends to animated transitions, too. Transitions interpolate styles and attributes over time. The time between can be controlled with easing functions.
    • D3’s interpolators support primitives (numbers and numbers within strings like font size, etc) and compound values. They are also extendable.
    • Examples:

    To fade the background of the page to black:

    d3.select("body").transition()     .style("background-color", "black");

    Or, to resize circles in a symbol map with a staggered delay:

    d3.selectAll("circle").transition()     .duration(750)     .delay(function(d, i) { return i * 10; })     .attr("r", function(d) { return Math.sqrt(d * scale); });
    • D3 allows you to modify only the elements that change, which reduces overhead and allows more complexity at high frame rates.
    • D3 allows sequencing of complex transitions via events
    • D3 does not replace the browser’s toolbox, but instead exposes it and makes it easier to use. You can still use CSS transitions.

    Notes from Thinking with Joins by Mike Bostock

    • D3 has no primitive for creating multiple DOM elements. The .append() method can create single elements, but if you want multiple, you need to think in a different way.
    • Instead of telling D3 how to do something, tell D3 what you want. For example, on the Thinking with Joins page, Mike uses this snippet to tell D3 that the selection “circle” should correspond to data points. This concept is called a data join:
    svg.selectAll("circle")   .data(data)   .enter().append("circle")     .attr("cx", function(d) { return d.x; })     .attr("cy", function(d) { return d.y; })     .attr("r", 2.5);

    Data Enter Update Elements Exit

    Data points joined to the existing elements produce the update (inner) selection. Leftover unbound data produce the enter (left) selection, which represents missing elements. Any remaining unbound elements produce the exit (right) selection, which represents elements to be removed.

    Now let’s explain the svg.selectAll("circle"):

    1. First it returns an empty selection since the SVG container was empty. The parent node of the selection was the SVG container.
    2. The selection is joined to a data array, resulting in three new selections that represent three possible states: enter, update, or exit. Since the selection was empty, the update and exit selections are empty and the enter selection contains a placeholder for each new data point.
    3. The update selection is returned by selection.data and the enter and exit selections hang off the update selection. selection.enter returns the enter selection.
    4. The missing elements are added to the SVG container by calling selection.append on the enter selection. This appends a new circle for each data point in the SVG container.
    • Thinking with joins means declaring a relationship between a selection (such as a “circle”) and data, then implementing this relationship through the three enter, update, and exit states.
    • For static visualizations, the enter selection is sufficient. But you can support dynamic visualizations with minor modifications to update and exit.
    • If a given enter, update, or exit selection happens to be empty, the corresponding code does not operate.
    • Joins let you target operations to specific states. For example, you can set constant attributes (such as the circle’s radius, defined by the “r” attribute) on enter rather than update. By reselecting elements and minimizing DOM changes, you vastly improve rendering performance!
    • Similarly, it allows you to target animations to specific states like expanding circles as the come in and contract circles as they go out.
  • Relearning D3.js


    I was listening to Data Stories Episode 22: NYT Graphics and D3 with Mike Bostock and Shan Carter today and I realized that while I’ve used D3.js for bar charts and mapping projections, I don’t really understand it. I never took the time to learn the fundamentals, so I’ve always been constrained to cobbling together things from other examples.

    That needs to change.

    Here is a list of resources I plan to go through. I should be able to relearn the basics fairly quickly so that I can spend time making charts of my own. I’ll update completed items with check marks as I go through them.

  • Responsive D3.js bar chart with labels


    Hey, so this post is broken. I moved platforms and some of my old tutorials don’t play nicely with WordPress. I’m working on fixing them, but in the meantime you can view the old version here: https://cagrimmett-jekyll.s3.amazonaws.com/til/2016/04/26/responsive-d3-bar-chart.html

    Today I learned some cool stuff with D3.js!

    Here is a minimalist responsive bar chart with quantity labels at the top of each bar and text wrapping of the food labels. It is actually responsive, it doesn’t merely scale the SVG proportionally, it keeps a fixed height and dynamically changes the width.

    For simplicity I took the left scale off. All bars are proportional and are labeled anyway.

    Go ahead and resize your window! This has a minimum width of about 530px because of the text labels. Any smaller than that and they are very difficult to read.

    The basic HTML

     id ="chartID"> 

    The Styles

    You’ll see that the axis is actually there but it is white. I found it useful to learn to draw it, but I didn’t want it so I am keeping it hidden.

    .axis path, .axis line {     fill: none;     stroke: #fff;   } .axis text {   	font-size: 13px;   } .bar {     fill: #8CD3DD;   } .bar:hover {     fill: #F56C4E;   } svg text.label {   fill:white;   font: 15px;     font-weight: 400;   text-anchor: middle; } #chartID { 	min-width: 531px; }

    The Data

    var data = [{"food":"Hotdogs","quantity":24},{"food":"Tacos","quantity":15},{"food":"Pizza","quantity":3},{"food":"Double Quarter Pounders with Cheese","quantity":2},{"food":"Omelets","quantity":30},{"food":"Falafel and Hummus","quantity":21},{"food":"Soylent","quantity":13}]

    The Javascript Heavy Lifting

    This is where D3 really comes in.

    1. Setting the margins, sizes, and figuring out the basic scale.
    2. Setting the axes
    3. Drawing the basic SVG container with the proper size and margins
    4. Scaling the axes
    5. Drawing the bars themselves
    var margin = {top:10, right:10, bottom:90, left:10};  var width = 960 - margin.left - margin.right;  var height = 500 - margin.top - margin.bottom;  var xScale = d3.scale.ordinal().rangeRoundBands([0, width], .03)  var yScale = d3.scale.linear()       .range([height, 0]);   var xAxis = d3.svg.axis() 		.scale(xScale) 		.orient("bottom");               var yAxis = d3.svg.axis() 		.scale(yScale) 		.orient("left");  var svgContainer = d3.select("#chartID").append("svg") 		.attr("width", width+margin.left + margin.right) 		.attr("height",height+margin.top + margin.bottom) 		.append("g").attr("class", "container") 		.attr("transform", "translate("+ margin.left +","+ margin.top +")");  xScale.domain(data.map(function(d) { return d.food; })); yScale.domain([0, d3.max(data, function(d) { return d.quantity; })]);   //xAxis. To put on the top, swap "(height)" with "-5" in the translate() statement. Then you'll have to change the margins above and the x,y attributes in the svgContainer.select('.x.axis') statement inside resize() below. var xAxis_g = svgContainer.append("g") 		.attr("class", "x axis") 		.attr("transform", "translate(0," + (height) + ")") 		.call(xAxis) 		.selectAll("text"); 			 // Uncomment this block if you want the y axis /*var yAxis_g = svgContainer.append("g") 		.attr("class", "y axis") 		.call(yAxis) 		.append("text") 		.attr("transform", "rotate(-90)") 		.attr("y", 6).attr("dy", ".71em") 		//.style("text-anchor", "end").text("Number of Applicatons");  */   	svgContainer.selectAll(".bar")   		.data(data)   		.enter()   		.append("rect")   		.attr("class", "bar")   		.attr("x", function(d) { return xScale(d.food); })   		.attr("width", xScale.rangeBand())   		.attr("y", function(d) { return yScale(d.quantity); })   		.attr("height", function(d) { return height - yScale(d.quantity); });

    Adding the quantity labels to the top of each bar

    This took me a while to figure out because I was originally appending to the rect element. According to the SVG specs this is illegal, so I moved on to appending them after everything else to they’d show on top. The positioning is tricky, too. I eventually found the correct variables to position it close to center. Then text-anchor: middle; sealed the deal.

    // Controls the text labels at the top of each bar. Partially repeated in the resize() function below for responsiveness. 	svgContainer.selectAll(".text")  		 	  .data(data) 	  .enter() 	  .append("text") 	  .attr("class","label") 	  .attr("x", (function(d) { return xScale(d.food) + xScale.rangeBand() / 2 ; }  )) 	  .attr("y", function(d) { return yScale(d.quantity) + 1; }) 	  .attr("dy", ".75em") 	  .text(function(d) { return d.quantity; });   	  

    Responsiveness

    The general method for making D3 charts responsive is to scale the SVG down proportionally as the window gets smaller by manipulating the viewBox and preserveAspectRatio attributes. But after digging around on Github for a while, I found a fancier solution that preserves the height and redraws the SVG as the width shrinks.

    document.addEventListener("DOMContentLoaded", resize); d3.select(window).on('resize', resize);   function resize() { 	console.log('----resize function----');   // update width   width = parseInt(d3.select('#chartID').style('width'), 10);   width = width - margin.left - margin.right;    height = parseInt(d3.select("#chartID").style("height"));   height = height - margin.top - margin.bottom; 	console.log('----resiz width----'+width); 	console.log('----resiz height----'+height);   // resize the chart        xScale.range([0, width]);     xScale.rangeRoundBands([0, width], .03);     yScale.range([height, 0]);      yAxis.ticks(Math.max(height/50, 2));     xAxis.ticks(Math.max(width/50, 2));      d3.select(svgContainer.node().parentNode)         .style('width', (width + margin.left + margin.right) + 'px');      svgContainer.selectAll('.bar')     	.attr("x", function(d) { return xScale(d.food); })       .attr("width", xScale.rangeBand());           svgContainer.selectAll("text")  		 	 // .attr("x", function(d) { return xScale(d.food); }) 	 .attr("x", (function(d) { return xScale(d.food	) + xScale.rangeBand() / 2 ; }  ))       .attr("y", function(d) { return yScale(d.quantity) + 1; })       .attr("dy", ".75em");   	            svgContainer.select('.x.axis').call(xAxis.orient('bottom')).selectAll("text").attr("y",10).call(wrap, xScale.rangeBand());     // Swap the version below for the one above to disable rotating the titles     // svgContainer.select('.x.axis').call(xAxis.orient('top')).selectAll("text").attr("x",55).attr("y",-25);     	     }

    Wrapping text labels

    Wrapping text labels is tricky. The best solution I found is the one Mike Bostock (D3’s creator) describes. I modified it slightly to work with my chart, but the overall solution is the same.

    function wrap(text, width) {   text.each(function() {     var text = d3.select(this),         words = text.text().split(/s+/).reverse(),         word,         line = [],         lineNumber = 0,         lineHeight = 1.1, // ems         y = text.attr("y"),         dy = parseFloat(text.attr("dy")),         tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");     while (word = words.pop()) {       line.push(word);       tspan.text(line.join(" "));       if (tspan.node().getComputedTextLength() > width) {         line.pop();         tspan.text(line.join(" "));         line = [word];         tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);       }     }   }); }