Last active
October 14, 2016 15:55
-
-
Save jashcny/549b69d1b59ca48b52ac to your computer and use it in GitHub Desktop.
Week9: Stacked Transition.
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
<!DOCTYPE html> | |
<!-- code loosely inspired by this block https://gist.github.com/mstanaland/6100713 --> | |
<meta charset="utf-8"> | |
<style> | |
@import url(https://fonts.googleapis.com/css?family=Dosis); | |
@import url(https://fonts.googleapis.com/css?family=Raleway:400); | |
@import url(https://fonts.googleapis.com/css?family=Raleway:400italic|Poiret+One|Ubuntu|Oxygen); | |
@import url(https://fonts.googleapis.com/css?family=Raleway:400italic|Poiret+One|Ubuntu); | |
body { | |
font: 12px sans-serif; | |
padding: 50px; | |
margin-left: 20%; | |
margin-bottom:100px; | |
} | |
h2 { | |
margin-left: 140px; | |
margin-bottom: 50px; | |
font-family: 'Dosis', sans-serif; | |
font-size: 28px; | |
color: rgb(233, 42, 58); | |
} | |
p { | |
font-size: 16px; | |
margin-left: 20px; | |
font-family: 'Ubuntu', sans-serif; | |
} | |
#form { | |
position: relative; | |
right: 2px; | |
top: 20px; | |
font-size: 13px; | |
font-family: 'Ubuntu', sans-serif; | |
} | |
.legend { | |
font-size: 13px; | |
font-family: 'Ubuntu', sans-serif; | |
} | |
svg { | |
background-color: rgba(203,222,229,0.5) ; | |
padding-left: 25px; | |
margin-top: 40px; | |
} | |
.axis { | |
font-size: 13px; | |
font-family: 'Ubuntu', sans-serif; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
.x.axis path { | |
display: none; | |
} | |
.tooltip { | |
position: absolute; | |
z-index: 10; | |
opacity: 1; | |
} | |
.tooltip p { | |
width: initial; | |
font-family: "Open Sans", sans-serif; | |
line-height: 1.4; | |
color: black; | |
font-size: 13px; | |
background-color: rgba(255,255,255,0.8); | |
border: rgba(230,230,230,1) 1px solid; | |
padding: 5px 7px 5px 7px; | |
border-radius: 10px; | |
} | |
</style> | |
<body> | |
<h2>An estimation of major religious group</h2> | |
<p>This Stacked bars illustrate estimated religion by top 5 countries in 2010.</p> | |
<p>Source: A report of the Global Religious Landscape from <a href="http://www.pewforum.org/2012/12/18/global-religious-landscape-exec/">Pew Research Center.</a></p> | |
<div id="form"> | |
<label><input type="radio" name="mode" value="bycount" checked>Raw Count</label> | |
<label><input type="radio" name="mode" value="bypercent">As Percent of Religion</label> | |
</div> | |
<div id="chart"></div> | |
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script> | |
<script> | |
var currentMode = "bycount"; | |
var fullwidth = 700, fullheight = 530; | |
var margin = {top: 20, right: 150, bottom: 50, left: 40}, | |
width = fullwidth - margin.left - margin.right, | |
height = fullheight - margin.top - margin.bottom; | |
var xScale = d3.scale.ordinal() | |
.rangeRoundBands([0, width], .6); | |
var yScale = d3.scale.linear() | |
.rangeRound([height, 0]); | |
var color = d3.scale.category20(); | |
var xAxis = d3.svg.axis() | |
.scale(xScale) | |
.orient("bottom") | |
.innerTickSize([0]); | |
var yAxis = d3.svg.axis() | |
.scale(yScale) | |
.orient("left") | |
.ticks(11) | |
.tickFormat(d3.format(".2s")); // for the stacked totals version | |
var stack = d3.layout | |
.stack(); // default view is "zero" for the count display. | |
var svg = d3.select("#chart").append("svg") | |
.attr("width", fullwidth) | |
.attr("height", fullheight) | |
.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
var tooltip = d3.select("body").append("div").attr("class", "tooltip"); | |
d3.csv("religion.csv", function(error, data) { | |
if (error) { | |
console.log(error); | |
} | |
data.sort(function(a, b) {return d3.ascending(a.Country,b.Country);}); | |
// how would we sort by largest total bar? what would we have to calculate? | |
var religions = ["Christian","Muslim","Unaffiliated","Hindu", | |
"Buddhist","Folk_Religions","Other_Religions","Jewish"]; | |
color.domain(religions); | |
xScale.domain(data.map(function(d) { return d.Country; })); | |
svg.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + height + ")") | |
.call(xAxis) | |
.selectAll("text") | |
.attr("dy", "1em") | |
.attr("transform", "rotate(-30)") | |
.style("text-anchor", "end"); | |
svg.append("g") | |
.attr("class", "y axis") | |
.call(yAxis) | |
.append("text") | |
.attr("transform", "rotate(-90)") | |
.attr("y", 6) | |
.attr("dy", ".71em") | |
.style("text-anchor", "end") | |
.text("people"); | |
transitionCount(); // this will use the by-count stack, and make the data, and draw. | |
drawLegend(); | |
d3.selectAll("input").on("change", handleFormClick); | |
// All the functions for stuff above! | |
function handleFormClick() { | |
if (this.value === "bypercent") { | |
currentMode = "bypercent"; | |
transitionPercent(); | |
} else { | |
currentMode = "bycount"; | |
transitionCount(); | |
} | |
} | |
function makeData(religions, data) { | |
return religions.map(function(religion) { | |
return data.map(function(d) { | |
return {x: d.Country, y: +d[religion], religion: religion}; | |
}) | |
}); | |
} | |
function transitionPercent() { | |
yAxis.tickFormat(d3.format("%")); | |
stack.offset("expand"); // use this to get it to be relative/normalized! | |
var stacked = stack(makeData(religions, data)); | |
// call function to do the bars, which is same across both formats. | |
transitionRects(stacked); | |
} | |
function transitionCount() { | |
yAxis.tickFormat(d3.format(".2s")); // for the stacked totals version | |
stack.offset("zero"); | |
var stacked = stack(makeData(religions, data)); | |
transitionRects(stacked); | |
} | |
function transitionRects(stacked) { | |
// this domain is using the last of the stacked arrays, which is the last illness, and getting the max height. | |
yScale.domain([0, d3.max(stacked[stacked.length-1], function(d) { return d.y0 + d.y; })]); | |
var religion = svg.selectAll("g.religion") | |
.data(stacked); | |
religion.enter().append("g") | |
.attr("class", "religion") | |
.style("fill", function(d, i) { return color(d[0].religion); }); | |
// then data for each, plus mouseovers - a nested selection/enter here | |
religion.selectAll("rect") | |
.data(function(d) { | |
console.log("array for a rectangle", d); | |
return d; }) // this just gets the array for bar segment. | |
.enter().append("rect") | |
.attr("width", xScale.rangeBand()) | |
.on("mouseover", mouseover) | |
.on("mousemove", mousemove) | |
.on("mouseout", mouseout); | |
// the thing that needs to transition is the rectangles themselves, not the g parent. | |
religion.selectAll("rect") | |
.transition() | |
.duration(1000) | |
.attr("x", function(d) { | |
return xScale(d.x); }) | |
.attr("y", function(d) { | |
return yScale(d.y0 + d.y); }) // | |
.attr("height", function(d) { | |
return yScale(d.y0) - yScale(d.y0 + d.y); }); // height is base - tallness | |
religion.exit().remove(); // there's actually nothing removed here - we just transition. | |
svg.selectAll(".y.axis").transition().duration(1000).call(yAxis); | |
} | |
// Building a legend by hand, based on http://bl.ocks.org/mbostock/3886208 | |
function drawLegend() { | |
// reverse to get the same order as the bar color layers | |
var religions_reversed = religions.slice().reverse(); | |
var legend = svg.selectAll(".legend") | |
.data(religions_reversed) // make sure your labels are in the right order -- if not, use .reverse() here. | |
.enter().append("g") | |
.attr("class", "legend") | |
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; }); | |
legend.append("rect") | |
.attr("x", width) | |
.attr("width", 18) | |
.attr("height", 18) | |
.style("fill", function(d) {return color(d)}); | |
legend.append("text") | |
.attr("x", width + 24) | |
.attr("y", 9) | |
.attr("dy", ".35em") | |
.style("text-anchor", "start") | |
.text(function(d, i) { return religions_reversed[i].replace(/_/g, " "); }); | |
} | |
function mouseover(d) { | |
// this will highlight both a dot and its line. | |
var number; | |
d3.select(this) | |
.transition() | |
.style("stroke", "white"); | |
if (currentMode == "bypercent") { | |
number = d3.format(".1%")(d.y); | |
} else { | |
number = d3.format(",")(d.y); | |
} | |
tooltip | |
.style("display", null) // this removes the display none setting from it | |
.html("<p>Religion: " + d.religion.replace(/_/g, " ") + | |
"<br>People: " + number + | |
"<br>Country: " + d.x + " </p>"); | |
} | |
function mousemove(d) { | |
tooltip | |
.style("top", (d3.event.pageY - 10) + "px" ) | |
.style("left", (d3.event.pageX + 10) + "px"); | |
} | |
function mouseout(d) { | |
d3.select(this) | |
.transition() | |
.style("stroke", "none"); | |
tooltip.style("display", "none"); // this sets it to invisible! | |
} | |
}); | |
</script> |
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
Country | Christian | Muslim | Unaffiliated | Hindu | Buddhist | Folk_Religions | Other_Religions | Jewish | |
---|---|---|---|---|---|---|---|---|---|
China | 68410000 | 24690000 | 700680000 | 0 | 244130000 | 294320000 | 9080000 | 0 | |
India | 31130000 | 176190000 | 870000 | 973750000 | 9250000 | 5840000 | 27560000 | 0 | |
USA | 243060000 | 2770000 | 50980000 | 1790000 | 3570000 | 0 | 1900000 | 5690000 | |
Indonesia | 23660000 | 209120000 | 0 | 4050000 | 0 | 0 | 0 | 0 | |
Brazil | 173300000 | 0 | 15410000 | 0 | 0 | 5540000 | 0 | 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment