|
<!DOCTYPE html> |
|
|
|
<html lang="en"> |
|
|
|
<head> |
|
|
|
<meta charset="utf-8"> |
|
|
|
<title>Core Housing Need in Canada</title> |
|
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js" charset="utf-8"></script> |
|
<link href='https://fonts.googleapis.com/css?family=Open+Sans+Condensed:300,700,300italic' rel='stylesheet' type='text/css'> |
|
|
|
<style type="text/css"> |
|
|
|
body { |
|
margin: 0; |
|
background-color: #fff; |
|
font-family: Helvetica,Arial,sans-serif; |
|
} |
|
|
|
#container { |
|
max-width: 700px; |
|
position: relative; |
|
margin-left: 20px; |
|
margin-right: auto; |
|
margin-top: 20px; |
|
padding: 10px 50px; |
|
background-color: #fff; |
|
box-shadow: 2px 2px 2px 2px #ccc; |
|
} |
|
|
|
#chartbox { |
|
max-width: 650px; |
|
position: absolute; |
|
top: 20px; |
|
left: 850px; |
|
padding: 10px; |
|
box-shadow: 2px 2px 2px 2px #ccc; |
|
/* visibility: hidden; */ |
|
} |
|
#mapBox { |
|
max-width: 650px; |
|
position: absolute; |
|
top: 20px; |
|
left: 1230px; |
|
padding: 10px; |
|
box-shadow: 2px 2px 2px 2px #ccc; |
|
/* visibility: hidden; */ |
|
} |
|
p { |
|
font-size: 16px; |
|
color: #4e5a64; |
|
margin: 15px 0 50px; |
|
} |
|
|
|
h1 { |
|
font-weight: 700; |
|
color: #4e5a64; |
|
font-size: 32px; |
|
} |
|
h2 { |
|
font-weight: 500; |
|
color: #4e5a64; |
|
font-size: 22px; |
|
} |
|
|
|
a:link { |
|
text-decortion: none; |
|
color: gray; |
|
} |
|
|
|
a:hover { |
|
text-decoration: underline; |
|
} |
|
|
|
a:visited { |
|
color: gray; |
|
} |
|
|
|
a:active { |
|
color: #87ceeb; |
|
} |
|
|
|
svg { |
|
background-color: #fff; |
|
} |
|
|
|
g.bar text { |
|
font-size: 14px; |
|
font-weight: 550; |
|
text-anchor: end; |
|
opacity: 0; |
|
fill: #fff; |
|
stroke: none; |
|
} |
|
|
|
g.bar:hover text { |
|
opacity: 1; |
|
} |
|
|
|
.axis path,.axis line { |
|
fill: none; |
|
stroke: #4e5a64; |
|
} |
|
|
|
.axis text { |
|
font-family: sans-serif; |
|
font-size: 12px; |
|
fill: #4e5a64; |
|
stroke: none; |
|
} |
|
|
|
.y.axis path,.y.axis line { |
|
opacity: 0; |
|
} |
|
|
|
path { stroke:#fff ; |
|
stroke-width: .9px; |
|
fill: #BDD684; |
|
} |
|
path:hover{ |
|
fill: #5e6b42; |
|
} |
|
|
|
#mapBox { |
|
background-color: white; |
|
margin: auto; |
|
max-width: 800px; |
|
} |
|
|
|
.header, |
|
.info, |
|
{ |
|
margin: auto; |
|
max-width: 800px; |
|
} |
|
|
|
#tooltip { |
|
z-index: 1; |
|
position: absolute; |
|
width: auto; |
|
height: auto; |
|
padding: 6px; |
|
background-color: white; |
|
opacity: 1; |
|
-webkit-border-radius: 10px; |
|
-moz-border-radius: 10px; |
|
border-radius: 10px; |
|
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); |
|
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); |
|
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); |
|
pointer-events: none; |
|
} |
|
|
|
#tooltip.hidden { |
|
display: none; |
|
} |
|
|
|
#tooltip p { |
|
margin: 0; |
|
font-family: 'Open Sans Condensed', sans-serif; |
|
font-size: 1em; |
|
line-height: 1; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</style> |
|
|
|
</head> |
|
|
|
|
|
<body> |
|
<div id="container"> |
|
<h1>Core Housing Need in Canada - Part 1</h1> |
|
<p>A household is said to be in core housing need if its housing falls |
|
below at least one of the adequacy, affordability or suitability, |
|
standards and it would have to spend 30% or more of its total |
|
before-tax income to pay the median rent of alternative local housing |
|
that is acceptable (meets all three housing standards).<br> |
|
- Adequate housing are reported by their residents as not requiring any |
|
major repairs.<br> |
|
- Affordable dwellings costs less than 30% of total before-tax |
|
household income.<br> |
|
- Suitable housing has enough bedrooms for the size and make-up of |
|
resident households, according to National Occupancy Standard (NOS) |
|
requirements.</p> |
|
<h2>What do Canadians earn?</h2> |
|
<p>Average Household Income ($) by Province and Territory - 2011<br> |
|
Source: <a href="http://cmhc.beyond2020.com/">Canada Mortgage and |
|
Housing Corporation</a>, 2015</p> |
|
</div> |
|
<div id="chartbox"> |
|
<h1>Regional Profile</h1> |
|
</div> |
|
<div id="mapBox"> |
|
|
|
</div> |
|
|
|
<script type="text/javascript"> |
|
|
|
var w = 650; |
|
var h = 450; |
|
var padding = [20, 10, 30, 220]; //Top, right, bottom, left |
|
|
|
var widthScale = d3.scale.linear() |
|
.range([0, w - padding[1] - padding[3]]); |
|
|
|
var heightScale = d3.scale.ordinal() |
|
.rangeBands([padding[1], h / 1.2 - padding[1]], 0.1); |
|
|
|
var xAxis = d3.svg.axis() |
|
.scale(widthScale) |
|
.orient("bottom") |
|
.ticks(7) |
|
//.tickFormat(function(d){return "$" + d; }) |
|
//Sets up the axis with $ sign and , seperators. |
|
.tickFormat(d3.format("$,.0f")); |
|
|
|
var yAxis = d3.svg.axis() |
|
.scale(heightScale) |
|
.orient("left"); |
|
|
|
var svg = d3.select("#container") |
|
.append("svg") |
|
.attr("width", w) |
|
.attr("height", h); |
|
|
|
var svg2 = d3.select("#chartbox") |
|
.append("svg") |
|
.attr("width", w / 2) |
|
.attr("height", h * 1.5) |
|
.attr("id", "redLine"); |
|
|
|
|
|
|
|
/* |
|
var recto = d3.select("g") |
|
.attr("id", "blueLine"); |
|
*/ |
|
|
|
/* |
|
============================ |
|
*/ |
|
|
|
d3.csv("coreHousingNeed-prov-tert.csv", function(data) |
|
{ |
|
data.sort(function(a, b){ |
|
return d3.descending(+a.Average_Household_Income, +b.Average_Household_Income); |
|
}); |
|
widthScale.domain([0, d3.max(data, function(d){ |
|
return +d.Average_Household_Income; |
|
})]); |
|
heightScale.domain(data.map(function(d){ |
|
return d.Geography; |
|
})); |
|
|
|
|
|
/* |
|
var recto2 = svg.selectAll("g") |
|
.data(data) |
|
.enter() |
|
.append("g") |
|
.on("click", function() |
|
{ |
|
// Determine if current line is visible |
|
var active = blueLine.active ? false : true, |
|
newOpacity = active ? 0 : 1; |
|
// Hide or show the elements |
|
d3.select("#blueLine") |
|
.transition() |
|
.duration(2000) |
|
.style("opacity", newOpacity); |
|
// Update whether or not the elements are active |
|
blueLine.active = active; |
|
}) |
|
*/ |
|
var groups = svg.selectAll("g") |
|
.data(data) |
|
.enter() |
|
.append("g") |
|
.attr("class", "bar"); |
|
|
|
//"bar" class help us differentiate these groups |
|
//from the groups created later for axes |
|
|
|
var group = svg2.selectAll("g") |
|
.data(data) |
|
.enter() |
|
.append("g") |
|
.on("click", function(){ |
|
// Determine if current line is visible |
|
var active = redLine.active ? false : true, |
|
newOpacity = active ? 0 : 1; |
|
// Hide or show the elements |
|
d3.select("#redLine") |
|
.transition() |
|
.duration(2000) |
|
.style("opacity", newOpacity); |
|
// Update whether or not the elements are active |
|
redLine.active = active; |
|
}); |
|
//.attr("class", "bar"); |
|
//Add a rect to each group |
|
var rects = groups.append("rect") |
|
.attr("x", padding[3]) |
|
.attr("id", "blueLine") |
|
.attr("y", function(d){ |
|
return heightScale(d.Geography); |
|
}) |
|
.attr("width", 0) |
|
.attr("height", heightScale.rangeBand()) |
|
//.attr("fill", "green"); |
|
.on("click", function(d){ |
|
console.log(d); |
|
}) |
|
.attr({ |
|
fill: function(d) |
|
{ |
|
if (d.Average_Household_Income > 90000){ |
|
return "#FF0033"; |
|
} |
|
else if (d.Average_Household_Income < 70000){ |
|
return "#CCCCFF"; |
|
} |
|
else{ |
|
return "#33CCFF"; |
|
} |
|
} |
|
}); |
|
|
|
/* |
|
.on("click", function(){ |
|
// Determine if current line is visible |
|
var active = blueLine.active ? false : true, |
|
newOpacity = active ? 0 : 1; |
|
// Hide or show the elements |
|
d3.select("#blueLine") |
|
.transition() |
|
.duration(2000) |
|
.style("opacity", newOpacity); |
|
// Update whether or not the elements are active |
|
blueLine.active = active; |
|
}) |
|
|
|
*/ |
|
|
|
|
|
|
|
var numFormat = d3.format("$,"); |
|
//Add a text element to each group |
|
groups.append("text") |
|
.attr("x", function(d) |
|
{ |
|
return padding[3] + widthScale(d.Average_Household_Income) - |
|
3; |
|
}) |
|
.attr("y", function(d) |
|
{ |
|
return heightScale(d.Geography) + 17; |
|
}) |
|
.text(function(d) |
|
{ |
|
return "Average household income" + " " + numFormat(d.Average_Household_Income); |
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
var weebox = group.append("circle") |
|
.attr("r", 90) |
|
.attr("cx", 100) |
|
.attr("cy", 100) |
|
.attr("fill", "#ffffff") |
|
.transition() |
|
.duration(10000) |
|
.attr( |
|
{ |
|
fill: function(d) |
|
{ |
|
if (d.Average_STIR > 100) |
|
{ |
|
return "#FF0033"; |
|
} |
|
else |
|
{ |
|
return "#bdd684"; |
|
} |
|
} |
|
}) |
|
.transition() |
|
.duration(5000) |
|
.attr("cx", 200) |
|
.transition() |
|
.duration(5000) |
|
.attr("cx", 100); |
|
|
|
var rects2 = group.select("circle") |
|
.on("click", function(d){ |
|
window.alert("You clicked the circle and now it will vanish"); |
|
}) |
|
|
|
|
|
rects.transition() |
|
.delay(function(d, i) |
|
{ |
|
return i * 50; |
|
}) |
|
.duration(1000) |
|
.attr("width", function(d) |
|
{ |
|
return widthScale(d.Average_Household_Income); |
|
}); |
|
/* |
|
This simply creates text inside the SVG - can be handy to place info/detail |
|
svg.append("text") |
|
.attr("id","babyText") |
|
.attr("fill-opacity",1) |
|
.attr("x",260) |
|
.attr("y",215) |
|
.attr("fill","steelblue") |
|
.text("babies called Kevin") |
|
.append("tspan") |
|
.attr("x",260) |
|
.attr("dy",20) |
|
.text("born in Zurich"); |
|
*/ |
|
|
|
|
|
|
|
svg.append("g") |
|
.attr("class", "x axis") |
|
.attr("transform", "translate(" + padding[3] + "," + (h / 1.11 - |
|
padding[2]) + ")") |
|
.call(xAxis); |
|
svg.append("g") |
|
.attr("class", "y axis") |
|
.attr("transform", "translate(" + padding[3] + ",0)") |
|
.call(yAxis); |
|
}); |
|
</script> |
|
|
|
|
|
<script type="text/javascript"> |
|
var width = 800, //5000 |
|
height =700, |
|
centered; //2500 |
|
//Define the map projection, scale etc |
|
var projection = d3.geo.albers() |
|
.center([-3.5, 46.4]) |
|
.rotate([121,-11]) |
|
.scale(2600) |
|
.translate([w / 2, h / 2]); |
|
|
|
//define path generator |
|
var path = d3.geo.path() |
|
.projection(projection); |
|
|
|
var svg3 = d3.select("#mapBox") |
|
.append("svg") |
|
.attr("width", w) |
|
.attr("height", h * 1.5); |
|
|
|
d3.json(src="https://gist.githubusercontent.com/majomo/1beba4e212d12f3d6e29/raw/1bd280591bc4959449505395c90f7ffdd2e2ddbd/bcGeo.json", function(json) |
|
{ |
|
|
|
//Bind data and create one path per GeoJSON feature |
|
svg3.selectAll("path") |
|
.data(json.features) |
|
.enter() |
|
.append("path") |
|
.attr("d", path) |
|
// .on('click', function(d) { console.log(d.properties.CDNAME) |
|
|
|
|
|
.on("mousemove", function(d) { |
|
var xPosition = parseFloat(d3.select(this).attr("cx")); |
|
var yPosition = parseFloat(d3.select(this).attr("cy")); |
|
var currentState = this; |
|
d3.select(this).style('fill-opacity', 1); |
|
|
|
//Update the tooltip position and value |
|
d3.select("#tooltip") |
|
.style("left", (d3.event.pageX+20) + "px") |
|
.style("top", (d3.event.pageY ) + "px") |
|
.select("#NAME") |
|
.text(d.properties.CDNAME); |
|
|
|
|
|
|
|
// //Show the tooltip |
|
d3.select("#tooltip").classed("hidden", false); |
|
}) |
|
|
|
.on("mouseout", function() { |
|
d3.selectAll('path') |
|
.style({ |
|
'fill-opacity':10}); |
|
//Hide the tooltip |
|
d3.select("#tooltip").classed("hidden", true); |
|
|
|
|
|
|
|
}); |
|
}); |
|
|
|
|
|
|
|
</script> |
|
|
|
|
|
</body> |
|
|
|
</html> |