Skip to content

Instantly share code, notes, and snippets.

@beemyfriend
Last active April 20, 2017 17:03
Show Gist options
  • Select an option

  • Save beemyfriend/7ae636bb5e8202b6f926485af2b45e82 to your computer and use it in GitHub Desktop.

Select an option

Save beemyfriend/7ae636bb5e8202b6f926485af2b45e82 to your computer and use it in GitHub Desktop.
D3 Makeover: Week 1
<head>
<link rel='stylesheet' href='../styles.css'>
<style>
div.tooltip{
position: absolute;
text-align: left;
width: 250px;
height: 90px;
padding: 5px;
font-size: 14px;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
#sections > div{
opacity: .3;
}
#sections div.graph-scroll-active{
opacity: 1;
}
#container{
position: relative;
overflow: auto;
}
#sections{
width: 400px;
float: left;
}
#graph{
float: left;
left: 400px;
}
#graph.graph-scroll-fixed{
position: fixed;
top: 0px;
left: 400;
margin-left: 10;
}
body{
background-color: whitesmoke;
}
svg{
background-color: none;
position: absolute;
top:0px;
z-index: 2;
}
</style>
</head>
<body>'>
<div id = 'container'>
<div id = 'sections'>
<div>
<h1>ONE LAPTOP PER CHILD</h1>
<b><a href = 'http://one.laptop.org/'>One Laptop Per Child</a></b> is an organization dedicated
to providing low cost laptop computers to the world's poorest
children. The goal is to empower the world's poorest children
through education provided by these laptops. So far, OLPC has
delivered laptops to over 2 million children and teachers in 6 different countries.
<br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br>
</div>
<div>
Ofcourse, the laptops are not evenly distributed among all the
different countries. Some countries receive more laptops than others.
Here the countries are represented by circles. The radii of these
circles correlates to the number of laptops given to children
and teachers of their corresponding countries.<br><br>
<i>The radius is correleated to the square root of the total number of laptops given.</i>
<br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br>
</div>
<div>
While it is nice to see the numbers of computers given to a particular
country, it is perhaps even more useful to see the ratio of computer given
to the total population of the country. This may provide us with more information
on the impact <b>OLPC</b> is having on the country. <br><br>
<i>The radius is correlated to the square root of the laptop/population ratio.</i><br><br>
<i>The radius of the circles will stay the same from now on.</i>
<br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br>
</div>
<div>
Truth be told, it is hard to compare the size of the circles when
they are layed out on a map. Here is a more conventional map with
continents on the x-axis and the natural log of the total number of
laptops given on the y-axis. Peru and Uruguay quickly stand for receiving
a big portion of these laptops. Indeed, these two countries received
1.5 million free laptops.<br><br>
<i>If you want to know which country each circle represents, then
simply place your mouse ontop of the circle.</i>
<br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br>
</div>
<div>
Here is the same information, but now the natural log of the
laptop/population ratio is plotted on the y-axis.
<br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br>
</div>
<div>
Most of the laptops that <b>OLPC</b> provides go to countries with
at least 20% of their populations below the national poverty line.<br><br>
<i>Poverty information provided by the CIA's <a href = 'https://www.cia.gov/library/publications/the-world-factbook/fields/2046.html'>World Factbook</a></i>
<br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br>
</div>
<div>
Most of the countries recieving laptops have high literacy rates. This
suggests that a majority of the children receiving laptops should be
able to take advantage of the free resources provided online (which they
can connect to with their computer)<br><br>
<i>A low literacy rate is a literacy rate of less than 70%, an average
literacy rate is one between 70% and 90%, and a high literacy rate is
one above 90%</i><br><br>
<i>Literacy information provided by the <a href = 'http://data.worldbank.org/indicator'>World Bank</a></i>
<br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br>
</div>
<div>
Most of the countries recieving laptops have an average or
high unemployment rate. The laptops not only provide children with
a new tool for education, but also provides them with a possible
employed future. Indeed, the number of remote jobs, that is jobs where
the location does not matter as long as the employee has a computer, is on the rise.
The children recieving the laptops, whe we suppose are literate, can now
develop skills in order to seek employment in the future.<br><br>
<i>A low unemployment rate is an unemployment rate of less than 4%, an average
unemployment rate is one between 4% and 10%, and a high unemployment rate is
one above 10%</i><br><br>
<i>Literacy information provided by the <a href = 'http://data.worldbank.org/indicator'>World Bank</a></i><br><br><br><br>
<i>The aggregated data can be found in csv format <a href = 'beemyfriend.github.io/Projects/laptops.csv'>here</a></i>
<br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br>
</div>
</div>
<div id = 'graph'></div>
</div>
<script src = 'https://d3js.org/d3.v4.js'></script>
<script src = 'https://rawgit.com/beemyfriend/beemyfriend.github.io/master/Projects/graph-scroll.js'></script>
<script>
var width = 900,
height = 600,
margin = {
left: 140,
top: 100,
right:50,
bottom: 50
},
animationDuration = 1500,
logBase = Math.E,
circleRRange = [4,40];
//=============================================//
//create data for .scaleOrdinal. spacing arrays will position
//each data point on the x-axis
//=============================================//
var continents = ["Africa", "Asia", "Central America", "Europe",
"Oceania", "South America", 'None']
var continentSpacing = [];
for(var i = 0; i < continents.length -1; i++){
var tot_width = width - margin.left - margin.right;
var spacing = tot_width/(continents.length-1);
continentSpacing.push(margin.left +((i+1) * spacing))
}
var rangeSpacing = [];
for(var i = 0; i< 4; i++){
var tot_width = width - margin.left - margin.right;
var spacing = tot_width/4;
rangeSpacing.push(margin.left + ((i+1) * spacing))
}
var colors = ['#8dd3c7', '#ffffb3', '#fdb462', '#80b1d3',
'#b3de69', '#fccde5', 'grey']
var colorsBinary = ['lightgrey', 'lightgrey', 'lightgrey', 'lightgrey',
'lightgrey', 'lightgrey', 'grey']
var svg = d3.select('#graph')
.append('svg')
.attr('width', width)
.attr('height', height);
//=============================================//
//create scales for graph axis
//=============================================//
var projection = d3.geoMercator()
.scale(width/2/Math.PI)
.translate([width/2, height/2]);
var path = d3.geoPath()
.projection(projection);
var r_count = d3.scaleSqrt()
.range(circleRRange);
var r_countxpop = d3.scaleSqrt()
.range(circleRRange);
var x_continent = d3.scaleOrdinal()
.domain(continents)
.range(continentSpacing)
var x_poverty = d3.scaleLinear()
.range([margin.left, width-margin.right])
var x_ranges = d3.scaleOrdinal()
.range(rangeSpacing)
.domain(['Low', 'Average', 'High', 'Not Available'])
var y_count = d3.scaleLog()
.base(logBase)
.range([height-margin.bottom, margin.top])
var y_countxpopulation = d3.scaleLog()
.base(logBase)
.range([height-margin.bottom, margin.top])
var clr = d3.scaleOrdinal()
.range(colors)
.domain(continents);
var clrBinary = d3.scaleOrdinal()
.range(colorsBinary)
.domain(continents);
//=============================================//
//div and handle_mouse_ functions are for tooltips
//=============================================//
var div = d3.select('body').append('div')
.attr('class', 'tooltip')
.style('opacity', 0);
function handle_mouse_over(d, i){
div.transition()
.duration(500)
.style('opacity', .8)
svg.selectAll('#' + d.Country_code)
.attr('fill-opacity', 1)
.raise()
if(d3.event.pageX < (440 + width/2) ){
div.style('left', (d3.event.pageX) + 'px')
} else {
div.style('left', (d3.event.pageX - 250) + 'px')
}
if(this.getBBox().y < (height/2)){
div.style('top', (d3.event.pageY) + 'px');
} else {
div.style('top', (d3.event.pageY - 90) + 'px');
}
div.html('<b>Country:</b> ' + d.country +
'<br><b>Computers given:</b> ' + d.count +
'<br><b>Population:</b> ' + d.Population +
'<br><b>Computers Given/Population Ratio:</b> ' + d.countxpopulation +
'<br><b>% Below Poverty Line:</b> ' + d.pop_below_poverty + '%'
);
}
function handle_mouse_out(d, i){
svg.selectAll('#' + d.Country_code)
.attr('fill-opacity', .6)
.lower()
svg.selectAll('path')
.lower()
div.transition()
.duration(500)
.style('opacity', 0);
}
var leftCountAxis = d3.axisLeft(y_count);
var leftCountxpopulationAxis = d3.axisLeft(y_countxpopulation);
var bottomContinentAxis = d3.axisBottom(x_continent);
var bottomRangesAxis = d3.axisBottom(x_ranges);
var bottomPovertyAxis = d3.axisBottom(x_poverty);
var map;
var data;
var scrollFunctions;
var gs;
//=============================================//
//load all the data before using d3.functions()
//=============================================//
d3.queue()
.defer(d3.json, 'https://raw.githubusercontent.com/beemyfriend/beemyfriend.github.io/master/Projects/worldmap.countries.geo.json')
.defer(d3.csv, 'https://raw.githubusercontent.com/beemyfriend/beemyfriend.github.io/master/Projects/laptops.csv')
.await(ready)
function ready(error, geojson, csv){
function findContinent(country){
var involved = csv.filter(function(x){return x.country == country});
if(involved.length != 0){
return involved[0].Continent
} else {
return 'None'
}
}
geojson.features
.map(function(x){return x.properties.continent = findContinent(x.properties.name);});
//=============================================//
//debugging purposes
//=============================================//
map = geojson;
data = csv;
//=============================================//
//add dynamic domains to scales
//=============================================//
x_poverty.domain(d3.extent(csv.map(function(d){return +d.pop_below_poverty})));
y_count.domain(d3.extent(csv.map(function(d){return +d.count})));
y_countxpopulation.domain(d3.extent(csv.map(function(d){return +d.countxpopulation})))
r_count.domain([0, d3.max(csv.map(function(d){return +d.count}))]);
r_countxpop.domain([0, d3.max(csv.map(function(d){return +d.countxpopulation}))])
//=============================================//
//functions used throughout the scroll story. Contains transitions,
//colors and positions of the svg elements
//=============================================//
function continentxgeolocated(){
svg.selectAll('g')
.remove();
svg.selectAll('path')
.transition().duration(animationDuration)
.attr('fill', function(d){return clr(d.properties.continent)})
.attr('stroke', 'black');
svg.selectAll('circle')
.transition().duration(animationDuration)
.attr('r', 0);
}
function countxgeolocated(){
svg.selectAll('g')
.remove()
svg.selectAll('path')
.transition().duration(animationDuration)
.attr('fill', function(d){return clrBinary(d.properties.continent)})
svg.selectAll('circle')
.transition().duration(animationDuration)
.attr('r', function(d){return r_count(+d.count)})
}
function countxpopulationxgeolocated(){
svg.selectAll('g')
.remove()
svg.selectAll('circle')
.transition().duration(animationDuration)
.attr('cx', function(d){return projection([d.longitude, d.latitude])[0]; })
.attr('cy', function(d){return projection([d.longitude, d.latitude])[1]; })
.attr('r', function(d){return r_countxpop(+d.countxpopulation)})
svg.selectAll('path')
.transition().duration(animationDuration)
.attr('fill', function(d){return clrBinary(d.properties.continent)})
.attr('stroke', 'black');
}
function countxcontinent(){
svg.selectAll('g')
.remove()
svg.selectAll('path')
.transition().duration(animationDuration)
.attr('fill', 'white')
.attr('stroke', 'white');
svg.selectAll('circle')
.transition().duration(animationDuration)
.attr('cx', function(d){return x_continent(d.Continent)})
.attr('cy', function(d){return y_count(+d.count)})
.attr('r', function(d){return r_countxpop(+d.countxpopulation)});
svg.append('g')
.attr('transform', 'translate(' + 0 + ',' + (height - margin.bottom/2) + ')')
.call(bottomContinentAxis);
svg.append('g')
.attr('transform', 'translate(' + (margin.left )+ ',' + 0 + ')')
.call(leftCountAxis)
svg.append('g')
.attr('transform', 'translate(' + margin.left + ',50)')
.append('text')
.attr('font-size', 20)
.text('Computers Given Sorted by Continent')
}
function contxpopulationxcontinent(){
svg.selectAll('g')
.remove()
svg.selectAll('circle')
.transition().duration(animationDuration)
.attr('cx', function(d){return x_continent(d.Continent)})
.attr('cy', function(d){return y_countxpopulation(+d.countxpopulation)})
.attr('r', function(d){return r_countxpop(+d.countxpopulation)});
svg.append('g')
.attr('transform', 'translate(' + 0 + ',' + (height - margin.bottom/2) + ')')
.call(bottomContinentAxis);
svg.append('g')
.attr('transform', 'translate(' + (margin.left ) + ',' + 0 + ')')
.call(leftCountxpopulationAxis)
svg.append('g')
.attr('transform', 'translate(' +( margin.left )+ ',50)')
.append('text')
.attr('font-size', 20)
.text('Computers Given (%population) Sorted by Continent')
}
function countxpopulationxpoverty(){
svg.selectAll('g')
.remove()
svg.selectAll('circle')
.transition().duration(animationDuration)
.attr('cx', function(d){return x_poverty(d.pop_below_poverty == 'NA' ? 0 : +d.pop_below_poverty)})
.attr('cy', function(d){return y_countxpopulation(+d.countxpopulation)})
.attr('r', function(d){return r_countxpop(+d.countxpopulation)});
svg.append('g')
.attr('transform', 'translate(' + 0 + ',' + (height - margin.bottom/2) + ')')
.call(bottomPovertyAxis);
svg.append('g')
.attr('transform', 'translate(' + (margin.left ) + ',' + 0 + ')')
.call(leftCountxpopulationAxis)
svg.append('g')
.attr('transform', 'translate(' + margin.left + ',50)')
.append('text')
.attr('font-size', 20)
.text('Computers Given (%population) Sorted by Poverty Rate')
}
function countxpopulationxliteracy(){
svg.selectAll('g')
.remove()
svg.selectAll('circle')
.transition().duration(animationDuration)
.attr('cx', function(d){return x_ranges(d.literacyrange)})
.attr('cy', function(d){return y_countxpopulation(+d.countxpopulation)})
.attr('r', function(d){return r_countxpop(+d.countxpopulation)});
svg.append('g')
.attr('transform', 'translate(' + 0 + ',' + (height - margin.bottom/2) + ')')
.call(bottomRangesAxis);
svg.append('g')
.attr('transform', 'translate(' + (margin.left ) + ',' + 0 + ')')
.call(leftCountxpopulationAxis)
svg.append('g')
.attr('transform', 'translate(' + margin.left + ',50)')
.append('text')
.attr('font-size', 20)
.text('Computers Given (%population) Sorted by Literacy Rate')
}
function countxpopulationxunemployment(){
svg.selectAll('g')
.remove()
svg.selectAll('circle')
.transition().duration(animationDuration)
.attr('cx', function(d){return x_ranges(d.unemploymentrange)})
.attr('cy', function(d){return y_countxpopulation(+d.countxpopulation)})
.attr('r', function(d){return r_countxpop(+d.countxpopulation)});
svg.append('g')
.attr('transform', 'translate(' + 0 + ',' + (height - margin.bottom/2) + ')')
.call(bottomRangesAxis);
svg.append('g')
.attr('transform', 'translate(' + (margin.left ) +',' + 0 + ')')
.call(leftCountxpopulationAxis)
svg.append('g')
.attr('transform', 'translate(' + margin.left + ',50)')
.append('text')
.attr('font-size', 20)
.text('Computers Given (%population) Sorted by Unemployment Rate')
}
//=============================================//
//d3.transitions() don't work if the svg elements don't already
//exist. The below svg calls create a transparent map and circles.
//The page can be loaded from anywhere and the d3.transition()
//animation will work.
//=============================================//
svg.selectAll('path')
.data(geojson.features)
.enter().append('path')
.attr('d', path)
.attr('fill', 'white')
.attr('fill-opacity', 1)
.attr('stroke', 'white')
.attr('stroke-width', 1);
svg.selectAll('circle')
.data(csv).enter()
.append('circle')
.attr('cx', function(d){return projection([d.longitude, d.latitude])[0]; })
.attr('cy', function(d){return projection([d.longitude, d.latitude])[1]; })
.attr('fill', function(d){return clr(d.Continent)})
.attr('fill-opacity', .65)
.attr('r', 0)
.attr('stroke', 'black')
.attr('id', function(d){return d.Country_code})
.on('mouseover', handle_mouse_over)
.on('mouseout', handle_mouse_out);
//=============================================//
// An array of functions in the order of the scroll-story
//=============================================//
scrollFunctions = [continentxgeolocated,
countxgeolocated,
countxpopulationxgeolocated,
countxcontinent,
contxpopulationxcontinent,
countxpopulationxpoverty,
countxpopulationxliteracy,
countxpopulationxunemployment
]
//=============================================//
//scroll-story logic
//=============================================//
gs = d3.graphScroll()
.container(d3.select('#container'))
.graph(d3.selectAll('#graph'))
.sections(d3.selectAll('#sections > div'))
.on('active', function(i){
var to_display = scrollFunctions;
to_display[i]()
})
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment