If you are confused by the tornado server feel free to check out the *Learn Tornado* series. Enjoy!
This is the template we will be using. These pieces are all HTML.
# <!DOCTYPE html>
# <head>
# <title>{{ escape(main_title) }}</title>
# <script src="http://d3js.org/d3.v3.min.js"></script>
# </head>
#
# <style type="text/css">
# <!-- Style code goes here -->
# </style>
#
# <body>
#
# <div>
# <h2>{{ escape(page_title) }}</h2>
# <br>
# </div>
#
# <div>
# {% block content %}
# {% end block %}
# </div>
#
#</body>
#</html>
This code gives the web page a dynamic title.
# <title>{{ escape(main_title) }}</title>
Here we load the D3 library from the web.
# <script src="http://d3js.org/d3.v3.min.js"></script>
This code gives the web page a dynamic heading.
# <h2>{{ escape(page_title) }}</h2>
The second DIV section will be where our D3 code will reside
# <div>
# {% block content %}
# <!-- D3 code goes here -->
# {% end block %}
# </div>
# {% extends "template.html" %}
# {% block content %}
#
# {% end block %}
This code simply imports the template we created earlier
# {% extends "template.html" %}
Any code we place here will go on the second DIV of our template. In this example we did not add any code.
# {% block content %}
#
# {% end block %}
All of our D3 code will go between out script tags. From here on out, I will be ommiting the extends and block code to save space.
# {% extends "template.html" %}
# {% block content %}
#
# <script type="text/javascript">
# // D3 code goes here
# </script>
#
# {% end block %}
# <script type="text/javascript">
#
# // Create Global Variable
# var dataset;
#
# // Add data to variable
# dataset = [1,2,3,4,5,6,7,8,9,10]
#
# // Print to console
# console.log(dataset)
#
# </script>
This is how we comment out lines.
# // Anything past the two slashes will be ignored
This is how we declare variables.
# // Create Global Variable
# var dataset;
This is how we set variables.
# // Add data to variable
# dataset = [1,2,3,4,5,6,7,8,9,10]
This is the equivalent to *print. If you are in chrome, right click on web page and click on inspect element, then go to console* and expand Array[10].
# // Print to console
# console.log(dataset)
Here we load the csv file. After the csv file is loaded the function is called. This function sets the variable *dataset* and gives it the contents of the csv file. Then the *Graph* function is called and we pass the "dataset" variable to it. The *Graph* function then prints the contents of the object that is passed to it.
# // Create Global Variable
# var dataset;
#
# // Load csv file
# d3.csv("{{ static_url('data/SingleColumn.csv') }}", function(data) {
#
# dataset = data;
#
# // Call function
# Graph(dataset);
#
# });
#
# // Create function
# function Graph(input) {
#
# // Print to console
# console.log(dataset)
#
# };
The only difference between the previous lesson is the code below. We convert the data type of the column *Revenue. by default js will treat the values in the column as strings. Adding a plus sign will do the conversion. If you look at the console, you will see Revenue: 1*, the number one is no longer in quotes like in the previous lesson.
# // Convert from string to numeric
# dataset.forEach(function(d) {
# d.Revenue = +d.Revenue;
# });
We first select the body of the page and append an svg element to it. This is our first svg element. You can think of this as our canvas to draw on that is 1200px x 800px (pixels). We can specify other supported dimensions like (em, pt, in, cm, and mm).
# //Create SVG element
# var svg = d3.select("body")
# .append("svg")
# .attr("width", 1200)
# .attr("height", 800);
# If you go to "inspect element" you will see:
# <svg width="1200" height="800"></svg>
Now that we have our svg canvas in place. We will attempt to make a column chart by adding rectangle shapes driven by data.
We select the "rect" that will be created when we call .append("rect")
We add the data by .data(input)
We add attributes to the rectangles with .attr():
When you run the code you will only see one rectangle. The reason is that the (x,y) position of every rectangle is the same. The height of each triangle should be relative the the data you feed it, but in this lesson we hard coded it to be *50*. Both of these issues will need to be resolved in future lessons.
# //Add rectangles
# svg.selectAll("rect")
# .data(input)
# .enter()
# .append("rect")
# .attr("fill", "red")
# .attr("x", 0)
# .attr("y", 0)
# .attr("width", 20)
# .attr("height", 50);
To get each rectangle to show, we need a way to give each one a different x coordinate. We start by declaring a new set of variables.
# // Declare Variables
# var margin = {top: 40, right: 40, bottom: 40, left:40},
# w = 960 - margin.left - margin.right,
# h = 500 - margin.top - margin.bottom;
We now create a scale for the x axis.
*domain* = from zero to the number of records in data set.
*range* = from zero to the width of the svg
# //Create X Scale
# var xScale = d3.scale.linear()
# .domain([0, input.length])
# .range([0, w]);
We then modify the svg element to take into consideration our new variables. The transforms will create a smaller box inside the bigger box (the w and h ignoring the margins). This maller box will be where we draw our graphs.
# //Create SVG element
# var svg = d3.select("body")
# .append("svg")
# .attr("width", w + margin.left + margin.right)
# .attr("height", h + margin.top + margin.bottom)
# .append('g')
# .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
Here is where we actually give each rectangle their own unique x coordiante.
*function(d,i)*, the d is the data set, the i is the index (0,1,2...). Zero based index.
# .attr("x", function(d, i) { return xScale(i); })
To fix the issue of the height of the rectangles being tied to the data, we simply make a function that does just that. Every rectangle will look at the "Revenue" column and use that number as its height. Before we do this, we first make a Y scale.
# //Create Y Scale
# var yScale = d3.scale.linear()
# .domain([0,d3.max(input, function(d) { return d.Revenue; })])
# .range([h, 0]);
We then replace the hard coded number with this.
# .attr("height", function(d) { return h - yScale(d.Revenue) });
If you have not noticed the chart has been upside down this whole time. We correct this by adjusting the "y" attribute.
# .attr("y", function(d) { return yScale(d.Revenue) })
Lets crete a Y axis. We start by letting d3 know where to place the axis.
# //Create Y Axis
# var yAxis = d3.svg.axis()
# .scale(yScale)
# .orient('left');
and finally add the axis.
# //Draw Y axis
# svg.append("g")
# .attr("class", "y axis")
# .call(yAxis);
At this point we will change our data set from one with one column to one with multiple columns.
State,CustomerCount
FL,200
GA,210
NY,250
TX,225
ALL,1400
We use an *ordinal* scale since our data for the X axis is not numeric.
# //Create X Scale
# var xScale = d3.scale.ordinal()
# .domain(input.map(function (d){ return d.State;}))
# .rangeRoundBands([0, w], 0.5);
Here we create the X axis and orient it to the bottom of our chart.
# //Create X Axis
# var xAxis = d3.svg.axis()
# .scale(xScale)
# .orient("bottom");
This is where the X axis is actually drawn.
# //Draw X axis
# svg.append("g")
# .attr("class", "x axis")
# .attr("transform", "translate(0," + h + ")")
# .call(xAxis);
We first are going to place our bars into a *class*. This step is very important since it will allow us to easily target the bars.
We change...
# //Add rectangles
# svg.selectAll("rect")
# .data(input)
# .enter()
# .append("rect")
to...
# //Add rectangles
# svg.selectAll(".bar")
# .data(input)
# .enter()
# .append("rect")
# .attr("class", "bar")
We are also going to map each rectangle to a specific color. We first create color scale.
# //Create color scale
# var cScale = d3.scale.linear()
# .domain([0,d3.max(input, function(d) { return d.CustomerCount; })])
# .range(["blue","red"]);
Then we apply the colors to their respective bars.
# .attr("fill",function(d) {return cScale(d.CustomerCount);})
Now we can add the legend.
# // Add Legend
# var legend = svg.append("g")
# .attr("class", "legend")
# .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
Here we create the rectabgles.
# legend.selectAll('rect')
# .data(input)
# .enter()
# .append('rect')
# .attr("x", d3.max(xScale.range()) + xScale.rangeBand() + 40 )
# .attr("y", function(d, i){ return i * 25;})
# .attr("width", 18)
# .attr("height", 18)
# .attr("fill",function(d) {return cScale(d.CustomerCount);});
We now add the text that goes next to the rectangles.
# legend.selectAll('text')
# .data(input)
# .enter()
# .append('text')
# .attr("x", d3.max(xScale.range()) + xScale.rangeBand() + 40 )
# .attr("y", function(d, i){ return (i * 25) + 8;})
# .attr("dy", ".35em")
# .style("text-anchor", "end")
# .text(function(d) { return d.State; });
# //Draw title
# svg.append("text")
# .attr("x", w / 2 )
# .attr("y", yScale(d3.max(input, function(d) { return d.CustomerCount; })) - 40 )
# .style("text-anchor", "middle")
# .text("Title of Graph");
# //Create X axis label
# svg.append("text")
# .attr("x", w / 2 )
# .attr("y", yScale(0) + 40 )
# .style("text-anchor", "middle")
# .text("State");
# //Create Y axis label
# svg.append("text")
# .attr("transform", "rotate(-90)")
# .attr("y", xScale(0) - 80 )
# .attr("x",0 - (h / 2))
# .attr("dy", "1em")
# .style("text-anchor", "middle")
# .text("Revenue");