2. SVG
Basic Shapes
Paths
Javascript vs. D3js
Creating a Bar Graph
Scaling
What Next?
3. SVG (ScalableVector Graphics)
Scalable images
Open standard
Files saved as standard XML
Need to have good command of Javascript
D3js is a library written in javascript
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8">
D3js makes it easy to manipulate SVG
graphics
4. D3 = Data Driven Documents
Produce interactive visualizations (web)
Has several libraries to help with creating
graphics
Focus on the visualization part (not the code)
Support for binding data
5. Selections
CSS-style selector to select DOM nodes
Manipulations (append, remove, etc.)
Transitions
“smoothly” interpolate attributes of graphic, creating
an aesthetically pleasing visual
Better than recreating each element
Data-binding
Use a dataset (json, csv, javascript arrays, etc.) to
allow D3 to create an object for each element
6. Based on a coordinate system with (0,0) at
top left
Basic shapes include
Lines, Rectangles, Circles, and Polygons
Paths allow highly customized polygons
Using basic shapes to create interactive
charts like bar charts, area charts, pie charts
More Advanced creations are possible
8. d3.select("body") // select <body> tag
.append("svg") // add <svg> tag
.attr("width", "100") // width of svg, use default for height
.append("rect") // add <rect> element
.attr("width", 50) // set attribute of rect
.attr("height", 150) // set attribute of rect
.attr("x", 25) // move 25 px from left of svg
.attr("y", 25) // move 25 px from top of svg
.style("fill", "blue"); // add some style
9. d3.select("body") // select <body> tag
.append("svg") // add <svg> tag
.attr("width", "100")
.append("circle") // add <circle> element
.attr("cx", 50) // move center of circle 50 px from left of svg
.attr("cy", 50) // move center of circle 50 px from top of svg
.attr("r", 25) // radius of circle
.style("fill", "blue"); // add some style
10. //The data for our line; simply an array of x,y coordinates for each line segment
var lineData = [ { "x": 1, "y": 5}, { "x": 20, "y": 20},
{ "x": 40, "y": 10}, { "x": 60, "y": 40},
{ "x": 80, "y": 5}, { "x": 100, "y": 60}];
//This accessor function will use the x,y coordinates and create a path of points
var lineFunction = d3.svg.line() // we’ll revisit this later
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("linear"); // we’ll revisit this later
//The SVG Container
var svgContainer = d3.select("body").append("svg")
.attr("width", 200)
.attr("height", 200);
//The line SVG Path we draw
var lineGraph = svgContainer.append("path")
.attr("d", lineFunction(lineData)) // concept of dynamic property; calls the lineFunction
defined above
.attr("stroke", "blue")
.attr("stroke-width", 2)
.attr("fill", "none");
Interpolate(“linear”)
Interpolate(“step-before”)
14. D3.js includes a set of Path Data Generators helper
classes for generating SVG Path instructions.
d3.svg.line - create a new line generator
d3.svg.line.radial - create a new radial line generator
d3.svg.area - create a new area generator
d3.svg.area.radial - create a new radial area generator
d3.svg.arc - create a new arc generator
d3.svg.symbol - create a new symbol generator
d3.svg.chord - create a new chord generator
d3.svg.diagonal - create a new diagonal generator
d3.svg.diagonal.radial - create a new radial diagonal
generator
15. var arc = d3.svg.arc() // What does this do?
.innerRadius(20)
.outerRadius(100)
.startAngle(0)
.endAngle(Math.PI) ;
// Angles are specified in radians;
0 corresponds to 12 o’clock (negative y)
and proceeds clockwise, repeating at 2π
var chart =
d3.select("body").append("svg:svg")
.attr("width", 200)
.attr("height", 250).append("svg:g")
.attr("transform", "translate(50,150)") ;
chart.append("svg:path")
.attr("fill", "green")
.attr("d", arc) ;
endAngle(Math.PI)
endAngle(0.5* Math.PI)
16. Where are we?
We know about SVG shapes
We know a little bit about D3js
We can create different shapes using D3js syntax
What haven’t we done?
No Data examples
No interactive examples (can’t show much in ppt)
18. Greater flexibility with more complex data
Handles binding automatically
Easier transitions
grp.attr("transform", "translate(20,20)");
Moves the entire group 20x20
19. A little tough to understand at first
Makes sense after reviewing examples
General Pattern:
// Update…
var p = d3.select("body").selectAll("p")
.data([4, 8, 15, 16, 23, 42]) // array of numbers
.text(String);
// Enter… this will be called for each data element
p.enter().append("p")
.text(String);
// Exit…
p.exit().remove();
20. // Update…
var p = d3.select("body").selectAll("p")
.data([4, 8, 15, 16, 23, 42])
// join “p” with data items
.text(function(d){
console.log(d);
return (d*d).toString();
});
Notice we can call a function
to get the data element
Since there are no “p” tags in
the body, nothing is displayed
No log information is created
<body>
</body>
html output
21. var p = d3.select("body").selectAll("p")
.data([4, 8, 15, 16, 23, 42])
.text(function(d){
console.log(d);
return d.toString();});
p.enter().append("p")
.text(function(d) {
console.log ("value of d is: " + d.toString());
return (d*2).toString();});
Notice the console output (original
values) and the eventual data that’s
rendered (value doubled)
Essentially, create a “p” tag for each
data element
<body>
<p>8</p><p>16</p><p>30</p>
<p>32</p><p>46</p><p>84</p>
</body>
console.log output
html output
22. This time the html body tag has
some <p> tags defined like this:
<body>
<p>this is p1</p>
<p>this is p2</p>
<p>this is p3</p>
</body>
Here’s the D3 “update” code:
var p = d3.select("body").selectAll("p")
.data([4, 8, 15, 16, 23, 42])
.text(function(d){
console.log("d in update:" + d.toString());
return d.toString();
});
Notice the original <p> text has
been replaced
<body>
<p>4</p>
<p>8</p>
<p>15</p>
</body>
html output
console.log output
23. The html body tag has some
<p> tags defined like this:
<body>
<p>this is p1</p>
<p>this is p2</p>
<p>this is p3</p>
</body>
Here’s the D3 Enter code:
p.enter().append("p")
.text(function(d) {
console.log ("value of d is: " + d.toString());
return (d*2).toString();});
Notice the last three <p> tags
have the doubled value
<body>
<p>4</p>
<p>8</p>
<p>15</p>
<p>32</p>
<p>46</p>
<p>84</p>
</body>
html output
console.log output
24. var data = [4, 8, 15, 16, 23, 42];
function createSomething(){
var p = d3.select("body").selectAll("p")
.data(data)
.text(function(d){
console.log("d in update:" + d.toString());
return d.toString();});
p.enter().append("p")
.text(function(d) {
console.log ("value of d is: " + d.toString());
return (d*2).toString();})
// attach click event to remove an element
.on("click", function(d, i){
console.log(“onclick d, i: “ + d + “, “ + i);
data.splice(d, 1); // let’s remove a value
from data
createSomething();
});
// Exit ...
p.exit().remove();
}
createSomething();
Clicked on 46.What happened
to 32 and 84?
From the
“update”
From the
Enter
selection
The
onclick
removed
an item
from the
array
When data is updated (removed), one
of the <p> is also removed. Notice
that the values are not doubled.
That’s because the update selection
was executed, not Enter. The log
output confirms that.
25. // what would happened if we added an element ???
var data = [4, 8, 15, 16, 23, 42];
function createSomething(){
var p = d3.select("body").selectAll("p")
.data(data)
.text(function(d){
console.log("d in update:" + d.toString());
return d.toString();});
p.enter().append("p")
.text(function(d) {
console.log ("value of d is: " + d.toString());
return (d*2).toString();})
.on("click", function(){
console.log(“addding 10 to data“);
// add the number 10 to the top of array
d.splice(0,0, 10);
createSomething();
});
// Exit ...
p.exit().remove();
}
createSomething();
Notice that the new value was joined
with the first <p>; the first 6 <p> were
updated, and the 7th was inserted
(that’s why it has the double value)Clicked on 46.What happened
to 32?
26. // what would happened if we added an
element ???
var data = [4, 8, 15, 16, 23, 42];
function createSomething(){
var p = d3.select("body").selectAll("p")
.data(data)
.text(function(d){
console.log("d in update:" + d.toString());
return d.toString();});
p.enter().append("p")
.text(function(d) {
console.log ("value of d is: " + d.toString());
return (d*2).toString();})
.on("click", function(){
console.log(“addding 10 to data“);
// add the number 10 to the bottom of array
d.splice(4,0, 10);
createSomething();
});
// Exit ...
p.exit().remove();
}
createSomething();
What will be displayed
when you click on 46?
Look at the log output.
27. Understanding the Update, Enter, Exit
selections is critical
Now we know about SVG graphicsAND
D3 Scripting (a little bit) AND
Selections
So now you are only bound by your
imagination (and a bit of geometry)
28. A Bar chart is simply a set of rectangles
Let’s use D3 to create some rectangles
Here’s something that looks like a bar chart
with 5 rectangles
29. <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
<script
src="http://d3js.org/d3.v3.min.js"></script>
<script>
<!– on right side -->
</script>
</head>
<body>
</body>
</html>
var w = 300;
var h = 100;
var padding = 2;
var data = [15, 10, 50, 20, 25];
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect") // select all rect
.data(data) // our data
.enter() // cross your fingers
.append("rect") // it will append rect
.attr("x", function(d,i) {
return i*(w/dataset.length);
}) // set x … d is data and i is index of data
.attr("y", function(d, i){
return h-(d);
}) // set y … remember y start in top-left corner
.attr("width", w/dataset.length - padding)
.attr("height", function(d) {
return d;
});
30. var w = 300;
var h = 200;
var padding = 2;
var dataset = [15, 10, 45, 20, 25];
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
function getColor(v) {
if (v<=20) {return "#666666"; }
else if(v<40) {return "#FF5010";}
else {return "#FF0000";}
}
svg.selectAll("rect") // select all rect
.data(dataset) // our data
.enter() // will append if doesn't exist
.append("rect") // it will append rect
.attr("x", function(d,i) {
return i*(w/dataset.length);
})
.attr("y", function(d, i){
return h-(d*4);
})
.attr("width", w/dataset.length - padding)
.attr("height", function(d) {
return d*4;
})
.attr("fill", function(d) {return getColor(d);});
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {return d;})
.attr({
"text-anchor": "middle",
x: function(d,i){
return i*(w/dataset.length)+(w/dataset.length - padding) /2;
},
y: function(d) {return h-(d*4)+14;}, // drop it inside the bar
"font-family": "sans-serif",
"font-size": 12,
"fill": "#ffffff"
});
31. var w = 300, //width
h = 300, //height
r = 100, //radius
color = d3.scale.category20c(); //builtin range of colors
data = [{
"label": "one",
"value": 20
}, {
"label": "two",
"value": 50
}, {
"label": "three",
"value": 30
}];
var vis = d3.select("body")
.append("svg:svg") //create the SVG element inside the <body>
.data([data]) //associate our data with the document
.attr("width", w) //set the width and height of our visualization (these will be attributes of the <svg> tag
.attr("height", h)
.append("svg:g") //make a group to hold our pie chart
.attr("transform", "translate(" + r + "," + r + ")"); //move the center of the pie chart from 0, 0 to radius,
radius
var arc = d3.svg.arc() //this will create <path> elements for us using arc data
.outerRadius(r);
var pie = d3.layout.pie() //this will create arc data for us given a list of values
.value(function(d) {
return d.value;
}); //we must tell it out to access the value of each element in our data array
var arcs = vis.selectAll("g.slice") //this selects all <g> elements with class slice (there aren't any yet)
.data(pie) //associate the generated pie data (an array of arcs, each having startAngle, endAngle and
value properties)
.enter() //this will create <g> elements for every "extra" data element that should be associated with a
selection. The result is creating a <g> for every object in the data array
.append("svg:g") //create a group to hold each slice (we will have a <path> and a <text> element
associated with each slice)
.attr("class", "slice"); //allow us to style things in the slices (like text)
arcs.append("svg:path")
.attr("fill", function(d, i) {
return color(i);
}) //set the color for each slice to be chosen from the color function defined above
.attr("d", arc); //this creates the actual SVG path using the associated data (pie) with the arc drawing
function
arcs.append("svg:text") //add a label to each slice
.attr("transform", function(d) { //set the label's origin to the center of the arc
//we have to make sure to set these before calling arc.centroid
d.innerRadius = 0;
d.outerRadius = r;
return "translate(" + arc.centroid(d) + ")"; //this gives us a pair of coordinates like [50, 50]
})
.attr("text-anchor", "middle") //center the text on it's origin
.text(function(d, i) {
return data[i].label;
}); //get the label from our original data array
32. Scaling is important for creatingAxis
Can also be used to resize graphics
Use d3.scale.linear() to create scale
var scale = d3.scale.linear()
.domain([130,350]) // min/max
.range([10,100]); // output range;
// now use the scale object to get scaled value
console.log(scale(130)); // 10
console.log(scale(350)); // 100
console.log(scale(300)); // 79.54
console.log(scale(150)); // 18.18
33. Transitions deal with animation; Not possible
to show in a presentation (see demos later)
A little ironic that we can’t show transitions in ppt
Very Powerful feature of D3
A very simply transition:
d3.select("body") .style("color", "green") // make the body green
.transition() .style("color", "red"); // then transition to red
https://www.youtube.com/watch?v=vLk7mlAtEXI
http://bl.ocks.org/mbostock/3943967
36. https://github.com/mbostock/d3/wiki/Gallery
Source code available for all examples
Copy source and play with it in a javascript
environment (jsfiddle.net, jsbin.com, etc.)
Use debugger and console.log liberally to
better understand what’s happening
37. Debugging in javascript
Examples from today
Examples from NYTimes.com
Examples from around the Internet