|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
|
|
<link href='https://fonts.googleapis.com/css?family=Fira+Sans:300,400,500,700' rel='stylesheet' type='text/css'> |
|
<link href='style.css' rel='stylesheet' type='text/css'> |
|
<script src="//d3js.org/d3.v4.min.js"></script> |
|
|
|
<body> |
|
<!-- |
|
If there is an order you prefer for your elements, |
|
consider pre-defining the g elements in your |
|
preferred order. |
|
//--> |
|
<svg> |
|
<!-- axis lines should be in back --> |
|
<g id="x"></g> <!-- x axis --> |
|
<g id="y"></g> <!-- y axis --> |
|
|
|
<!-- header (title) and footer (source) --> |
|
<g id="header"></g> |
|
<g id="footer"></g> |
|
|
|
<!-- actual visualization on top --> |
|
<g id="plot"> |
|
<g id="area"></g> <!-- area chart --> |
|
<g id="note"></g> <!-- area annotation --> |
|
<g id="line"></g> <!-- line chart --> |
|
<g id="points"></g> <!-- line markers --> |
|
<g id="bars"></g> <!-- heavy black axis lines --> |
|
</g> |
|
</svg> |
|
|
|
|
|
<script> |
|
var size = {width: 600, height: 750}; |
|
var pad = {top: 100, right: 45, bottom: 75, left: 45}; |
|
|
|
var width = size.width - pad.left - pad.right; |
|
var height = size.height - pad.top - pad.bottom; |
|
|
|
var x = d3.scaleLinear() |
|
.range([0, width]); |
|
|
|
var y = d3.scaleLinear() |
|
.range([0, height]); |
|
|
|
var xaxis = d3.axisBottom(x) |
|
.tickSize(20, 0) |
|
.tickValues([1990, 2000, 2010]) |
|
.tickFormat(function(d) { return "" + d + "s"; }); |
|
|
|
var yaxis = d3.axisLeft(y) |
|
.ticks(6) |
|
|
|
var line = d3.line() |
|
.curve(d3.curve) |
|
.x(function(d) { return x(d.Year); }) |
|
.y(function(d) { return y(d.Total); }); |
|
|
|
var area = d3.area() |
|
.curve(d3.curveMonotoneX) |
|
.x(function(d) { return x(d.Year); }) |
|
.y1(0) |
|
.y0(function(d) { return y(d.Total); }); |
|
|
|
function translate(x, y) { |
|
return "translate(" + x + "," + y + ")"; |
|
} |
|
|
|
function convert(row) { |
|
row.Year = +row.Year; |
|
row.Population = +row.Population; |
|
row.Total = +row.Total; |
|
row.Rate = +row.Rate; |
|
return row; |
|
} |
|
|
|
var svg = d3.select("body").select("svg"); |
|
var plot = svg.select("g#plot"); |
|
var header = svg.select("g#header"); |
|
var footer = svg.select("g#footer"); |
|
|
|
svg.attr("width", size.width); |
|
svg.attr("height", size.height); |
|
plot.attr("transform", translate(pad.left, pad.top)); |
|
|
|
/* |
|
* add various text annotation to svg |
|
*/ |
|
|
|
header.append("text") |
|
.attr("class", "title") |
|
.attr("x", 0) |
|
.attr("y", 0) |
|
.attr("dx", "5px") |
|
.attr("dy", "36pt") |
|
.text("Gun deaths in Florida"); |
|
|
|
header.append("text") |
|
.attr("class", "subtitle") |
|
.attr("x", 0) |
|
.attr("y", 0) |
|
.attr("dx", "5px") |
|
.attr("dy", "58pt") |
|
.text("Number of murders committed using firearms"); |
|
|
|
footer.append("text") |
|
.attr("x", 0) |
|
.attr("y", size.height) |
|
.attr("dx", "5px") |
|
.attr("dy", "-22pt") |
|
.text("Source: Florida Department of Law Enforcement"); |
|
|
|
footer.append("text") |
|
.attr("x", 0) |
|
.attr("y", size.height) |
|
.attr("dx", "5px") |
|
.attr("dy", "-6pt") |
|
.text("Inspiration: http://tinyurl.com/led8ggk"); |
|
|
|
/* |
|
* draw heavy axis lines at y-min and y-max |
|
*/ |
|
|
|
var bars = plot.select("g#bars"); |
|
bars.append("line") |
|
.attr("x1", 0) |
|
.attr("y1", 0) |
|
.attr("x2", width) |
|
.attr("y2", 0) |
|
|
|
bars.append("line") |
|
.attr("x1", 0) |
|
.attr("y1", height) |
|
.attr("x2", width) |
|
.attr("y2", height); |
|
|
|
|
|
d3.csv("data.csv", convert, function(error, data) { |
|
if (error) throw error; |
|
|
|
console.log(data); |
|
x.domain(d3.extent(data, function(e) {return e.Year; })); |
|
y.domain([0, 1000]); |
|
|
|
/* |
|
* draw x- and y-axis |
|
* should be behind other elements |
|
*/ |
|
svg.select("g#y") |
|
.attr("transform", translate(pad.left, pad.top)) |
|
.call(yaxis); |
|
|
|
svg.select("g#x") |
|
.attr("transform", translate(pad.left, pad.top + height)) |
|
.call(xaxis) |
|
.selectAll(".tick text") |
|
.style("text-anchor", "start") |
|
.attr("x", 6) |
|
.attr("y", 6); |
|
|
|
/* |
|
* plot red area |
|
*/ |
|
|
|
plot.select("g#area") |
|
.append("path") |
|
.datum(data) |
|
.attr("d", area) |
|
.style("fill", 'red') |
|
|
|
/* |
|
* draw line on top of area |
|
*/ |
|
/* |
|
plot.select("g#line") |
|
.append("path") |
|
.datum(data) |
|
.attr("d", line); |
|
*/ |
|
/* |
|
* draw points on top of line |
|
*/ |
|
/* |
|
plot.select("g#points") |
|
.selectAll("circle") |
|
.data(data) |
|
.enter() |
|
.append("circle") |
|
.attr("cx", function(d) { return x(d.Year); }) |
|
.attr("cy", function(d) { return y(d.Total); }) |
|
.attr("r", 6); |
|
*/ |
|
/* |
|
* plot annotation on top of area |
|
*/ |
|
|
|
var note = plot.select("g#note"); |
|
|
|
note.selectAll("text") |
|
// text with manual line breaks |
|
.data(["2005", "Florida enacted", "its 'Stand Your", "Ground' law"].reverse()) |
|
.enter() |
|
.append("text") |
|
.attr("id", function(d, i) { return "note-" + i; }) |
|
.attr("x", x(2000)) |
|
.attr("y", y(840)) |
|
.attr("dx", x(2002) - x(2005)) |
|
.style('fill', 'black') |
|
.attr("dy", function(d, i) { return (-i * 2) + "ex"; }) |
|
.text(function(d) { return d; }); |
|
|
|
|
|
note.append("line") |
|
.style('fill', "black") |
|
.attr("x1", x(2002)) |
|
.attr("x2", x(2005)) |
|
.attr("y1", y(700) + 5) |
|
.attr("y2", y(521)) |
|
.style("stroke", 'black'); |
|
|
|
}); |
|
|
|
</script> |
|
</body> |
|
</html> |