Skip to content

Instantly share code, notes, and snippets.

@JenHLab
Created March 21, 2016 20:15
Show Gist options
  • Save JenHLab/681648ebf01471b351f1 to your computer and use it in GitHub Desktop.
Save JenHLab/681648ebf01471b351f1 to your computer and use it in GitHub Desktop.
City Drove_Alone Carpooled Public_Transportation Walked Bicycle Taxi_Motorcycle Work_at_Home
Washington DC 109902 18748 122831 40082 12606 3878 15192
Los Angeles 1194792 173981 193510 63911 19528 28405 102193
San Francisco 165314 34250 150244 47037 17353 10503 32423
New York City 822821 177907 2113347 379800 37604 63926 150416
Boston 126543 20968 109104 48163 6225 2948 12122
Seattle 185236 30509 71188 33778 13438 4721 24335
Chicago 609654 108735 332316 81857 17104 18326 53757
Houston 775255 124612 43920 20428 5107 19406 32685
Dallas 444209 63458 23652 10384 1153 9807 24229
Atlanta 138885 15409 20275 9326 1622 2433 14800
Detroit 141216 25064 18135 6724 8151 4075 6724
Miami 126948 17244 20913 8255 1651 2201 6053
<!DOCTYPE html>
<!-- code loosely inspired by this block https://gist.github.com/mstanaland/6100713 -->
<meta charset="utf-8">
<head>
<link href='https://fonts.googleapis.com/css?family=Amiri' rel='stylesheet' type='text/css'>
<style>
body {
font-family: serif;
font: 12px;
padding: 50px;
}
h1{
font-family: 'Amiri', serif;
}
#form {
position: relative;
right: 10px;
top: 10px;
padding-bottom: 20px;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
.tooltip {
position: absolute;
z-index: 10;
}
.tooltip p {
background-color: white;
border: gray 1px solid;
padding: 2px;
max-width: 180px;
}
</style>
</head>
<body>
<h1>Commuting Preferences in Top Congested U.S. Cities</h1>
<div id="form">
<label><input type="radio" name="mode" value="bycount" checked>Total Commuters</label>
<label><input type="radio" name="mode" value="bypercent">As Percent of Commuting Preferences</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 = 980, fullheight = 500;
var margin = {top: 20, right: 170, bottom: 80, left: 80},
width = fullwidth - margin.left - margin.right,
height = fullheight - margin.top - margin.bottom;
var xScale = d3.scale.ordinal()
.rangeRoundBands([0, width], .3);
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")
.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("cities_commuters.csv", function(error, data) {
if (error) {
console.log(error);
}
data.sort(function(a, b) {return d3.ascending(a.City,b.City);});
// how would we sort by largest total bar? what would we have to calculate?
var cities = ["Drove_Alone","Carpooled","Public_Transportation","Walked",
"Bicycle","Taxi_Motorcycle","Work_at_Home"];
color.domain(cities);
xScale.domain(data.map(function(d) { return d.City; }));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.attr("dy", ".5em")
.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("Commuters");
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(cities, data) {
return cities.map(function(city) {
return data.map(function(d) {
return {x: d.City, y: +d[city], city: city};
})
});
}
function transitionPercent() {
yAxis.tickFormat(d3.format("%"));
stack.offset("expand"); // use this to get it to be relative/normalized!
var stacked = stack(makeData(cities, 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(cities, data));
transitionRects(stacked);
}
function transitionRects(stacked) {
// this domain is using the last of the stacked arrays, which is the last city, and getting the max height.
yScale.domain([0, d3.max(stacked[stacked.length-1], function(d) { return d.y0 + d.y; })]);
var city = svg.selectAll("g.city")
.data(stacked);
city.enter().append("g")
.attr("class", "city")
.style("fill", function(d, i) { return color(d[0].city); });
// then data for each, plus mouseovers - a nested selection/enter here
city.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.
city.selectAll("rect")
.transition()
.duration(250)
.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
city.exit().remove(); // there's actually nothing removed here - we just transition.
svg.selectAll(".y.axis").transition().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 cities_reversed = cities.slice().reverse();
var legend = svg.selectAll(".legend")
.data(cities_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 cities_reversed[i].replace(/_/g, " "); });
}
function mouseover(d) {
// this will highlight both a dot and its line.
var number;
d3.select(this)
.transition()
.style("stroke", "black");
if (currentMode == "bypercent") {
number = d3.format(".1%")(d.y);
} else {
number = d.y;
}
tooltip
.style("display", null) // this removes the display none setting from it
.html("<p>Mode of Transportation: " + d.city.replace(/_/g, " ") +
"<br>Commuters: " + number +
"<br>City: " + 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>
<p><i>Sources:</i><br>
<a href="http://factfinder.census.gov/faces/nav/jsf/pages/index.xhtml">American FactFinder. US Census Bureau. 2016.</a>.
<br>
<a href="http://d2dtl5nnlpfr0r.cloudfront.net/tti.tamu.edu/documents/mobility-scorecard-2015.pdf">2015 Urban Mobility Scorecard. Texas A&amp;M Transportation Institute. 2016.</a>.
</p>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment