|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="utf-8"> |
|
<title>Jersey City . Public Housing</title> |
|
<script type="text/javascript" src="http://d3js.org/d3.v3.js"></script> |
|
<style type="text/css"> |
|
|
|
body { |
|
font-family: Helvetica, Arial, sans-serif; |
|
background-color: white; |
|
} |
|
svg { |
|
background-color: white; |
|
} |
|
h1 { |
|
font-size: 24px; |
|
margin: 0; |
|
} |
|
p { |
|
font-size: 14px; |
|
margin: 10px 0 0 0; |
|
} |
|
|
|
path { |
|
stroke: gray; |
|
stroke-width: 0.5; |
|
} |
|
|
|
path:hover, |
|
g.highlight path:hover { |
|
stroke: orange; |
|
stroke-width: 2; |
|
opacity: 1.0; |
|
} |
|
|
|
g.highlight path { |
|
stroke: steelblue; |
|
stroke-width: 2; |
|
} |
|
|
|
.axis path, |
|
.axis line { |
|
fill: none; |
|
stroke: black; |
|
shape-rendering: crispEdges; |
|
} |
|
|
|
.axis text { |
|
font-family: sans-serif; |
|
font-size: 11px; |
|
} |
|
|
|
#propertyName_list{ |
|
position:absolute; |
|
left:650px; |
|
margin-top:20px; |
|
width: 170px; |
|
} |
|
#propertyName_list p{ |
|
margin: 5px; |
|
padding: 3px; |
|
cursor: pointer; |
|
background-color:#dddddd; |
|
} |
|
#propertyName_list p:hover{background-color:steelblue;} |
|
.axistitle{ |
|
font-size: 12px; |
|
} |
|
|
|
|
|
</style> |
|
</head> |
|
<body> |
|
|
|
<h1>Vacancy Rates by Property</h1> |
|
|
|
<p>Monthly vacancy rates Jan 2014-May 2015. Source: <a href="http://www.jerseycitynj.gov/data.aspx?id=14832">Jersey City Housing Authority</a>.</p> |
|
|
|
<p> |
|
<!-- inspiration for the buttons came from http://bl.ocks.org/d3noob/7030f35b72de721622b8 --> |
|
<button id="vacancy">Vacancy Rate</button> |
|
<button id="vacantUnits">Vacant Units</button> |
|
<button id="totalUnits">Total Units</button> |
|
</p> |
|
<div id="propertyName_list"></div> |
|
<script type="text/javascript"> |
|
var groups; |
|
|
|
//Create a new, empty array to hold our restructured dataset |
|
var dataset = []; |
|
|
|
var padding = [ 50, 10, 100, 70 ]; //Top, right, bottom, left |
|
var percent = d3.format(".0%"); |
|
|
|
var w = 700; |
|
var h = 400; |
|
|
|
//Set up date formatting and years |
|
var dateFormat = d3.time.format("%B %Y"); |
|
var dateFormat2 = d3.time.format("%b %Y"); |
|
|
|
//Set up scales |
|
var xScale = d3.time.scale() |
|
.range([ padding[3], w - padding[1] - padding[3] ]); |
|
|
|
var yScale = d3.scale.linear() |
|
.range([ padding[0], h - padding[2] ]); |
|
|
|
//Configure axis generators |
|
var xAxis = d3.svg.axis() |
|
.scale(xScale) |
|
.orient("bottom") |
|
.ticks(15) |
|
.tickFormat(function(d) { |
|
return dateFormat2(d); |
|
}); |
|
|
|
var yAxis = d3.svg.axis() |
|
.scale(yScale) |
|
.orient("left"); |
|
|
|
|
|
//Configure line generator |
|
var line = d3.svg.line() |
|
.x(function(d) { |
|
return xScale(dateFormat.parse(d.years)); |
|
}) |
|
.y(function(d) { |
|
return yScale(+d.amount); |
|
}); |
|
|
|
//Create the empty SVG image |
|
var svg = d3.select("body") |
|
.append("svg") |
|
.attr("width", w) |
|
.attr("height", h); |
|
|
|
//Load Jersey City Housing Authority data |
|
d3.csv("JCHAVacancies.csv", function(data) { |
|
|
|
//Usefull reading for manipulating arrays: https://github.com/mbostock/d3/wiki/Arrays |
|
|
|
var yearsArray = ["January 2014","February 2014","March 2014","April 2014","May 2014","June 2014", |
|
"July 2014","August 2014","September 2014","October 2014","November 2014","December 2014", |
|
"January 2015","February 2015","March 2015","April 2015","May 2015"]; |
|
|
|
//Loop once for each row in data |
|
|
|
for (var i = 0; i < data.length; i++) { |
|
|
|
var adding = true; |
|
var ix = dataset.length; |
|
|
|
// Checking if property is already stored in the dataset |
|
for (var k = 0; k < dataset.length; k++) { |
|
if ( dataset[k].propertyName == data[i].propertyName ) { |
|
adding = false; |
|
ix = k; |
|
}; |
|
}; |
|
|
|
// Property is not in dataset yet, adding it |
|
if (adding) { |
|
dataset.push({ |
|
propertyName: data[i].propertyName, |
|
propertyCategory: data[i].propertyCategory, |
|
moveIns: [], |
|
moveOuts: [], |
|
vacancyDaysAllUnits: [], |
|
totalUnits: [], |
|
vacantUnits: [], |
|
vacancyRate: [] |
|
}); |
|
}; |
|
|
|
// Adding values which are on each row |
|
// parsing month and year string and converting them to date |
|
var years = dateFormat.parse(data[i].month+" "+data[i].year); |
|
// console.log(years); |
|
|
|
dataset[ix].moveIns.push({ years: dateFormat(years), amount: data[i].moveIns }); |
|
dataset[ix].moveOuts.push({ years: dateFormat(years), amount: data[i].moveOuts }); |
|
dataset[ix].vacancyDaysAllUnits.push({ years: dateFormat(years), amount: data[i].vacancyDaysAllUnits }); |
|
dataset[ix].totalUnits.push({ years: dateFormat(years), amount: data[i].totalUnits }); |
|
dataset[ix].vacantUnits.push({ years: dateFormat(years), amount: data[i].vacantUnits }); |
|
dataset[ix].vacancyRate.push({ years: dateFormat(years), amount: data[i].vacancyRate }); |
|
}; |
|
|
|
// dataset looks like this |
|
// [ |
|
// { |
|
// propetyName: "Marion Gardens", |
|
// propetyCategory: "Conventional Public Housing", |
|
// moveIns: [ |
|
// { year: "January2014", amount: 1 }, |
|
// { year: "February2014", amount: 2 }, |
|
// { year: "March2014", amount: 3 }, |
|
// … |
|
// ], |
|
// moveOuts: [ |
|
// { year: "January2014", amount: 1 }, |
|
// { year: "February2014", amount: 2 }, |
|
// { year: "March2014", amount: 3 }, |
|
// … |
|
// ] |
|
// },… |
|
// ] |
|
|
|
//Uncomment to log the original data to the console |
|
// console.log(data); |
|
|
|
//Uncomment to log the newly restructured dataset to the console |
|
console.log(dataset); |
|
|
|
//Set scale domains |
|
xScale.domain([ |
|
d3.min(yearsArray, function(d) { |
|
return dateFormat.parse(d); |
|
}), |
|
d3.max(yearsArray, function(d) { |
|
return dateFormat.parse(d); |
|
}) |
|
]); |
|
|
|
yScale.domain([ |
|
d3.max(dataset, function(d) { |
|
return d3.max(d.vacancyRate, function(d) { |
|
return +d.amount; |
|
}); |
|
}), |
|
0 |
|
]); |
|
|
|
console.log("done with scale domains"); |
|
|
|
//Make a group for each property |
|
groups = svg.selectAll("g") |
|
.data(dataset) |
|
.enter() |
|
.append("g") |
|
.classed("highlight", function(d) { |
|
|
|
if (d.propertyCategory == "Homeownership Project" ) { |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
|
|
}); |
|
|
|
//Append a title with the country name (so we get easy tooltips) |
|
groups.append("title") |
|
.text(function(d) { |
|
return d.propertyName; |
|
}); |
|
|
|
//Within each group, create a new line/path, |
|
//binding just the vacancyRate data to each one |
|
groups.selectAll("path") |
|
.data(function(d) { |
|
return [ d.vacancyRate ]; |
|
}) |
|
.enter() |
|
.append("path") |
|
.attr("class", "line") |
|
.attr("d", line) |
|
.attr("fill", "none") |
|
.attr("stroke", "steelblue") |
|
.attr("stroke-width", 2) |
|
.transition() |
|
.duration(1950) |
|
.ease("linear") |
|
.attrTween("stroke-dasharray", animateLine) |
|
.attr("display", 1); |
|
|
|
//Axes |
|
svg.append("g") |
|
.attr("class", "x axis") |
|
.attr("transform", "translate(0," + (h - padding[2]) + ")") |
|
.call(xAxis) |
|
.selectAll("text") |
|
.attr("y", 0) |
|
.attr("x", 9) |
|
.attr("dy", ".35em") |
|
.attr("transform", "rotate(90)") |
|
.style("text-anchor", "start"); |
|
|
|
svg.append("g") |
|
.attr("class", "y axis") |
|
.attr("transform", "translate(" + (padding[3]) + ",0)") |
|
.call(yAxis); |
|
|
|
console.log("done done done"); |
|
|
|
}); |
|
//End Jersey City Housing Authority data load function |
|
|
|
d3.select("#vacantUnits") |
|
.on("click", function() { |
|
//Update scale domain |
|
yScale.domain([ |
|
d3.max(dataset, function(d) { |
|
return d3.max(d.vacantUnits, function(d) { |
|
return +d.amount; |
|
}); |
|
}), |
|
0 |
|
]); |
|
//Update the data |
|
groups.selectAll("path") |
|
.data(function(d) { |
|
return [ d.vacantUnits ]; // set the new data |
|
}) |
|
.attr("class", "line") |
|
.attr("d", line) // apply the new data values |
|
.transition() |
|
.duration(1950) |
|
.ease("linear") |
|
.attrTween("stroke-dasharray", animateLine) |
|
.attr("display", 1); |
|
|
|
//Update axis |
|
d3.select(".y.axis") |
|
.transition() |
|
.duration(1000) |
|
.call(yAxis); |
|
}); |
|
d3.select("#totalUnits") |
|
.on("click", function() { |
|
//Update scale domain |
|
yScale.domain([ |
|
d3.max(dataset, function(d) { |
|
return d3.max(d.totalUnits, function(d) { |
|
return +d.amount; |
|
}); |
|
}), |
|
0 |
|
]); |
|
//Update the data |
|
groups.selectAll("path") |
|
.data(function(d) { |
|
return [ d.totalUnits ]; // set the new data |
|
}) |
|
.attr("class", "line") |
|
.attr("d", line) // apply the new data values |
|
.transition() |
|
.duration(1950) |
|
.ease("linear") |
|
.attrTween("stroke-dasharray", animateLine) |
|
.attr("display", 1); |
|
|
|
//Update axis |
|
d3.select(".y.axis") |
|
.transition() |
|
.duration(1000) |
|
.call(yAxis); |
|
}); |
|
d3.select("#vacancy") |
|
.on("click", function() { |
|
//Update scale domain |
|
yScale.domain([ |
|
d3.max(dataset, function(d) { |
|
return d3.max(d.vacancyRate, function(d) { |
|
return +d.amount; |
|
}); |
|
}), |
|
0 |
|
]); |
|
//Update the data |
|
groups.selectAll("path") |
|
.data(function(d) { |
|
return [ d.vacancyRate ]; // set the new data |
|
}) |
|
.attr("class", "line") |
|
.attr("d", line) // apply the new data values |
|
.transition() |
|
.duration(1950) |
|
.ease("linear") |
|
.attrTween("stroke-dasharray", animateLine) |
|
.attr("display", 1); |
|
|
|
//Update axis |
|
d3.select(".y.axis") |
|
.transition() |
|
.duration(1000) |
|
.call(yAxis); |
|
}); |
|
|
|
|
|
function animateLine() { |
|
var l = this.getTotalLength(); |
|
i = d3.interpolateString("0," + l, l + "," + l); |
|
return function(t) { return i(t); }; |
|
}; |
|
|
|
</script> |
|
|
|
</body> |
|
</html> |