$(document).bind("pageinit", init); var chartMargin = {top: 20, right: 20, bottom: 30, left: 80}, chartWidth = 300 - chartMargin.left - chartMargin.right, chartHeight = 200 - chartMargin.top - chartMargin.bottom; var chartColor = d3.scale.ordinal() .domain(["Dem","Toss up","GOP"]) .range(["#2166AC","#B7B7B7","#B2182B"]); var x = d3.scale.ordinal() .domain(["Dem","GOP","Toss up"]) .rangeRoundBands([0, chartWidth], .1) var y = d3.scale.linear() .domain([0, 538]) .range([chartHeight, 0]); var xAxis = d3.svg.axis() .scale(x) .orient("bottom"); var yAxis = d3.svg.axis() .scale(y) .orient("left") .tickValues([270, 538]); var stateColor = d3.scale.linear() .clamp(true) .domain([-7,0,7]) .range(["#2166AC","#B7B7B7","#B2182B"]); var dateFormatter = d3.time.format("%b %d, %Y"); var selectedState = null; var transDur = 200; var electionDataDate = 1199163600000, // Jan 01, 2008 (2008 election data) firstDate = electionDataDate, lastDate = 0; var totalVotes = [ {"party": "Dem", "votes": 0}, {"party": "GOP", "votes": 0}, {"party": "Toss up", "votes": 0} ]; function init() { var w = 650, h = 400; var path = d3.geo.path(); var cartogramSvg = d3.select("#cartogram").append("svg:svg").attr("width", w).attr("height", h); d3.json("us-states.json", function(states) { var nodes = [], links = []; var temp; var maxVotes = 55; //CA var baseArea = 5000; //base pixel area for rescaling states var i = 0; // compute new area for each state states.features.forEach(function(d, j) { d.properties.origArea = path.area(d.geometry); d.properties.newArea = baseArea * d.properties.votes / maxVotes; // put polls in chronological order d.properties.polls = d.properties.polls.reverse(); // indicate the initial selection is the most recent poll d.properties.selectedPoll = d.properties.polls.length -1; for (i = 0; i < d.properties.polls.length; ++i) { // the first "poll" is the 2008 election with Obama v. McCain // all other polls were in 2012 var pollDate = new Date(d.properties.polls[i].date + (i == 0 ? ",2008" : ",2012")); d.properties.polls[i].date = pollDate; if (i > 0 && (pollDate.getTime() < firstDate || firstDate == electionDataDate)) firstDate = pollDate.getTime(); if (pollDate.getTime() > lastDate) lastDate = pollDate.getTime(); } }); // Election 2008/2012 toggle $("input[type='radio']").bind( "change", function(event, ui) { if (event.target.value == "election2008") { $("#date-slider").slider("disable"); updatePollSelection(electionDataDate); updateStateColors(); stateClick(selectedState); updateChart(); } else { $("#date-slider").slider("enable"); updatePollSelection($("#date-slider").attr("value")); updateStateColors(); stateClick(selectedState); updateChart(); } }); // ensure the 2012 radio button is checked and the slider is enabled $("input#election-2008-radio").attr("checked",false).checkboxradio("refresh"); $("input#election-2012-radio").attr("checked",true).checkboxradio("refresh"); $("#date-slider").slider("enable"); // update the slider with the min and max dates $("#date-slider") .attr("min", firstDate) .attr("value", lastDate) .attr("max", lastDate) .slider("refresh"); $("#date-slider").change(function(event) { updatePollSelection(+event.target.value); updateStateColors(); stateClick(selectedState); updateChart(); }); d3.select("#date-slider-min-label").text(dateFormatter(new Date(firstDate))); d3.select("#date-slider-max-label").text(dateFormatter(new Date(lastDate))); d3.select("#date-slider-value-label").text(dateFormatter(new Date(lastDate))); states.features.forEach(function(d, i) { var centroid = path.centroid(d); centroid.x = centroid[0]; centroid.y = centroid[1]; centroid.feature = d; nodes.push(centroid); }); var node = cartogramSvg.selectAll("g") .data(nodes) .enter() .append("svg:g") .append("svg:path") .attr("class", "state") .attr("transform", function(d) { return d.feature.transform; }) .attr("d", function(d) { return path(d.feature); }) .attr("title", function(d) { return d.feature.properties.name + ": " + d.feature.properties.votes + " votes"; }) .style("stroke-width", function(d) { return 1 / Math.sqrt(d.feature.properties.newArea/d.feature.properties.origArea); }) .style("fill", function(d) { var selectedPoll = d.feature.properties.polls[d.feature.properties.selectedPoll]; var diff = selectedPoll.gop - selectedPoll.dem; return stateColor(diff); }) .on("click", function(d) { d3.selectAll("path.state").classed("selected", false); d3.select(this).classed("selected", true); stateClick(d); }); // find the poll for each state and calculate votes updatePollSelection($("#date-slider").attr("value")); var chartSvg = d3.select("#vote-chart").append("svg") .attr("width", chartWidth + chartMargin.left + chartMargin.right) .attr("height", chartHeight + chartMargin.top + chartMargin.bottom) .append("g") .attr("transform", "translate(" + chartMargin.left + "," + chartMargin.top + ")"); chartSvg.append("g") .attr("class", "title") .append("text") .attr("x", 25) .text("US Electoral Votes"); chartSvg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + chartHeight + ")") .call(xAxis); chartSvg.append("g") .attr("class", "y axis") .call(yAxis); chartSvg.selectAll(".bar").data(totalVotes) .enter().append("rect") .attr("class", "bar") .attr("x", function(d) { return x(d.party); }) .attr("width", x.rangeBand()) .attr("y", function(d) { return y(d.votes); }) .attr("height", function(d) { return chartHeight - y(d.votes); }) .attr("title", function(d) { return d.votes + " votes"; }) .style("fill", function(d) { return chartColor(d.party); }); }); } function updatePollSelection(date) { var currDate = new Date(+date); // update slider label if (date == electionDataDate) { d3.select("#date-slider-value-label").text("Election results: "); d3.select("#date-slider-value").text("2008"); } else { d3.select("#date-slider-value-label").text("Polls on or before: "); d3.select("#date-slider-value").text(dateFormatter(currDate)); } totalVotes[0].votes = 0; totalVotes[1].votes = 0; totalVotes[2].votes = 0; d3.selectAll("path.state").each(function(d) { var i = 0; var poll; // find the first date >= currently selected date // if no poll after selected date, use the most recent poll // if no poll available, uses Election 2008 for (i = 0; i < d.feature.properties.polls.length; ++i) { poll = d.feature.properties.polls[i]; if (poll.date > currDate) { break; } d.feature.properties.selectedPoll = i; } // recompute the electoral vote totals if (poll.dem - poll.gop > 2) { // Dem winning totalVotes[0].votes += d.feature.properties.votes; } else if (poll.gop - poll.dem > 2) { //GOP winning totalVotes[1].votes += d.feature.properties.votes; } else { //toss up totalVotes[2].votes += d.feature.properties.votes; } }); } function updateStateColors() { d3.selectAll("path.state").transition() .duration(transDur) .style("fill", function(d) { var poll = d.feature.properties.polls[d.feature.properties.selectedPoll]; return stateColor(poll.gop - poll.dem); }); } function updateChart() { d3.selectAll(".bar").data(totalVotes) .transition().duration(transDur) .attr("y", function(d) { return y(d.votes); }) .attr("height", function(d) { return chartHeight - y(d.votes); }) .attr("title", function(d) { return d.votes + " votes"; }); } function stateClick(d) { if (d === null) return; var selectedPoll = d.feature.properties.polls[d.feature.properties.selectedPoll]; var detailsHtml = "

" + d.feature.properties.name + "

"; detailsHtml += "

# of votes: " + d.feature.properties.votes + "

"; detailsHtml += "

# of polls: " + (d.feature.properties.polls.length -1) + "

"; detailsHtml += "

" + "Displaying poll: " + selectedPoll.pollster + "

"; detailsHtml += "

" + "Date conducted: " + dateFormatter(selectedPoll.date) + "

"; detailsHtml += "

" + "% Dem: " + selectedPoll.dem + "

"; detailsHtml += "

" + "% GOP: " + selectedPoll.gop + "

"; d3.select("#state-details").html(detailsHtml); selectedState = d; }