Built with blockbuilder.org
[ ] 20 pts
Create a heatmap encoding weekday versus month (or visa versa) along the x- and y-axis, and incident count using a 3-color sequential color palette.
[ ] 04 pts
Add a compact color legend to your visualization.
[ ] 02 pts
Add annotations for the x-axis, y-axis, and a chart title or caption. This includes axis lines, tick marks, and tick labels. The exact style is up to you.
[ ] 02 pts
Add text annotations for the minimum and maximum values only.
[ ] 02 pts
Add one general conclusion about the data that you can make from your visualization. This can be placed as paragraph text below your SVG.
xxxxxxxxxx
<head>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="colorbrewer.js"></script>
<script src="heatmap.js"></script>
</head>
<body>
<script>
/* helper method to make translating easier */
var translate = function(x, y) { return "translate(" + x + "," + y + ")"; };
// container for configuration parameters
var config = {};
config.svg = {width: 960, height: 500};
config.margin = { top: 50, right: 10, bottom: 20, left: 40};
config.plot = {
width: config.svg.width - config.margin.right - config.margin.left,
height: config.svg.height - config.margin.top - config.margin.bottom
};
config.legend = {
width: 100,
height: 15
};
// create svg based on config
var svg = d3.select("body").append("svg")
.attr("width", config.svg.width)
.attr("height", config.svg.height);
// create plot area based on config
var plot = svg.append("g")
.attr("id", "plot")
.attr("transform", translate(config.margin.left, config.margin.top));
// create the scales and set ranges
// https://github.com/mbostock/d3/wiki/Ordinal-Scales#ordinal_rangeBands
// we only need the plot dimensions for the range!
// we will set the domain later when we load the data
var xScale = d3.scale.ordinal()
.rangeBands([0, config.plot.width], 0, 0);
var yScale = d3.scale.ordinal()
.rangeBands([config.plot.height, 0], 0, 0);
// create color scale for each cell
// https://github.com/mbostock/d3/wiki/Ordinal-Scales#colorbrewer
// https://bl.ocks.org/mbostock/5577023
// we will use a 3 color sequential palette for this demo
var colorScale = d3.scale.linear()
.range(colorbrewer.YlGnBu[3]);
// formatter to parse dates
// https://github.com/mbostock/d3/wiki/Time-Formatting
// must match format used by csv
var dateFormat = d3.time.format("%-m/%-d/%Y");
// used to nest data and output values nicely
// var yearFormat = d3.time.format("%Y");
var monthFormat = d3.time.format("%b");
// placeholder for data
var data = [];
// d3.csv("Monthly_Property_Crime_2005_to_2015.csv",
d3.csv("sfpd-property-crimes-2005-2015.csv",
function(d) { // accessor - will format data as it loads it
var row = {};
// row.date = dateFormat.parse(d["Date"]);
// row.year = yearFormat(row.date);
// row.month = monthFormat(row.date);
// row.type = d["Category"]-;
// row.count = +d["IncidntNum"];
row.date = dateFormat.parse(d["Date"]);
row.month = monthFormat(row.date);
row.weekday = d["Weekday"];
row.count = +d["Incidents"];
// console.log(row);
return row;
},
function(error, rows) { // callback - will try to format it as it goes
if (error) {
console.warn(error);
}
// we can use fancy d3 operations to group our data by year and month
// https://github.com/mbostock/d3/wiki/Arrays#nest
data = d3.nest()
.key(function(d) { return d.month; }) // group by month first
.key(function(d) { return d.weekday; }) // group by weekday second
.rollup(function(values) {
// sum together all of the categories
return d3.sum(values, function(d) {
return d.count;
})
})
.map(rows, d3.map); // return as nested d3.maps
// console.log("rows", rows);
// console.log("nest", data);
// pull out years and months
// use to set domain of x and y scales
var months = data.keys();
var weekday = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
// var days = data.get(weekday.keys());
// console.log(weekday);
xScale.domain(weekday);
yScale.domain(months);
/*
* how will we map our values to those colors?
* lets use the min, average, and max values as our "breaks"
* (this means our "middle" color might not be the "middle" value)
*/
// our first step is to calculate the min, max, and average
// the issue now is our sums are nested
// un-nesting these values is a little strange looking
// debug each step in the console so you know what is going on
// this returns all the values in our map
// in this case, our values are themselves maps
var values = data.values();
// now we have an array of maps
// grab the map values for each inner map
values = values.map(function(d) { return d.values(); });
// now we have an array of arrays
// merge arrays into a single array
values = d3.merge(values);
// now we can calculate the min, max, and average of our counts
var min = d3.min(values);
var max = d3.max(values);
var avg = d3.mean(values);
// console.log("min:" + min + " max:" + max + " avg:" + avg);
/*
* is this efficient?
* goodness no! but if you cared, why are you doing
* data processing in javascript!
*/
// use these values as domain for x, y, and color scales
colorScale.domain([min, avg, max]);
// finally, draw stuff
drawBackground();
drawAxes();
drawHeatmap();
drawTitle();
drawLegend();
});
</script>
</body>
https://d3js.org/d3.v3.min.js