Skip to content

Instantly share code, notes, and snippets.

@tophtucker
Last active June 24, 2016 03:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tophtucker/d161169c785ac1eeec01c92f89bd0ecd to your computer and use it in GitHub Desktop.
Save tophtucker/d161169c785ac1eeec01c92f89bd0ecd to your computer and use it in GitHub Desktop.
The Brexit Game
height: 1160
<!DOCTYPE html>
<meta charset="utf-8">
<!-- <meta name="viewport" content="width=device-width, initial-scale=1"> -->
<meta name="viewport" content="width=960">
<style>
html, body {
margin: 0;
padding: 0;
}
* {
box-sizing: border-box;
}
.subunit.SCT { fill: rgba(255,0,0,.05); stroke: #aaa; }
.subunit.WLS { fill: rgba(0,255,0,.05); stroke: #aaa; }
.subunit.NIR { fill: rgba(255,0,255,.05); stroke: #aaa; }
.subunit.ENG { fill: rgba(0,255,255,.05); stroke: #aaa; }
.subunit.IRL,
.subunit-label.IRL {
display: none;
}
.subunit-boundary {
fill: none;
stroke: #777;
stroke-dasharray: 2,2;
stroke-linejoin: round;
}
.subunit-boundary.IRL {
stroke: #aaa;
}
.subunit-label {
fill: #777;
fill-opacity: .5;
font-size: 20px;
font-weight: 300;
text-anchor: middle;
}
.place,
.place-label {
display: none;
fill: #444;
}
text {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 10px;
pointer-events: none;
}
h1 {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
text-align: center;
position: fixed;
width: 100%;
padding: 0 .25em;
font-size: 50px;
pointer-events: none;
}
h1.prompt {
top: .25em;
}
h1.results {
bottom: .25em;
}
circle {
stroke-width: 2;
stroke: black;
}
circle.guess {
fill: rgba(0,0,0,.6);
}
circle.answer {
fill: rgba(0,255,0,.6);
}
button {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%,-50%) rotate(20deg);
border-radius: 50%;
padding: 1em;
background: rgba(255,255,255,.5);
border: 2px solid black;
font-size: 70px;
cursor: pointer;
}
div.finish {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255,255,255,.95);
}
div.finish h1 {
position: absolute;
top: 50%;
transform: translate(0,-50%);
margin: 0;
}
</style>
<body>
<h1 class="prompt">
<span class="name"></span> voted to
<span class="result"></span>,
<span class="percentage"></span>%. Where do you think it is?
</h1>
<h1 class="results"></h1>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script src="results.js"></script>
<script>
// COORDINATES are LONG, LAT
var guessDistances = [];
var width = 960,
height = 1160;
var projection = d3.geo.albers()
.center([0, 55.4])
.rotate([4.4, 0])
.parallels([50, 60])
.scale(1200 * 5)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection)
.pointRadius(2);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("uk.json", function(error, uk) {
var subunits = topojson.feature(uk, uk.objects.subunits),
places = topojson.feature(uk, uk.objects.places);
svg.selectAll(".subunit")
.data(subunits.features)
.enter().append("path")
.attr("class", function(d) { return "subunit " + d.id; })
.attr("d", path);
svg.append("path")
.datum(topojson.mesh(uk, uk.objects.subunits, function(a, b) { return a !== b && a.id !== "IRL"; }))
.attr("d", path)
.attr("class", "subunit-boundary");
svg.append("path")
.datum(topojson.mesh(uk, uk.objects.subunits, function(a, b) { return a === b && a.id === "IRL"; }))
.attr("d", path)
.attr("class", "subunit-boundary IRL");
svg.selectAll(".subunit-label")
.data(subunits.features)
.enter().append("text")
.attr("class", function(d) { return "subunit-label " + d.id; })
.attr("transform", function(d) { return "translate(" + path.centroid(d) + ")"; })
.attr("dy", ".35em")
.text(function(d) { return d.properties.name; });
svg.append("path")
.datum(places)
.attr("d", path)
.attr("class", "place");
svg.selectAll(".place-label")
.data(places.features)
.enter().append("text")
.attr("class", "place-label")
.attr("transform", function(d) { return "translate(" + projection(d.geometry.coordinates) + ")"; })
.attr("x", function(d) { return d.geometry.coordinates[0] > -1 ? 6 : -6; })
.attr("dy", ".35em")
.style("text-anchor", function(d) { return d.geometry.coordinates[0] > -1 ? "start" : "end"; })
.text(function(d) { return d.properties.name; });
promptPlace(results.pop());
function promptPlace(place) {
if(place===undefined) {
finish();
return;
}
// reset
d3.selectAll('circle').remove();
d3.select('button').remove();
d3.select('.results').text('');
// fill in prompt
d3.select('.name').text(place.name);
d3.select('.result').text(place.vote > 50 ? 'remain' : 'leave');
d3.select('.percentage').text(place.vote > 50 ? place.vote : 100-place.vote);
d3.select('svg').on('click', function() {
var performance = d3.scale.threshold()
.domain([20,80,150,300])
.range(['Amazing!!!', 'Very good!', 'OK!', 'Mediocre.', 'You have no clue.'])
var guess = projection.invert(d3.mouse(this));
var dist = distance(place.coordinates, guess);
guessDistances.push(dist);
d3.select('.results').text('You were ' + Math.round(dist) + ' “kilometres” away. ' + performance(dist));
svg.append('circle')
.classed('guess', true)
.attr('cx', d3.mouse(this)[0])
.attr('cy', d3.mouse(this)[1])
.attr('r', 1e-6)
.transition()
.delay(0)
.duration(250)
.attr('r', 30);
svg.append('circle')
.classed('answer', true)
.attr('cx', projection(place.coordinates)[0])
.attr('cy', projection(place.coordinates)[1])
.attr('r', 1e-6)
.transition()
.delay(250)
.duration(250)
.attr('r', 30);
// next
d3.select('svg').on('click', null);
setTimeout(function() {
d3.select('body').append('button').text('Next!')
.on('click', function() {
promptPlace(results.pop());
})
}, 1000);
})
}
function finish() {
var performance = d3.scale.threshold()
.domain([20,80,150,300])
.range([
'You are practically Executive Director Dr. John Ludden of the British Geological Survey!',
'You must be British!',
'You are a decent human being!',
'You are not good at this but we support your existence anyway!',
'You are very very bad at this unimportant game.'
]);
d3.select('body').append('div')
.classed('finish', true)
.append('h1')
.text('THE END! On average you were off by ' + Math.round(d3.mean(guessDistances)) + ' km, with a st.dev. of ' + Math.round(d3.deviation(guessDistances)) + ' km. ' + performance(d3.mean(guessDistances)));
}
});
// from https://www.geodatasource.com/developers/javascript
function distance(from, to, unit) {
if(unit===undefined) unit = 'K';
var lat1 = from[1];
var lon1 = from[0];
var lat2 = to[1];
var lon2 = to[0];
var radlat1 = Math.PI * lat1/180
var radlat2 = Math.PI * lat2/180
var theta = lon1-lon2
var radtheta = Math.PI * theta/180
var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
dist = Math.acos(dist)
dist = dist * 180/Math.PI
dist = dist * 60 * 1.1515
if (unit=="K") { dist = dist * 1.609344 }
if (unit=="N") { dist = dist * 0.8684 }
return dist
}
</script>
var results = [
{
'name': 'Clackmannanshire',
'coordinates': [-3.75, 56.166667],
'vote': 57.78
},
{
'name': 'Sunderland',
'coordinates': [-1.385, 54.91],
'vote': 38.66
},
{
'name': 'Isles of Scilly',
'coordinates': [-6.322778, 49.936111],
'vote': 56.39
},
{
'name': 'Broxbourne',
'coordinates': [-0.0216, 51.7495],
'vote': 33.74
},
{
'name': 'Swindon',
'coordinates': [-1.78, 51.56],
'vote': 45.34
},
{
'name': 'Kettering',
'coordinates': [-0.72292, 52.39312],
'vote': 39.01
},
{
'name': 'Shetland Islands',
'coordinates': [-1.216667, 60.35],
'vote': 56.51
},
{
'name': 'South Tyneside',
'coordinates': [-1.438, 54.959],
'vote': 37.95
},
{
'name': 'West Dunbartonshire',
'coordinates': [-4.515, 55.99],
'vote': 61.99
},
{
'name': 'Dundee',
'coordinates': [-2.97, 56.464],
'vote': 59.78
},
{
'name': 'Cannock Chase',
'coordinates': [-2.001, 52.746],
'vote': 31.14
},
{
'name': 'Coventry',
'coordinates': [-1.510556, 52.408056],
'vote': 44.4
},
{
'name': 'Rochdale',
'coordinates': [-2.161, 53.6136],
'vote': 39.93
},
{
'name': 'Erewash',
'coordinates': [-1.316667, 52.916667],
'vote': 38.77
},
{
'name': 'South Ribble',
'coordinates': [-2.69, 53.697],
'vote': 41.44
},
{
'name': 'South Somerset',
'coordinates': [-2.9893344, 50.9844058],
'vote': 42.75
},
{
'name': 'Haringey',
'coordinates': [-0.112915, 51.601632],
'vote': 75.57
},
{
'name': 'Oadby & Wigston',
'coordinates': [-1.095, 52.592],
'vote': 45.42
},
{
'name': 'Carlisle',
'coordinates': [-2.937, 54.879],
'vote': 39.86
},
{
'name': 'Peterborough',
'coordinates': [-0.25, 52.583333],
'vote': 39.11
},
{
'name': 'South Lakeland',
'coordinates': [-2.88, 54.312],
'vote': 52.86
},
{
'name': 'Reigate & Banstead',
'coordinates': [-0.16, 51.249],
'vote': 49.51
},
{
'name': 'Hastings',
'coordinates': [0.572875, 50.856302],
'vote': 45.12
},
{
'name': 'Powys',
'coordinates': [-3.416667, 52.3],
'vote': 46.26
},
{
'name': 'Ipswich',
'coordinates': [1.155556, 52.059444],
'vote': 41.74
},
{
'name': 'Chelmsford',
'coordinates': [0.4798, 51.7361],
'vote': 47.17
},
{
'name': 'Doncaster',
'coordinates': [-1.133, 53.515],
'vote': 31.04
},
{
'name': 'Vale of White Horse',
'coordinates': [-1.5, 51.6],
'vote': 56.7
},
{
'name': 'Reading',
'coordinates': [-0.973056, 51.454167],
'vote': 58.03
},
{
'name': 'Ryedale',
'coordinates': [-0.79, 54.139],
'vote': 44.74
},
];
d3.shuffle(results);
results = results.slice(0,10);
/*
{
'name': '',
'coordinates': [0,0],
'vote': 50 //remain
},
*/
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment