forked from scotthmurray/d3-book
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Scott Murray
committed
Apr 18, 2017
1 parent
614bdb0
commit f6bf566
Showing
2 changed files
with
358 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,353 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="utf-8"> | ||
<title>D3: Click to transition</title> | ||
<script type="text/javascript" src="../d3.js"></script> | ||
<style type="text/css"> | ||
|
||
h1 { | ||
font-family: Helvetica, sans-serif; | ||
font-size: 14px; | ||
font-weight: bold; | ||
} | ||
|
||
.area { | ||
stroke: none; | ||
} | ||
|
||
.area:hover { | ||
fill: yellow; | ||
} | ||
|
||
</style> | ||
</head> | ||
<body> | ||
<h1>Monthly Number of Electric-Drive Vehicles Sold in the U.S. (by Type): January 2005–Feburary 2017</h1> | ||
<script type="text/javascript"> | ||
|
||
//Width and height | ||
var w = 1400; | ||
var h = 300; | ||
var padding = 20; | ||
|
||
var dataset, xScale, yScale, xAxis, yAxis, area; //Empty, for now | ||
|
||
//For converting strings to Dates | ||
var parseTime = d3.timeParse("%Y-%m"); | ||
|
||
//For converting Dates to strings | ||
var formatTime = d3.timeFormat("%b %Y"); | ||
|
||
//Set up stack methods | ||
var vehicleStack = d3.stack(); | ||
var typeStack = d3.stack(); | ||
|
||
//Load in data | ||
d3.request("vehicle_sales_data.csv") | ||
.mimeType("text/csv") | ||
.get(function(response) { | ||
|
||
|
||
|
||
// | ||
// DATA PARSING | ||
// | ||
|
||
//Parse each row of the CSV into an array of string values | ||
var rows = d3.csvParseRows(response.responseText); | ||
// console.log(rows); | ||
|
||
//Make dataset an empty array, so we can start adding values | ||
dataset = []; | ||
|
||
//Loop once for each row of the CSV, starting at row 3, | ||
//since rows 0-2 contain only vehicle info, not sales values. | ||
for (var i = 3; i < rows.length; i++) { | ||
|
||
//Create a new object | ||
dataset[i - 3] = { | ||
date: parseTime(rows[i][0]) //Make a new Date object for each year + month | ||
}; | ||
|
||
//Loop once for each vehicle in this row (i.e., for this date) | ||
for (var j = 1; j < rows[i].length; j++) { | ||
|
||
var make = rows[0][j]; //'Make' from 1st row in CSV | ||
var model = rows[1][j]; //'Model' from 2nd row in CSV | ||
var makeModel = rows[0][j] + " " + rows[1][j]; //'Make' + 'Model' will serve as our key | ||
var type = rows[2][j]; //'Type' from 3rd row in CSV | ||
var sales = rows[i][j]; //Sales value for this vehicle and month | ||
|
||
//If sales value exists… | ||
if (sales) { | ||
sales = parseInt(sales); //Convert from string to int | ||
} else { //Otherwise… | ||
sales = 0; //Set to zero | ||
} | ||
|
||
//Append a new object with data for this vehicle and month | ||
dataset[i - 3][makeModel] = { | ||
"make": make, | ||
"model": model, | ||
"type": type, | ||
"sales": sales | ||
}; | ||
|
||
} | ||
|
||
} | ||
|
||
//Log out the final state of dataset | ||
// console.log(dataset); | ||
|
||
|
||
|
||
// | ||
// STACKING | ||
// | ||
|
||
//Now that we know the column names in the data, | ||
//get all the keys (make + model), but toss out 'date' | ||
var keys = Object.keys(dataset[0]).slice(1); | ||
// console.log(keys); | ||
|
||
//Tell stack function where to find the keys | ||
vehicleStack.keys(keys) | ||
.value(function value(d, key) { | ||
return d[key].sales; | ||
}); | ||
|
||
//Stack the data and log it out | ||
var vehicleSeries = vehicleStack(dataset); | ||
// console.log(vehicleSeries); | ||
|
||
|
||
|
||
// | ||
// TYPE DATA SERIES | ||
// | ||
|
||
//The goal here is to make a totally separate data set that | ||
//includes just monthly totals for each `type` (HEV, PHEV, BEV, FCEV). | ||
|
||
//Make typeDataset an empty array, so we can start adding values | ||
typeDataset = []; | ||
|
||
//Loop once for each row of the CSV, starting at row 3, | ||
//since rows 0-2 contain only vehicle info, not sales values. | ||
for (var i = 3; i < rows.length; i++) { | ||
|
||
//Create a new object | ||
typeDataset[i - 3] = { | ||
date: parseTime(rows[i][0]), //Make a new Date object for each year + month | ||
"HEV": 0, | ||
"PHEV": 0, | ||
"BEV": 0, | ||
"FCEV": 0 | ||
}; | ||
|
||
//Loop once for each vehicle in this row (i.e., for this date) | ||
for (var j = 1; j < rows[i].length; j++) { | ||
|
||
var type = rows[2][j]; //'Type' from 3rd row in CSV | ||
var sales = rows[i][j]; //Sales value for this vehicle and month | ||
|
||
//If sales value exists… | ||
if (sales) { | ||
sales = parseInt(sales); //Convert from string to int | ||
} else { //Otherwise… | ||
sales = 0; //Set to zero | ||
} | ||
|
||
//Add sales value to existing sum | ||
typeDataset[i - 3][type] += sales; | ||
|
||
} | ||
|
||
} | ||
|
||
//Log out the final state of dataset | ||
// console.log(typeDataset); | ||
|
||
|
||
|
||
// | ||
// STACKING | ||
// | ||
|
||
//Tell stack function where to find the keys | ||
typeStack.keys([ "HEV", "PHEV", "BEV", "FCEV" ]); | ||
|
||
//Stack the data and log it out | ||
var typeSeries = typeStack(typeDataset); | ||
// console.log(typeSeries); | ||
|
||
|
||
|
||
// | ||
// MAKE THE CHART | ||
// | ||
|
||
//Create scale functions | ||
xScale = d3.scaleTime() | ||
.domain([ | ||
d3.min(dataset, function(d) { return d.date; }), | ||
d3.max(dataset, function(d) { return d.date; }) | ||
]) | ||
.range([padding, w - padding * 2]); | ||
|
||
yScale = d3.scaleLinear() | ||
.domain([ | ||
0, | ||
d3.max(dataset, function(d) { | ||
var sum = 0; | ||
|
||
//Loops once for each row, to calculate | ||
//the total (sum) of sales of all vehicles | ||
for (var i = 0; i < keys.length; i++) { | ||
sum += d[keys[i]].sales; | ||
}; | ||
|
||
return sum; | ||
}) | ||
]) | ||
.range([h - padding, padding / 2]) | ||
.nice(); | ||
|
||
//Define axes | ||
xAxis = d3.axisBottom() | ||
.scale(xScale) | ||
.ticks(10) | ||
.tickFormat(formatTime); | ||
|
||
//Define Y axis | ||
yAxis = d3.axisRight() | ||
.scale(yScale) | ||
.ticks(5); | ||
|
||
//Define area generator | ||
area = d3.area() | ||
.x(function(d) { return xScale(d.data.date); }) | ||
.y0(function(d) { return yScale(d[0]); }) | ||
.y1(function(d) { return yScale(d[1]); }); | ||
|
||
//Create SVG element | ||
var svg = d3.select("body") | ||
.append("svg") | ||
.attr("width", w) | ||
.attr("height", h); | ||
|
||
//Create areas for individual VEHICLES | ||
svg.append("g") | ||
.attr("id", "vehicles") | ||
.selectAll("path") | ||
.data(vehicleSeries) | ||
.enter() | ||
.append("path") | ||
.attr("class", "area") | ||
.attr("d", area) | ||
.attr("fill", function(d) { | ||
|
||
//Which vehicle is this? | ||
var thisKey = d.key; | ||
|
||
//What 'type' is this vehicle? | ||
var thisType = d[0].data[thisKey].type; | ||
// console.log(thisType); | ||
|
||
//New color var | ||
var color; | ||
|
||
switch (thisType) { | ||
case "HEV": | ||
color = d3.schemeCategory20[0]; | ||
break; | ||
case "PHEV": | ||
color = d3.schemeCategory20[1]; | ||
break; | ||
case "BEV": | ||
color = d3.schemeCategory20[2]; | ||
break; | ||
case "FCEV": | ||
color = d3.schemeCategory20[3]; | ||
break; | ||
} | ||
|
||
return color; | ||
}) | ||
.append("title") //Make tooltip | ||
.text(function(d) { | ||
return d.key; | ||
}); | ||
|
||
//Create areas for TYPES | ||
svg.append("g") | ||
.attr("id", "types") | ||
.selectAll("path") | ||
.data(typeSeries) | ||
.enter() | ||
.append("path") | ||
.attr("class", "area") | ||
.attr("d", area) | ||
.attr("fill", function(d) { | ||
|
||
//Which type is this? | ||
var thisType = d.key; | ||
|
||
//New color var | ||
var color; | ||
|
||
switch (thisType) { | ||
case "HEV": | ||
color = "rgb(110, 64, 170)"; | ||
break; | ||
case "PHEV": | ||
color = "rgb(76, 110, 219)"; | ||
break; | ||
case "BEV": | ||
color = "rgb(35, 171, 216)"; | ||
break; | ||
case "FCEV": | ||
color = "rgb(29, 223, 163)"; | ||
break; | ||
} | ||
|
||
return color; | ||
}) | ||
.on("click", function() { | ||
|
||
// d3.select("#types") | ||
// .selectAll("path") | ||
// .attr("opacity", 1) | ||
// .transition() | ||
// .duration(500) | ||
// .attr("opacity", 0); | ||
|
||
d3.select(this) | ||
.attr("opacity", 1); | ||
|
||
}) | ||
.append("title") //Make tooltip | ||
.text(function(d) { | ||
return d.key; | ||
}); | ||
|
||
//Create axes | ||
svg.append("g") | ||
.attr("class", "axis") | ||
.attr("transform", "translate(0," + (h - padding) + ")") | ||
.call(xAxis); | ||
|
||
svg.append("g") | ||
.attr("class", "axis") | ||
.attr("transform", "translate(" + (w - padding * 2) + ",0)") | ||
.call(yAxis); | ||
|
||
|
||
|
||
}); | ||
|
||
</script> | ||
</body> | ||
</html> |