|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style type="text/css"> |
|
/* Headers */ |
|
h2 { |
|
text-align: center; |
|
font-family: Arial; |
|
width: 1000; |
|
} |
|
h3 { |
|
text-align: center; |
|
font-family: Arial; |
|
top: 210px; |
|
left: 8px; |
|
position: absolute; |
|
width: 170px; |
|
font-size: 14px; |
|
} |
|
main { |
|
/*text-align: center;*/ |
|
font-family: Arial; |
|
left: 8px; |
|
width: 950px; |
|
font-size: 14px; |
|
padding-bottom: 5px; |
|
padding-left: 10px; |
|
} |
|
|
|
/* On mouse hover, lighten state color */ |
|
path:hover { |
|
fill-opacity: .7; |
|
} |
|
|
|
/* On mouse hover, lighten state color */ |
|
path:hover { |
|
fill-opacity: .7; |
|
} |
|
/* Style for Custom Tooltip */ |
|
div.tooltip { |
|
position: absolute; |
|
text-align: center; |
|
width: 100px; |
|
height: 28px; |
|
padding: 2px; |
|
font: bold 12px sans-serif; |
|
background: white; |
|
border: 0px; |
|
border-radius: 8px; |
|
pointer-events: none; |
|
} |
|
|
|
/* Style for buttons */ |
|
div.regions_buttons { |
|
position: absolute; |
|
top: 255px; |
|
left: 10px; |
|
} |
|
div.regions_buttons div { |
|
background-color: rgb(65,182,196); |
|
color: white; |
|
padding: 3px; |
|
margin: 7px; |
|
border-radius: 8px; |
|
text-align: center; |
|
font-family: "Arial"; |
|
opacity: .75; |
|
} |
|
|
|
</style> |
|
<body> |
|
<title>D3: Where is the South?</title> |
|
<script type="text/javascript" src="https://d3js.org/d3.v3.min.js"></script> |
|
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.12.0/d3-legend.js"></script> |
|
<script type="text/javascript"> |
|
|
|
//Width and height |
|
var w = 1000; |
|
var h = 600; |
|
//Define map projection |
|
var projection = d3.geo.albersUsa() |
|
.translate([w/2, h/2]) |
|
.scale([1000]); |
|
//Define path generator |
|
var path = d3.geo.path() |
|
.projection(projection); |
|
|
|
//Define quantize scale to sort data values into buckets of color |
|
//****CONSIDER MAKING SCALE LINEAR**** |
|
var color = d3.scale.quantize() |
|
.range(['rgb(255,255,204)','rgb(161,218,180)','rgb(65,182,196)','rgb(44,127,184)','rgb(37,52,148)']); |
|
//Colors taken from colorbrewer.js, included in the D3 download |
|
|
|
//create title |
|
d3.select("body") |
|
.append("h2") |
|
.text("Where is the South? "); |
|
//create description |
|
d3.select("body") |
|
.append("main") |
|
.html("There was a great article on <a href='http://fivethirtyeight.com/datalab/which-states-are-in-the-south/'>FiveThirtyEight </a> about which states are actually in the south, where users were asked to vote on which states were truly southern. 2,528 individuals from across the country responded (1,135 of which considered themselves southern). </br>" + |
|
"As a North Carolinan at heart I was surprised to see that my home state <i>(highlighted in red)</i> was not considered to be in the 'most southern' category according to respondents around the country (the default view). However, I was comforted to see that those who actually live in the <b>South Atlantic</b> do consider NC southern (and that is what really matters). Explore how where what region you are from influences your opinion of which states are 'southern' on your own with the filter buttons on the left hand side!</br></br>" + |
|
"<i>Note: Hover over a state to see exactly how many people that participated in the poll (from the region selected on the left) thought that state was 'southern'.</i>"); |
|
|
|
//Create SVG element |
|
var svg = d3.select("body") |
|
.append("svg") |
|
.attr("width", w) |
|
.attr("height", h); |
|
|
|
// Append Div for tooltip to SVG |
|
var div = d3.select("body") |
|
.append("div") |
|
.attr("class", "tooltip") |
|
.style("opacity", 0); |
|
//Load in voting data |
|
d3.csv("SOUTH_transformed_2.csv", function(data) { |
|
//Get distinct regions from the data |
|
var regions = d3.map(data, function(d) { |
|
return d.Region; |
|
}).keys(); |
|
//add one filter for ALL responses |
|
regions.push("All"); |
|
//sort alphabetically |
|
regions.sort(); |
|
//Add None option to the end |
|
regions.push("None"); |
|
//Add buttons for each region |
|
var buttons = d3.select("body") |
|
.append("div") |
|
.attr("class", "regions_buttons") |
|
.selectAll("div") |
|
.data(regions) |
|
.enter() |
|
.append("div") |
|
.text(function(d) { |
|
return d; |
|
}) |
|
.style("background-color", function(d) { |
|
if (d == "All") { |
|
return 'rgb(37,52,148)'; |
|
} else { |
|
return null; |
|
} |
|
}); |
|
|
|
//Add header for the filter buttons |
|
d3.select("body") |
|
.append("h3") |
|
.text("Respondent Region Filters"); |
|
|
|
function update(region) { |
|
//aggregate votes by state |
|
function agg_state(leaves) { |
|
var total = d3.sum(leaves, function(d) { |
|
if(d['Region'] == region || region == "All") { |
|
return d['Votes']; |
|
} else { |
|
return null; |
|
} |
|
}); |
|
return { |
|
'Votes' : total |
|
}; |
|
}; |
|
var nested = d3.nest() |
|
.key(function(d) { |
|
return d['State']; |
|
}) |
|
.rollup(agg_state) |
|
.entries(data); |
|
|
|
//Set input domain for color scale |
|
color.domain([ |
|
d3.min(nested, function(d) { return d.values.Votes; }), |
|
d3.max(nested, function(d) { return d.values.Votes; }) |
|
]); |
|
|
|
//make legend |
|
svg.append("g") |
|
.attr("class", "legendLinear") |
|
.attr("transform", "translate(8,550)"); |
|
var legendLinear = d3.legend.color() |
|
.labelFormat(d3.format(".0f")) |
|
.shapeWidth(70) |
|
.orient('horizontal') |
|
.scale(color); |
|
svg.select(".legendLinear") |
|
.call(legendLinear) |
|
.style("font-family", "Arial") |
|
.style("font-size", "11px"); |
|
|
|
//load states data |
|
d3.json("us-states.json", function(json) { |
|
//Merge the ag. data and GeoJSON |
|
//Loop through once for each ag. data value |
|
for (var i = 0; i < nested.length; i++) { |
|
|
|
//Grab state name |
|
var dataState = nested[i].key; |
|
|
|
//Grab data value, and convert from string to float |
|
var dataValue = +nested[i].values.Votes; |
|
|
|
//Find the corresponding state inside the GeoJSON |
|
for (var j = 0; j < json.features.length; j++) { |
|
|
|
var jsonState = json.features[j].properties.name; |
|
|
|
if (dataState == jsonState) { |
|
|
|
//Copy the data value into the JSON |
|
json.features[j].properties.value = dataValue; |
|
|
|
//Stop looking through the JSON |
|
break; |
|
} |
|
} |
|
} |
|
|
|
//Bind data and create one path per GeoJSON feature |
|
var state = svg.selectAll("path") |
|
.data(json.features); |
|
|
|
state.enter() |
|
.append("path") |
|
.attr("d", path); |
|
|
|
state.transition() |
|
.duration(200) |
|
//custom style for the border |
|
.style("stroke", function(d) { |
|
//Get data value |
|
var value = d.properties.value; |
|
var name = d.properties.name; |
|
|
|
if (value && value > 0) { |
|
//If value exists… |
|
console.log(name + ": " + value) |
|
//Give NC a red border, and everything else a black one |
|
if (name == "North Carolina") { |
|
return "red"; |
|
} else { |
|
return "black"; |
|
} |
|
} else { |
|
//If value is undefined… |
|
return null; |
|
} |
|
}) |
|
//custom colors for each state based upon how many votes they have |
|
.style("fill", function(d) { |
|
//Get data value |
|
var value = d.properties.value; |
|
|
|
if (value && value > 0) { |
|
//If value exists… |
|
console.log(d.properties.name + ": " + value) |
|
return color(value); |
|
} else { |
|
//If value is undefined… |
|
return "#ccc"; |
|
} |
|
}) |
|
.style("stroke-width", function(d) { |
|
//make NC's stroke width bigger |
|
var name = d.properties.name; |
|
if (name == "North Carolina") { |
|
return "2px"; |
|
} else { |
|
return ".5px"; |
|
} |
|
}); |
|
|
|
// Modification of custom tooltip code provided by Malcolm Maclean, "D3 Tips and Tricks" |
|
// http://www.d3noob.org/2013/01/adding-tooltips-to-d3js-graph.html |
|
state.on("mouseover", function(d) { |
|
//handle undefined values |
|
function null_votes(d) { |
|
var votes = d.properties.value; |
|
|
|
if (votes) { |
|
//if value exists |
|
return votes; |
|
} else { |
|
//value is undefined |
|
return 0; |
|
} |
|
}; |
|
|
|
div.transition() |
|
.duration(200) |
|
.style("opacity", .9); |
|
div.text(d.properties.name + ": " + null_votes(d)) |
|
.style("left", (d3.event.pageX) + "px") |
|
.style("top", (d3.event.pageY - 28) + "px"); |
|
}) |
|
// fade out tooltip on mouse out |
|
.on("mouseout", function(d) { |
|
div.transition() |
|
.duration(500) |
|
.style("opacity", 0); |
|
}); |
|
|
|
|
|
|
|
buttons.on("click", function(d) { |
|
d3.select(".regions_buttons") |
|
.selectAll("div") |
|
.transition() |
|
.duration(500) |
|
.style("color", "white") |
|
.style("background", "rgb(65,182,196)"); |
|
|
|
d3.select(this) |
|
.transition() |
|
.duration(500) |
|
.style("background", "rgb(37,52,148)") |
|
.style("color", "white"); |
|
setTimeout(update(d),500); |
|
}) |
|
}); |
|
}; |
|
update("All"); |
|
|
|
}); |
|
</script> |
|
</body> |