|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<meta charset="utf-8"> |
|
<meta name="viewport" content="width=device-width"> |
|
<script src="https://unpkg.com/d3@4.10.0"></script> |
|
<script src="https://unpkg.com/d3-area-label@1.2.0"></script> |
|
<title>Foreign Born in Canada</title> |
|
<style> |
|
body { |
|
margin: 0px; |
|
} |
|
.area-label { |
|
font-family: sans-serif; |
|
fill-opacity: 0.7; |
|
fill: white; |
|
} |
|
path:hover { |
|
fill-opacity: 1; |
|
fill:black; |
|
} |
|
path { |
|
fill-opacity: 0.8; |
|
stroke-width: 0.5; |
|
} |
|
text { |
|
pointer-events: none; |
|
} |
|
.axis--major .tick text, .legend text, .tooltip text { |
|
fill: #585858; |
|
font-family: sans-serif; |
|
font-size: 16pt; |
|
} |
|
.axis--minor .tick text { |
|
display: none; |
|
} |
|
.axis--major .tick line{ |
|
stroke: #ddd; |
|
stroke-width: 2px; |
|
} |
|
.axis--minor .tick line{ |
|
stroke: #eee; |
|
} |
|
.axis .domain { |
|
display: none; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<svg width="960" height="500"></svg> |
|
<script> |
|
|
|
function processChart3Data (chart3Data) { |
|
const keys = Object.keys(chart3Data[0]).filter(key => key !== 'Year'); |
|
const data = chart3Data.map(d => { |
|
d.date = new Date(d.Year); |
|
keys.forEach(key => { |
|
d[key] = +d[key].replace(/,/g, ''); |
|
}); |
|
return d; |
|
}); |
|
return { data, keys }; |
|
} |
|
|
|
function processChart4Data (chart4Data) { |
|
const nameColumn = 'Area of interest'; |
|
const years = Object.keys(chart4Data[0]).filter(key => key !== nameColumn); |
|
const keys = chart4Data.map(d => d[nameColumn]); |
|
const data = years.map(year => { |
|
const row = { |
|
date: new Date(year), |
|
Year: year |
|
}; |
|
chart4Data.forEach(d => { |
|
row[d[nameColumn]] = +d[year].replace(/,/g, ''); |
|
}); |
|
return row; |
|
}); |
|
|
|
return { data, keys }; |
|
} |
|
|
|
d3.queue() |
|
.defer(d3.csv, 'chart3Data.csv') |
|
.defer(d3.csv, 'chart4Data.csv') |
|
.await(function(error, chart3Data, chart4Data) { |
|
const chart3 = processChart3Data(chart3Data); |
|
const chart4 = processChart4Data(chart4Data); |
|
|
|
const chart4DataByYear = {}; |
|
chart4.data.forEach(d => chart4DataByYear[d.Year] = d); |
|
const keys = chart3.keys.concat(chart4.keys); |
|
const data = chart3Data.map(d => { |
|
const chart4d = chart4DataByYear[d.Year]; |
|
chart4.keys.forEach(key => { |
|
d[key] = chart4d ? chart4d[key] : 0; |
|
}); |
|
return d; |
|
}); |
|
|
|
const aggregations = { |
|
'United States': ['United States'], |
|
'British Isles': ['British Isles'], |
|
'Europe': [ |
|
'Western Europe', |
|
'Eastern Europe', |
|
'Scandinavia', |
|
'Southern Europe' |
|
], |
|
'Caribbean and Bermuda': ['Caribbean and Bermuda'], |
|
'Central and South America': ['Central and South America'], |
|
'Africa': [ |
|
'Northern Africa', |
|
'Sub-Saharan Africa' |
|
], |
|
'Asia': [ |
|
'Eastern Asia', |
|
'Southeast Asia', |
|
'Southern Asia' |
|
], |
|
'Oceania': ['Oceania'], |
|
'Middle East': ['Western Asia and Middle East'] |
|
}; |
|
|
|
const aggregatedData = data.map(d => { |
|
const row = { date: d.date}; |
|
Object.keys(aggregations).forEach(aggregation => { |
|
row[aggregation] = 0; |
|
aggregations[aggregation].forEach(key => { |
|
row[aggregation] += d[key]; |
|
}); |
|
}); |
|
return row; |
|
}); |
|
|
|
render(aggregatedData, Object.keys(aggregations)); |
|
}); |
|
|
|
var margin = { top: 0, bottom: 25, left: 0, right: 20 }; |
|
|
|
var svg = d3.select('svg'); |
|
var width = +svg.attr('width'); |
|
var height = +svg.attr('height'); |
|
|
|
var g = svg.append('g') |
|
.attr('transform', `translate(${margin.left},${margin.top})`); |
|
var xAxisG = g.append('g') |
|
.attr('class', 'axis'); |
|
var xAxisMinorG = xAxisG.append('g') |
|
.attr('class', 'axis axis--minor'); |
|
var xAxisMajorG = xAxisG.append('g') |
|
.attr('class', 'axis axis--major'); |
|
var marksG = g.append('g'); |
|
|
|
var stack = d3.stack() |
|
.offset(d3.stackOffsetWiggle) |
|
.order(d3.stackOrderInsideOut) |
|
; |
|
var xValue = function (d) { return d.date; }; |
|
var xScale = d3.scaleTime(); |
|
var yScale = d3.scaleLinear(); |
|
var colorScale = d3.scaleOrdinal().range(d3.schemeCategory10); |
|
|
|
var xAxisMajor = d3.axisBottom().scale(xScale); |
|
var xAxisMinor = d3.axisBottom().scale(xScale).ticks(30); |
|
|
|
var area = d3.area() |
|
.x(d => xScale(xValue(d.data))) |
|
.y0(d => yScale(d[0])) |
|
.y1(d => yScale(d[1])) |
|
.curve(d3.curveBasis); |
|
|
|
// Render StreamGraph |
|
function render(data, keys) { |
|
stack.keys(keys); |
|
var stacked = stack(data); |
|
|
|
var innerWidth = width - margin.right - margin.left; |
|
var innerHeight = height - margin.top - margin.bottom; |
|
|
|
xScale |
|
.domain(d3.extent(data, xValue)) |
|
.range([0, innerWidth]); |
|
|
|
yScale |
|
.domain([ |
|
d3.min(stacked, function (series) { |
|
return d3.min(series, function (d) { return d[0]; }); |
|
}), |
|
d3.max(stacked, function (series) { |
|
return d3.max(series, function (d) { return d[1]; }); |
|
}) |
|
]) |
|
.range([innerHeight, 0]); |
|
|
|
colorScale.domain(d3.range(keys.length)); |
|
|
|
var paths = marksG.selectAll('path').data(stacked); |
|
var pathsEnter = paths |
|
.enter().append('path'); |
|
pathsEnter.merge(paths) |
|
.attr('fill', function (d) { return colorScale(d.index); }) |
|
.attr('stroke', function (d) { return colorScale(d.index); }) |
|
.attr('d', area); |
|
|
|
paths.select('title') |
|
.merge(pathsEnter.append('title')) |
|
.text(function (d) { return d.key; }) |
|
|
|
var labels = marksG.selectAll('text').data(stacked) |
|
labels |
|
.enter().append('text') |
|
.attr('class', 'area-label') |
|
.merge(labels) |
|
.text(function (d) { return d.key; }) |
|
.attr('transform', d3.areaLabel(area).interpolateResolution(1000)); |
|
|
|
xAxisMajor.tickSize(-innerHeight); |
|
xAxisMinor.tickSize(-innerHeight); |
|
|
|
xAxisG.attr('transform', `translate(0,${innerHeight})`); |
|
xAxisMajorG.call(xAxisMajor); |
|
xAxisMinorG.call(xAxisMinor); |
|
} |
|
</script> |
|
</body> |
|
</html> |