|
<html> |
|
<head> |
|
<script src="https://d3js.org/d3.v2.js"></script> |
|
<link href='https://fonts.googleapis.com/css?family=Quicksand: 400, 700' rel='stylesheet' type='text/css'> |
|
<meta property="og:title" content="CLOSE VOTES - TULP interactive"> |
|
<meta property="og:image" content="http://tulpinteractive.com/projects/close-votes/close_votes_large.png" /> |
|
<meta property="og:description" content="Based on the 2012 parliament elections in The Netherlands, this visualization shows which cities distribute their votes similar over the political parties" /> |
|
<title>CLOSE VOTES - TULP interactive</title> |
|
<style> |
|
/*body { background-color: #DFCCA5; }*/ |
|
body { |
|
font-family: 'Quicksand', sans-serif; |
|
font-size: 11px; |
|
text-align: center; |
|
background: rgb(240,249,255); /* Old browsers */ |
|
background: -moz-radial-gradient(center, ellipse cover, rgba(240,249,255,1) 0%, rgba(203,235,255,1) 47%, rgba(174, 218, 288,1) 100%); /* FF3.6+ */ |
|
background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%,rgba(240,249,255,1)), color-stop(47%,rgba(203,235,255,1)), color-stop(100%,rgba(174, 218, 288,1))); /* Chrome,Safari4+ */ |
|
background: -webkit-radial-gradient(center, ellipse cover, rgba(240,249,255,1) 0%,rgba(203,235,255,1) 47%,rgba(174, 218, 288,1) 100%); /* Chrome10+,Safari5.1+ */ |
|
background: -o-radial-gradient(center, ellipse cover, rgba(240,249,255,1) 0%,rgba(203,235,255,1) 47%,rgba(174, 218, 288,1) 100%); /* Opera 12+ */ |
|
background: -ms-radial-gradient(center, ellipse cover, rgba(240,249,255,1) 0%,rgba(203,235,255,1) 47%,rgba(174, 218, 288,1) 100%); /* IE10+ */ |
|
background: radial-gradient(ellipse at center, rgba(240,249,255,1) 0%,rgba(203,235,255,1) 47%,rgba(174, 218, 288,1) 100%); /* W3C */ |
|
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f0f9ff', endColorstr='#a1dbff',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */ |
|
} |
|
ul { list-style: none; padding: 0; margin: 0; display: inline-block; position: absolute; z-index: 100; right: 0px; top: 30px; } |
|
select { position: absolute; right: 0px; top: 924px; } |
|
a:hover { color: #F9786C; text-decoration: underline; } |
|
h1 { font-size: 32px; margin-bottom: 0;} |
|
h2 { font-size: 18px; margin-top: 0; margin-bottom: 0; } |
|
h4 { margin: 0 auto; font-weight: normal; width: 500px; } |
|
li { text-align: left; } |
|
li span { width: 50px; display: inline-block; text-align: right; margin-right: 5px; } |
|
.selected { color: #000; font-weight: bold; text-decoration: none; cursor: default; } |
|
.selected:hover { text-decoration: none; color: #000; } |
|
.notselected { font-weight: normal; text-decoration: underline; cursor: pointer; color: #91B6D4; } |
|
.notselected:hover { color: #F9786C; } |
|
#viz { width: 100%; } |
|
#selectorcontainer { width: 1000px; text-align: right; margin: auto auto; position: relative; } |
|
#credit { position: absolute; top: 924px; left: 0; font-size: 9px; } |
|
#credit a, #credit a:link, #credit a:active { font-weight: normal; text-decoration: none; cursor: pointer; color: #000; } |
|
#credit a:hover { color: #F9786C; } |
|
</style> |
|
</head> |
|
<body> |
|
<h1>CLOSE VOTES</h1> |
|
<h2>Which cities vote like yours?</h2> |
|
<h4>Based on the 2012 parliament elections in The Netherlands, this visualization shows which cities distribute their votes similar over the political parties</h4> |
|
<div id="selectorcontainer"> |
|
<ul> |
|
<li><span>size:</span><a href="Javascript: void(0);" id="size_by_pop" class="notselected">population</a> / <a href="Javascript: void(0);" id="size_by_similarity" class="selected">similarity</a></li> |
|
<li><span>layout:</span><a href="Javascript: void(0);" id="layout_radial" class="notselected">radial</a> / <a href="Javascript: void(0);" id="layout_geo" class="selected">geographical</a></li> |
|
</ul> |
|
<span id="credit">created by <a href="http://tulpinteractive.com">TULP interactive</a></span> |
|
</div> |
|
<div id="viz"></div> |
|
<script type="text/javascript"> |
|
|
|
|
|
d3.json("results.json", function(json) { |
|
var data = json; |
|
|
|
var index = Math.round(Math.random() * data.length), |
|
w = 1000, |
|
h = 1000; |
|
|
|
// create the drop down menu of cities |
|
var selector = d3.select("#selectorcontainer") |
|
.append("select") |
|
.attr("id", "cityselector") |
|
.selectAll("option") |
|
.data(data) |
|
.enter().append("option") |
|
.text(function(d) { return d.city; }) |
|
.attr("value", function (d, i) { |
|
return i; |
|
}); |
|
|
|
d3.select("#cityselector").property("selectedIndex", index); |
|
//27861 |
|
var rl = d3.scale.linear().domain([0, 75]).range([0, w / 2 - 60]); |
|
var rs = d3.scale.log().domain([1, 74]).range([0, w / 2 - 60]); |
|
var rr = d3.scale.linear().domain([Math.sqrt(1 / Math.PI), Math.sqrt(15 / Math.PI), Math.sqrt(74 / Math.PI)]).range([22, 5, 2]); |
|
//var rr = d3.scale.linear().domain([Math.sqrt(1 / Math.PI), Math.sqrt(74 / Math.PI)]).range([22, 2]) |
|
|
|
var rpopulation = d3.scale.linear().domain(d3.extent(data, function(d) { return Math.sqrt(d.population / Math.PI); })).range([2, 40]) |
|
var ro = d3.scale.linear().domain(d3.extent(data, function(d) { return d.opk; })).range([2, 70]) |
|
var c = d3.scale.log().domain([1, 20, 74]).range(["#F9786C", "#41415F", "#193244"]); |
|
|
|
var axes = [ |
|
{ 'label': 'very similar', 'value': 15 }, |
|
{ 'label': 'similar', 'value': 30 }, |
|
{ 'label': 'different', 'value': 45 }, |
|
{ 'label': 'very different', 'value': 60 }, |
|
{ 'label': 'extremely different', 'value': 75} |
|
]; |
|
|
|
var rfuncs = [ |
|
function(d, i) { return rpopulation(Math.sqrt(d.population / Math.PI)); }, // population |
|
function(d, i) { return rr(Math.sqrt(d['chi'][index] / Math.PI) == 0 ? 1 : Math.sqrt(d['chi'][index] / Math.PI)); }, // similarity |
|
function(d, i) { return ro(d.opk); } // show up |
|
] |
|
|
|
var rfunc = rfuncs[1]; |
|
var layout = "geo"; |
|
var size = "sim"; |
|
|
|
var svg = d3.select("#viz").append("svg") |
|
.attr("width", w) |
|
.attr("height", h) |
|
|
|
var merc = d3.geo.mercator(); |
|
merc.translate([-986, 11195]); |
|
merc.scale(65668); |
|
|
|
var selectedCity = svg.append("text") |
|
.attr("id", "selectedcity") |
|
.attr("x", 54) |
|
.attr("y", 30) |
|
.text(data[index]["city"]) |
|
.style("fill", "#000") |
|
.style("text-anchor", "start") |
|
.style("font-family", "Quicksand") |
|
.style("font-size", "24px") |
|
|
|
var selectors = svg.append("foreignObject") |
|
.attr("transform", "translate(200, 200)") |
|
.append("body") |
|
.append("ul") |
|
|
|
selectors.append("li") |
|
selectors.append("li") |
|
|
|
var g = svg.append('g') |
|
.attr('transform', 'translate(' + w / 2 + ', ' + h / 2 + ')') |
|
|
|
var arc = d3.svg.arc() |
|
.outerRadius(function(d) { return rl(d.value); }) |
|
.startAngle(0) |
|
.endAngle(2 * Math.PI) |
|
|
|
var ga = g.append("g") |
|
.attr("id", "axisgroup") |
|
.style("opacity", 0) |
|
|
|
ga.selectAll(".axispath") |
|
.data(axes) |
|
.enter().append("path") |
|
.attr("id", function(d, i) { return "axispath" + i; }) |
|
.attr("class", "axispath") |
|
.attr("d", arc) |
|
.style("stroke", "#91B6D4") |
|
.style("fill", "none") |
|
.style("opacity", 0.6) |
|
|
|
ga.selectAll(".axislabel") |
|
.data(axes) |
|
.enter().append("text") |
|
.attr("class", "axislabel") |
|
.attr("dy", -5) |
|
.attr("dx", 0) |
|
.style("fill", "#91B6D4") |
|
.style("font-size", "11px") |
|
.style("text-anchor", "middle") |
|
.append("textPath") |
|
.attr("xlink:href", function(d, i) { return "#axispath" + i; }) |
|
.attr("startOffset", "40%") |
|
.text(function(d, i) { return d.label; }) |
|
|
|
d3.selection.prototype.moveToFront = function() { |
|
return this.each(function() { |
|
this.parentNode.appendChild(this); |
|
}); |
|
}; |
|
|
|
data.forEach(function(d, i) { |
|
d.s = i; |
|
}) |
|
|
|
d3.select("#cityselector") |
|
.on("change", function(d) { |
|
index = this.value; |
|
update(); |
|
}) |
|
|
|
d3.select("#size_by_pop") |
|
.on("click", function() { |
|
rfunc = rfuncs[0]; |
|
circle.each(function(d, i) { |
|
d3.select(this) |
|
.transition() |
|
.duration(800) |
|
.attr('r', rfunc) |
|
}) |
|
|
|
d3.select(this) |
|
.attr("class", "selected") |
|
|
|
d3.select("#size_by_similarity") |
|
.attr("class", "notselected") |
|
|
|
size = "pop"; |
|
}) |
|
|
|
d3.select("#size_by_similarity") |
|
.on("click", function() { |
|
rfunc = rfuncs[1]; |
|
circle.each(function(d, i) { |
|
d3.select(this) |
|
.transition() |
|
.duration(800) |
|
.attr('r', rfunc) |
|
}) |
|
|
|
d3.select(this) |
|
.attr("class", "selected") |
|
|
|
d3.select("#size_by_pop") |
|
.attr("class", "notselected") |
|
|
|
size = "sim"; |
|
}) |
|
|
|
d3.select("#layout_radial") |
|
.on("click", function() { |
|
layout = "radial"; |
|
|
|
d3.selectAll(".city") |
|
.transition() |
|
.duration(1300) |
|
.attr("cy", 0) |
|
.attr("cx", function(d, i) { return rs(d['chi'][index] == 0 ? 1 : d['chi'][index])}) |
|
.attr("transform", function(d, i) { return "rotate(" + d.s/data.length * 360 + " 0 0)"; }) |
|
|
|
d3.selectAll("#axisgroup") |
|
.transition() |
|
.duration(1300) |
|
.style("opacity", 1) |
|
|
|
d3.select(this) |
|
.attr("class", "selected") |
|
|
|
d3.select("#layout_geo") |
|
.attr("class", "notselected") |
|
}) |
|
|
|
d3.select("#layout_geo") |
|
.on("click", function() { |
|
layout = "geo"; |
|
|
|
d3.selectAll(".city") |
|
.transition() |
|
.duration(1300) |
|
.attr('cy', function(d) { return merc([d['geo'][1], d['geo'][0]])[1]; }) |
|
.attr('cx', function(d) { return merc([d['geo'][1], d['geo'][0]])[0]; }) |
|
.attr("transform", function(d, i) { return "rotate(0 0 0)"; }) |
|
|
|
d3.selectAll("#axisgroup") |
|
.transition() |
|
.duration(1300) |
|
.style("opacity", 0) |
|
|
|
d3.select(this) |
|
.attr("class", "selected") |
|
|
|
d3.select("#layout_radial") |
|
.attr("class", "notselected") |
|
}) |
|
|
|
function update() { |
|
if (layout == "radial") { |
|
d3.selectAll('.city') |
|
.transition() |
|
.duration(800) |
|
.attr("cx", function(d, i) { return rs(d['chi'][index] == 0 ? 1 : d['chi'][index])}) |
|
.attr('r', rfunc) |
|
.style('fill', function(d, i) { return index == d.s ? "#F9786C" : c(d['chi'][index] == 0 ? 1 : d['chi'][index]); }) |
|
} else { |
|
d3.selectAll('.city') |
|
.transition() |
|
.duration(800) |
|
.attr('r', rfunc) |
|
.style('fill', function(d, i) { return index == d.s ? "#F9786C" : c(d['chi'][index] == 0 ? 1 : d['chi'][index]); }) |
|
} |
|
|
|
d3.selectAll(".label") |
|
.remove(); |
|
|
|
d3.select("#selectedcity") |
|
.text(data[index]["city"]) |
|
|
|
bar |
|
.data(data[index]['percentVote']) |
|
.transition() |
|
.duration(800) |
|
.attr("width", function(d) { return bs(d); }) |
|
} |
|
|
|
var circle = g.selectAll('.city') |
|
.data(data) |
|
.enter().append('circle') |
|
.attr('class', 'city') |
|
.attr('r', rfunc) |
|
.attr('cy', function(d) { return merc([d['geo'][1], d['geo'][0]])[1]; }) |
|
.attr('cx', function(d) { return merc([d['geo'][1], d['geo'][0]])[0]; }) |
|
.style('fill', function(d, i) { return index == d.s ? "#F9786C" : c(d['chi'][index] == 0 ? 1 : d['chi'][index]); }) |
|
.style('stroke', 'white') |
|
.style('stroke-opacity', 0.3) |
|
.style("opacity", 0.9) |
|
.on("click", function(d, ind) { |
|
d3.selectAll('.city').each(function(d, ind) { |
|
if (d.s == index) { |
|
d3.select(this) |
|
.moveToFront() |
|
} |
|
}) |
|
|
|
index = ind; |
|
|
|
d3.select("#cityselector").property("selectedIndex", index); |
|
|
|
update(); |
|
|
|
}) |
|
.on("mouseover", function(d, i) { |
|
|
|
var labelbackground = d3.select(this.parentNode) |
|
.append('text') |
|
.attr('class', 'label') |
|
.style('text-anchor', 'middle') |
|
.text(function() { return d.city; }) |
|
.style('font-family', "'Quicksand', sans-serif") |
|
.style('font-size', '16px') |
|
.style('font-weight', 'bold') |
|
.style('stroke', 'rgb(240,249,255)') |
|
.style('stroke-width', 3.5) |
|
.style('stroke-opacity', 0.6) |
|
.style('filter', 'url:(#dropshadow)') |
|
.attr('dy', function() { return size == "pop" ? -1 * rpopulation(Math.sqrt(d.population / Math.PI)) - 5 : -1 * rr(Math.sqrt(d['chi'][index] / Math.PI) == 0 ? 1 : Math.sqrt(d['chi'][index] / Math.PI)) - 5; }) |
|
.style('fill', 'none') |
|
|
|
var labelforeground = d3.select(this.parentNode) |
|
.append('text') |
|
.attr('class', 'label') |
|
.style('text-anchor', 'middle') |
|
.text(function() { return d.city; }) |
|
.style('font-family', "'Quicksand', sans-serif") |
|
.style('font-size', '16px') |
|
.style('font-weight', 'bold') |
|
.attr('dy', function() { return size == "pop" ? -1 * rpopulation(Math.sqrt(d.population / Math.PI)) - 5 : -1 * rr(Math.sqrt(d['chi'][index] / Math.PI) == 0 ? 1 : Math.sqrt(d['chi'][index] / Math.PI)) - 5; }) |
|
.style('fill', '#000') |
|
|
|
if (layout == "geo") { |
|
labelbackground |
|
.attr('y', function() { return merc([d['geo'][1], d['geo'][0]])[1]; }) |
|
.attr('x', function() { return merc([d['geo'][1], d['geo'][0]])[0]; }) |
|
|
|
labelforeground |
|
.attr('y', function() { return merc([d['geo'][1], d['geo'][0]])[1]; }) |
|
.attr('x', function() { return merc([d['geo'][1], d['geo'][0]])[0]; }) |
|
} else { |
|
labelbackground |
|
.attr('x', function() { return index == i ? 0 : Math.cos(d.s/data.length * 2 * Math.PI) * rs(d['chi'][index] == 0 ? 1 : d['chi'][index]); }) |
|
.attr('y', function() { return index == i ? 0 : Math.sin(d.s/data.length * 2 * Math.PI) * rs(d['chi'][index] == 0 ? 1 : d['chi'][index]); }) |
|
|
|
labelforeground |
|
.attr('x', function() { return index == i ? 0 : Math.cos(d.s/data.length * 2 * Math.PI) * rs(d['chi'][index] == 0 ? 1 : d['chi'][index]); }) |
|
.attr('y', function() { return index == i ? 0 : Math.sin(d.s/data.length * 2 * Math.PI) * rs(d['chi'][index] == 0 ? 1 : d['chi'][index]); }) |
|
} |
|
|
|
bg.selectAll(".marker") |
|
.data(data[d.s]["percentVote"]) |
|
.enter().append("line") |
|
.attr("class", "marker") |
|
.attr("x1", function(d) { return 5 + bs(d); }) |
|
.attr("y1", function(d, i) { return i * 11; }) |
|
.attr("x2", function(d) { return 5 + bs(d); }) |
|
.attr("y2", function(d, i) { return 10 + i * 11; }) |
|
.style("stroke", "#000") |
|
.style("opacity", 0.8) |
|
}) |
|
.on("mouseout", function(d, i) { |
|
d3.selectAll('.label') |
|
.remove() |
|
|
|
d3.selectAll(".marker") |
|
.remove() |
|
}); |
|
|
|
var bs = d3.scale.linear().domain([0, 100]).range([0, 200]); |
|
|
|
var bg = svg.append("g") |
|
.attr("transform", "translate(50, 50)") |
|
|
|
var party = bg.selectAll(".party") |
|
.data(["VVD", "PvdA", "PVV", "SP", "CDA", "D66", "CU", "GrLinks", "SGP", "PvdD", "50+"]) |
|
.enter().append("text") |
|
.attr("class", "party") |
|
.attr("x", 0) |
|
.attr("y", function(d, i) { return 8 + i * 11; }) |
|
.text(String) |
|
.style("text-anchor", "end") |
|
.style("fill", "#91B6D4") |
|
|
|
var bar = bg.selectAll("rect") |
|
.data(data[index]["percentVote"]) |
|
.enter().append("rect") |
|
.attr("x", 5) |
|
.attr("y", function(d, i) { return i * 11; }) |
|
.attr("height", 10) |
|
.attr("width", function(d) { return bs(d); }) |
|
.style("fill", "#91B6D4") |
|
|
|
|
|
|
|
}); |
|
|
|
</script> |
|
<script> |
|
var _gaq=[['_setAccount','UA-27449759-1'],['_trackPageview']]; |
|
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0]; |
|
g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js'; |
|
s.parentNode.insertBefore(g,s)}(document,'script')); |
|
</script> |
|
</body> |
|
</html> |