Skip to content

Instantly share code, notes, and snippets.

@alecrajeev
Last active April 11, 2016 01:03
Show Gist options
  • Save alecrajeev/7e09253531725c3877305415ccb1a313 to your computer and use it in GitHub Desktop.
Save alecrajeev/7e09253531725c3877305415ccb1a313 to your computer and use it in GitHub Desktop.
Republican Primary
// this is the javascript file that has all the functions that regarding color
var color = d3.scale.linear() // initial color scale for the demographic data
.range(['rgb(247,252,245)','rgb(229,245,224)','rgb(199,233,192)','rgb(161,217,155)','rgb(116,196,118)','rgb(65,171,93)','rgb(35,139,69)','rgb(0,109,44)','rgb(0,68,27)']);
var candidateMargin = [0.0, 0.05, 0.1, 0.25, 1.0];
var republicanStateColors = ["#e41a1c", "#984ea3", "#4daf4a", "#377eb8", "#D3D3D3"];
var marginColors = ['#fee5d9','#fcae91','#fb6a4a','#de2d26','#a50f15', '#f2f0f7','#cbc9e2','#9e9ac8','#756bb1','#54278f', '#bae4b3', '#bdd7e7', "#D3D3D3"];
var trumpColor = d3.scale.linear()
.domain(candidateMargin)
.range(['#fee5d9','#fcae91','#fb6a4a','#de2d26','#a50f15']);
var cruzColor = d3.scale.linear()
.domain(candidateMargin)
.range(['#f2f0f7','#cbc9e2','#9e9ac8','#756bb1','#54278f'])
var kasichColor = d3.scale.linear()
.domain(candidateMargin)
.range(['#edf8e9','#bae4b3','#74c476','#31a354','#006d2c'])
var rubioColor = d3.scale.linear()
.domain(candidateMargin)
.range(['#eff3ff','#bdd7e7','#6baed6','#3182bd','#08519c'])
var shadeRange = ['#6BA347','#95C077','#BFDEA9','#E4F9D6','#FFF','#E0F0FD','#B3CFE9','#7FAAD3','#4488BD'];
var stateColor = ["#A94588","#D76940","#D13F46","#23A5C5", "#F0A851", "#F0A851", "#A94588", "#23A5C5", "#228947", "#2B6AA1", "#D13F46", "#A94588", "#A94588",
"#2B6AA1", "#F0A851", "#D76940", "#D13F46", "#D13F46", "#6EAE51", "#A94588", "#A94588", "#D76940", "#D13F46", "#F0A851", "#228947", "#D76940", "#23A5C5",
"#23A5C5", "#D13F46", "#6EAE51", "#A94588", "#2B6AA1", "#23A5C5", "#2B6AA1", "#6EAE51", "#2B6AA1", "#2B6AA1", "#D13F46", "#23A5C5", "#6EAE51", "#6EAE51",
"#D76940", "#6EAE51", "#228947", "#F0A851", "#F0A851", "#D13F46", "#726198", "#726198", "#726198"];
function updateHexagonColor(i) { // fills in the hexagons with the correct color according to the scale
hexagons
.transition()
.delay(500)
.style({fill: function(d) {return getDistrictColor(d.properties.districtID,i); },
stroke: function(d) {return getDistrictColor(d.properties.districtID,i); }});
}
function getDistrictColor(districtID,i) {
if (districtID != -1)
return color(dataByDistrictID[districtID][i])
}
function getStateColor(stateID) {
if (stateID != -1)
return stateColor[stateID];
}
function getPrimaryColor(d,i) {
if (i == 1) // if looking at delegates per district
d = d/9.0;
return primaryColor(d);
}
function getVoteStateColor(stateID) {
if (stateID != -1) {
if (isNaN(primaryByStateID[stateID][5]))
return "#D3D3D3";
else {
return republicanStateColors[primaryByStateID[stateID][4]];
}
}
}
function getShadedVoteStateColor(stateID) {
if (stateID != -1) {
if (isNaN(primaryByStateID[stateID][5]))
return '#D3D3D3';
else {
if (primaryByStateID[stateID][4] == 0) {
return trumpColor(primaryByStateID[stateID][5])
}
if (primaryByStateID[stateID][4] == 1) {
return cruzColor(primaryByStateID[stateID][5])
}
if (primaryByStateID[stateID][4] == 2) {
return kasichColor(primaryByStateID[stateID][5])
}
else
return rubioColor(primaryByStateID[stateID][5])
}
}
}
function updateVoteHexagonColor() {
hexagons
.transition()
.delay(500)
.style({fill: function(d) {return getVoteDistrictColor(d.properties.districtID); },
stroke: function(d) {return getVoteDistrictColor(d.properties.districtID); }});
}
function updateBernieHexagonColor() {
hexagons
.transition()
.delay(500)
.style({fill: function(d) {return getBernieDistrictColor(d.properties.bernieBin); },
stroke: function(d) {return getBernieDistrictColor(d.properties.bernieBin); }});
}
var width = 1250;
var height = 730;
var radius = 6.5;
var hexagons;
var demoData;
var districtList = {};
var primaryByStateID = {};
var specificDistrictID = -2;
var toolTipSelector = 0;
var legendRectSize = 15;
var legendSpacing = 7;
var bernieBorder;
var binSelector = -1;
var svg = d3.select(".map").append("svg")
.attr("width", width)
.attr("height", height);
var svgLegend = d3.select(".legendChart").append("svg")
.attr("height", "3200px")
.attr("width", "162px");
queue()
.defer(d3.json, "https://raw.githubusercontent.com/alecrajeev/UnitedStatesHex/Democratic-Primary/ushex.json")
.defer(d3.csv, "primary_state_results.csv")
.await(makeMyMap);
function makeMyMap(error, ushex, primaryData) {
if (error) {
return console.warn(error);
}
primaryData.forEach(function (d) {
d.stateID = +d.stateID;
early = false;
if (d.Margin == "NA")
early = true;
d.WinIndex = +d.WinIndex.substring()
d.Margin = (+d.Margin)*100.0;
primaryByStateID[d.stateID] = [d.TrumpVote, d.CruzVote, d.KasichVote, d.RubioVote, d.WinIndex, d.Margin];
})
var projection = hexProjection(radius);
var path = d3.geo.path().projection(projection);
var hexFeatures = topojson.feature(ushex, ushex.objects.states).features;
hexagons = svg.append("g").attr("class", "hexagon").selectAll("hexagon")
.data(hexFeatures)
.enter()
.append("path")
.attr("d", path)
.style({fill: function(d) {return getVoteStateColor(d.properties.stateID); },
stroke: function(d) {return getVoteStateColor(d.properties.stateID); }})
.on("mouseover", hoverOnDistrict)
var stateBorder = svg.append("path")
.attr("class", "stateBorder")
.call(drawStateBorder);
var districtBorder = svg.append("path")
.attr("class", "districtBorder")
.call(drawDistrctBorder);
var specificDistrict = svg.append("path")
.attr("class", "specificBorder")
.call(drawSpecificDistrict);
showLegend(0);
drawBernieBorder = function (border) {
border.attr("d", path(topojson.mesh(ushex, ushex.objects.states, checkBorderByBernie)));
}
bernieBorder = svg.append("path")
.attr("class", "bernieBorder")
.call(drawBernieBorder);
function hoverOnDistrict(d) {
specificDistrictID = d.properties.districtID;
specificDistrict.call(drawSpecificDistrict);
changeTooltip(d);
}
function drawSpecificDistrict(border) {
border.attr("d", path(topojson.mesh(ushex, ushex.objects.states, checkSpecificDistrict)));
}
function drawDistrctBorder(border) {
border.attr("d", path(topojson.mesh(ushex, ushex.objects.states, checkBorderByDistrict)));
}
function drawStateBorder(border) {
border.attr("d", path(topojson.mesh(ushex, ushex.objects.states, checkBorderByState)));
}
function checkBorderByBernie(hex1, hex2) {
hex1 = hex1.properties.bernieBin;
hex2 = hex2.properties.bernieBin;
hex1 = (hex1 == binSelector ? true : false);
hex2 = (hex2 == binSelector ? true : false);
return hex1 != hex2;
}
function checkSpecificDistrict(hex1, hex2) {
if (specificDistrictID < 0) // if there is no specific district to be highlighted
return false;
if (hex1.properties.districtID != specificDistrictID &&
hex2.properties.districtID != specificDistrictID)
// if when traversing the hexmesh you are not near the specific district
return false;
if (hex1.properties.state == hex2.properties.state)
return hex1.properties.district != hex2.properties.district;
else
return true;
}
function checkBorderByDistrict(hex1, hex2) {
if (hex1.properties.state == hex2.properties.state)
return hex1.properties.district != hex2.properties.district;
else
return true;
}
function checkBorderByState(hex1, hex2) {
return hex1.properties.state != hex2.properties.state;
}
function hexProjection(radius) { // comes from Mike Bostock's hexagon mesh source code
var dx = radius * 2 * Math.sin(Math.PI / 3),
dy = radius * 1.5;
return {
stream: function(stream) {
return {
point: function(x, y) { stream.point(x * dx / 2, (y - (2 - (y & 1)) / 3) * dy / 2); },
lineStart: function() { stream.lineStart(); },
lineEnd: function() { stream.lineEnd(); },
polygonStart: function() { stream.polygonStart(); },
polygonEnd: function() { stream.polygonEnd(); }
};
}
};
}
}
function showStates() {
d3.select(".header").text("States");
hexagons
.style({fill: function(d) {return getStateColor(d.properties.stateID); },
stroke: function(d) {return getStateColor(d.properties.stateID); }});
d3.select(".legend").style("display", "none");
d3.select(".voteLegend").style("display", "none");
toolTipSelector = 0;
}
function showStateVotes() {
d3.select(".header").text("Republican Primary Vote by State");
hexagons
.style({fill: function(d) {return getVoteStateColor(d.properties.stateID); },
stroke: function(d) {return getVoteStateColor(d.properties.stateID); }});
toolTipSelector = 2;
d3.select(".legend").style("display", "");
showLegend(0);
}
function showShadedStateVotes() {
d3.select(".header").text("Republican Primary Vote by State, shaded by margin of Victory");
hexagons
.style({fill: function(d) {return getShadedVoteStateColor(d.properties.stateID); },
stroke: function(d) {return getShadedVoteStateColor(d.properties.stateID); }});
toolTipSelector = 2;
d3.select(".legend").style("display", "");
showLegend(1);
}
function showLegend(j) {
var legendColor;
if (j == 0)
legendColor = republicanStateColors;
else
legendColor = marginColors;
var LegendContent = svgLegend.selectAll(".LegendContent")
.data(legendColor)
var LegendEnter = LegendContent.enter()
.append("g")
.attr("class", "LegendContent")
.attr("transform", function(d,i) {
var rectHeight = i*(legendRectSize + legendSpacing);
var rectWidth = legendRectSize;
return "translate(" + rectWidth + ", " + rectHeight + ")";
})
LegendEnter.append("rect")
.attr("width", legendRectSize-2)
.attr("height", legendRectSize)
.style("fill", function(d,i) {return legendColor[i]})
.style("stroke", "black")
LegendEnter.append("text")
.attr("x", legendRectSize + legendSpacing*1.3)
.attr("y", legendRectSize-1)
.text(function(d,i) {
if (j == 0) {
if (i == 0) {
return "Trump";
}
if (i == 1) {
return "Cruz";
}
if (i == 2) {
return "Kasich";
}
if (i == 3) {
return "Rubio";
}
else
return "No Contest";
}
else {
if (i == 0)
return "1% Trump";
if (i == 2)
return "5% Trump";
if (i == 3)
return "10% Trump";
if (i == 4)
return "25% Trump";
if (i == 5)
return ">26% Trump";
}
});
var updateSelection = svgLegend.selectAll(".LegendContent")
.transition()
.duration(1000)
.style("opacity", "1")
.attr("transform", function(d,i) {
var rectHeight = i*(legendRectSize + legendSpacing);
var rectWidth = legendRectSize;
return "translate(" + rectWidth + ", " + rectHeight + ")";
})
updateSelection.select("rect")
.style("fill", function(d,i) {return legendColor[i]; });
updateSelection.select("text")
.text(function(d,i) {
if (j == 0) {
if (i == 0) {
return "Trump";
}
if (i == 1) {
return "Cruz";
}
if (i == 2) {
return "Kasich";
}
if (i == 3) {
return "Little Marco";
}
else
return "No Contest";
}
else {
if (i == 0)
return "1% Trump";
if (i == 1)
return "5% Trump";
if (i == 2)
return "10% Trump";
if (i == 3)
return "25% Trump";
if (i == 4)
return ">25% Trump";
if (i == 5)
return "1% Cruz";
if (i == 6)
return "5% Cruz";
if (i == 7)
return "10% Cruz";
if (i == 8)
return "24% Cruz";
if (i == 9)
return ">26% Cruz";
if (i == 10)
return "10% Kasich";
if (i == 11)
return ">5% Little Marco"
return "No Contest";
}
});
LegendContent.exit()
.remove();
}
function changeTooltip(d) {
if (d.properties.state != "Ocean") { // if you ARE on a district
d3.select(".whichState").text(d.properties.state);
d3.select(".toolTipT").text("Trump Vote " + primaryByStateID[d.properties.stateID][0])
d3.select(".toolTipC").text("Cruz Vote " + primaryByStateID[d.properties.stateID][1])
d3.select(".toolTipK").text("Kasich Vote " + primaryByStateID[d.properties.stateID][2])
d3.select(".toolTipR").text("Rubio Vote " + primaryByStateID[d.properties.stateID][3])
}
else { // if you are NOT on a district
d3.select(".whichState").text("");
d3.select(".toolTipT").text("Trump Vote ")
d3.select(".toolTipC").text("Cruz Vote ")
d3.select(".toolTipK").text("Kasich Vote ")
d3.select(".toolTipR").text("Rubio Vote ")
}
}
function grabDistrictInfo(districtID, i) {
if (primaryByDistrictID[districtID][9])
return "";
return primaryByDistrictID[districtID][i];
}
function grabStateInfo(stateID, districtID, i) {
if (primaryByDistrictID[districtID][9])
return "";
if (i >= 4)
return d3.round(primaryByStateID[stateID][i]*100.0, 2) + "%";
if (i < 4)
return primaryByStateID[stateID][i];
}
function getRealDistrict(i, state) { // returns "at large" if the district number is 0, like Montana
if (i > 0)
return i;
return "At-Large";
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="icon" href="favicon.ico" />
<link rel="stylesheet", type="text/css", href="style.css">
<title>Best Map of the Republican Primary</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js">
</script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/queue-async/1.0.7/queue.min.js">
</script>
</head>
<body>
<div class="top">
<h3 class="header">Republican Primary Vote by State</h3>
</div>
<div class="left">
<div class="sidebar">
<text>Data Set</text>
<div class="typeToggle">
<input type="radio" name="data-selector" onclick="showStates()">States
</div>
<div class="typeToggle">
<input type="radio" name="data-selector" checked onclick="showStateVotes()">Primary Vote by State</input>
</div>
<div class="typeToggle">
<input type="radio" name="data-selector" onclick="showShadedStateVotes()">Scaled Primary Vote</input>
</div>
</div>
<div class="information">
<div>State: <span class="whichState"></span></div>
<div class="toolTipT">Trump Vote </div>
<div class="toolTipC">Cruz Vote </div>
<div class="toolTipK">Kasich Vote </div>
<div class="toolTipR">Rubio Vote </div>
</div>
<div class="legend">
<div class="legTitle">Margin of Victory</div>
<div class="legendChart"></div>
</div>
</div>
<div class="main">
<div class="map"></div>
<div class="sampleImage"> <img src="http://i.imgur.com/NKyAVOf.png" /></div>
</div>
</body>
<script type="text/javascript" src="hexscript.js"></script>
<script type="text/javascript" src="colorBuilder.js"></script>
</html>
State stateID TrumpVote CruzVote KasichVote RubioVote WinIndex Margin
AL 0 43.42% 21.09% 4.43% 18.66% 0 0.002233
AK 1 33.64% 36.37% 3.99% 15.16% 1 0.000273
AZ 2 45.95% 27.61% 10.57% 11.59% 0 0.001834
AR 3 32.75% 30.47% 3.71% 24.90% 0 0.000228
CA 4 NA NA NA NA NA NA
CO 5 NA NA NA NA NA NA
CT 6 NA NA NA NA NA NA
DE 7 NA NA NA NA NA NA
FL 8 45.72% 17.14% 6.77% 27.04% 0 0.002858
GA 9 38.81% 23.60% 5.59% 24.45% 0 0.001436
HI 10 42.40% 32.74% 10.57% 13.16% 0 0.000966
ID 11 28.11% 45.42% 7.43% 15.91% 1 0.001731
IL 12 38.84% 30.27% 19.73% 8.69% 0 0.000857
IN 13 NA NA NA NA NA NA
IA 14 24.30% 27.64% 3.33% 23.12% 1 0.000334
KS 15 23.35% 47.50% 11.07% 16.83% 1 0.002415
KY 16 35.92% 31.57% 14.42% 16.36% 0 0.000435
LA 17 41.45% 37.83% 1.51% 11.22% 0 0.000362
ME 18 32.59% 45.90% 12.19% 8.01% 1 0.001331
MD 19 NA NA NA NA NA NA
MA 20 49.30% 9.58% 18.02% 17.87% 0 0.003972
MI 21 36.50% 24.86% 24.23% 9.32% 0 0.001164
MN 22 21.30% 28.99% 5.75% 36.48% 3 0.000749
MS 23 47.32% 36.29% 8.84% 5.13% 0 0.001103
MO 24 40.92% 40.73% 9.91% 6.11% 0 0.000019
MT 25 NA NA NA NA NA NA
NE 26 NA NA NA NA NA NA
NV 27 45.75% 23.77% 3.59% 23.77% 0 0.002198
NH 28 35.24% 11.63% 15.72% 10.52% 0 0.002361
NJ 29 NA NA NA NA NA NA
NM 30 NA NA NA NA NA NA
NY 31 NA NA NA NA NA NA
NC 32 40.23% 36.76% 12.67% 7.73% 0 0.000347
ND 33 NA NA NA NA NA NA
OH 34 35.61% 13.10% 46.83% 2.91% 2 0.001122
OK 35 28.32% 34.37% 3.59% 26.01% 1 0.000605
OR 36 NA NA NA NA NA NA
PA 37 NA NA NA NA NA NA
RI 38 NA NA NA NA NA NA
SC 39 32.51% 22.48% 7.61% 22.48% 0 0.001003
SD 40 NA NA NA NA NA NA
TN 41 38.94% 24.71% 5.29% 21.18% 0 0.001423
TX 42 26.75% 43.76% 4.25% 17.73% 1 0.001701
UT 43 14.03% 69.17% 16.81% 0 1 0.005514
VT 44 32.72% 9.71% 30.39% 19.31% 0 0.002301
VA 45 34.73% 16.90% 9.42% 31.91% 0 0.001783
WA 46 NA NA NA NA NA NA
WV 47 NA NA NA NA NA NA
WI 48 14.29% 85.71% 14.10% 0 1 0.007142
WY 49 3.85% 34.62% 0 3.85% 1 0.003077
.top {
position: absolute;
left:0; right:0;
height: 92px;
margin-left: 15px;
font-family: 'Helvetica Neue';
font-weight: bold;
}
.left {
position: absolute;
left:0; top:80px; bottom: 0;
width: 178px;
margin-left: 15px;
}
.main {
position: relative;
left:180px; top:92px; right:0; bottom:0;
}
.right {
position: relative;
background: #fff;
top: -656px;
left: 1200px;
width: 178px;
}
.map {
position: relative;
top: -60px;
}
.legend {
position: relative;
top: -6px;
height: 380px;
border: solid black;
padding: 5px;
padding-top: 10px;
display: block;
font-family: "Helvetica Neue";
stroke-width: 2px;
/*display: none;*/
}
.legTitle {
padding-left: 15px;
}
.legendChart {
position: relative;
padding-top: 15px;
font-size: 14px;
}
.bernieLegend {
position: relative;
top: -6px;
height: 220px;
border: solid black;
padding: 5px;
padding-top: 10px;
display: block;
font-family: "Helvetica Neue";
stroke-width: 2px;
display: none;
}
.voteSelector {
position: relative;
height: 302px;
border: solid black;
padding: 5px;
display: block;
font-family: "Helvetica Neue";
stroke-width: 2px;
background: #fff;
overflow: scroll;
display: block;
}
.voteSelector text {
position: relative;
text-align: center;
display: block;
border-bottom: thin solid black;
padding: 10px 30px 10px 30px;
font-weight: bold;
font-family: 'Helvetica Neue';
}
.buttonDiv {
padding: 5px 5px 5px 5px;
font-family: "Helvetica Neue";
}
.voteLegend {
position: relative;
top: -3px;
height: 120px;
border: solid black;
padding: 5px;
padding-top: 10px;
display: block;
font-family: "Helvetica Neue";
stroke-width: 2px;
display: none;
}
.nytimesLogo {
position: relative;
top: -3px;
}
.passage {
width: 130px;
height: 40px;
}
.sidebar {
border: solid black;
padding: 5px;
font-family: 'Helvetica Neue';
}
.sidebar text{
position: relative;
text-align: center;
display: block;
border-bottom: thin solid black;
padding: 10px 30px 10px 30px;
font-weight: bold;
font-family: 'Helvetica Neue';
}
.whichState {
font-weight: bold;
}
.whichDistrict {
font-weight: bold;
}
.typeToggle {
font-family: 'Helvetica Neue';
padding: 3px;
font-size: 14px;
font-style: normal;
font-variant: normal;
font-weight: 400;
line-height: 20px;
}
.information {
position: relative;
top: -3px;
border: solid black;
padding: 5px;
font-family: 'Helvetica Neue';
display: block;
font-size: 15px;
}
.toggleSideBar {
position: relative;
top: -3px;
border: solid black;
padding: 5px;
font-family: 'Helvetica Neue';
display: block;
}
.hexagon {
fill: none;
pointer-events: all;
stroke: 0px;
z-index: 1;
}
.specificBorder {
fill: none;
stroke-width: 2.5px;
stroke-opacity: 1;
stroke: #fff;
pointer-events: none;
}
.districtBorder {
fill: none;
stroke: #000;
stroke-width: 1px;
stroke-opacity: .2;
pointer-events: none;
}
.stateBorder {
fill: none;
stroke: #000;
stroke-width: 2px;
pointer-events: none;
}
.bernieBorder {
fill: none;
stroke: #7fff00;
stroke-width: 2.5px;
stroke-opacity: 0;
pointer-events: none;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment