WORK IN PROGRESS
Attempt for #d3makeover, presenting data on how many laptops from the One Laptop Per Child (OLPC) project are present in each country.
Code based on djjupa's block: World Map of trust, which I updated to D4 v 4, and includes d3.zoom, with zoom.translateExtent. Also includes d3-tip, based on Micah Stubbs' example.
forked from tomshanley's block: One Laptop Per Child #D3makeover
xxxxxxxxxx
<meta charset="utf-8">
<link href="https://fonts.googleapis.com/css?family=Vollkorn" rel="stylesheet">
<style>
body {
font-family: 'Vollkorn', serif;
}
h1,
a {
color: black;
}
.legend {
font-size: 12px;
}
.sea {
fill: Azure;
}
.land {
fill: #999999;
opacity: .5;
}
.boundary {
fill: none;
stroke: LightGrey;
stroke-linejoin: round;
stroke-linecap: round;
}
.triangle, .legend-triangle, .legend-rect {
cursor: pointer
}
.legend-triangle, .legend-text {
fill: grey;
}
.legend-text {
font-size: 12px
}
.legend-rect {
stroke: white
}
.label {
text-anchor: middle;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v0.3.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
<body>
<h1>Changes in life expectancy between 1960 and 2015</h1>
<p>The map shows increases in life expectancy (triangle height) between 1960 and 2015.</p>
<p>The current (2015) average life expectancy is represented by colour, with red showings lower life expectancies.</p>
<p>Despite the larger increases across most African countries, average life expectancy is often lower than other parts of the world.
<div id="map"></div>
<p>Data source: <a href="https://data.worldbank.org/indicator/SP.DYN.LE00.IN">World Bank</a> (via <a href="https://www.makeovermonday.co.uk/data/">Makeover Monday</a>)</p>
<script>
const format = d3.format(',');
var width = 1100,
height = 720;
var labelPosition = 90;
var labelGap = 20;
var triangeWidth = 10;
var projection = d3.geoBonne()
.translate([width / 2, height / 2])
.scale(width / 2 / Math.PI)
var path = d3.geoPath()
.projection(projection);
var svg = d3.select("#map").append("svg")
.attr("width", width)
.attr("height", height);
var defs = svg.append("defs");
defs.append("path")
.datum({ type: "Sphere" })
.attr("id", "sphere")
.attr("d", path);
defs.append("clipPath")
.attr("id", "clip")
.append("use")
.attr("xlink:href", "#sphere");
svg.append("use")
.attr("class", "stroke")
.attr("xlink:href", "#sphere");
svg.append("use")
.attr("class", "fill")
.attr("xlink:href", "#sphere");
var g = svg.append("g")
.attr("class", "map");
g.append("rect")
.attr("class", "sea")
.attr("width", width)
.attr("height", height)
.attr("clip-path", "url(#clip)");
let labelCountry = g.append("text")
.text("Hover over country")
.attr("class", "label")
.attr("x", width / 2)
.attr("y", labelPosition)
let label1960 = g.append("text")
.text("for details")
.attr("class", "label")
.attr("x", width / 2)
.attr("y", labelPosition + labelGap)
let label2015 = g.append("text")
.text("")
.attr("class", "label")
.attr("x", width / 2)
.attr("y", labelPosition + (labelGap * 2))
let labelPercentage = g.append("text")
.text("")
.attr("class", "label")
.attr("x", width / 2)
.attr("y", labelPosition + (labelGap * 3))
d3.json("world-50m.json", function (error, world) {
g.append("path")
.datum(topojson.feature(world, world.objects.countries))
.attr("class", "land")
.attr("d", path)
.attr("clip-path", "url(#clip)")
g.append("path")
.datum(topojson.mesh(world, world.objects.countries, function (a, b) { return a !== b; }))
.attr("class", "boundary")
.attr("d", path)
.attr("clip-path", "url(#clip)");
d3.csv("lifeexpectancy.csv", convertTextToNumbers, function (error, data) {
let data2015NoBlanks = data.filter(function (d) { return d.year == 2015 && d.percentage > 0; });
data2015NoBlanks.sort(function (a, b) {
return b.lat - a.lat;
});
let extentPercentage = d3.extent(data2015NoBlanks, function (d) { return d.percentage });
let extentLE2015 = d3.extent(data2015NoBlanks, function (d) { return d.le })
let triangeHeight = d3.scaleLinear()
.domain([0, extentPercentage[1]])
.range([0, 50]);
let colour = d3.scaleSequential(d3.interpolateRdYlGn)
.domain(extentLE2015)
var triangles = g.append("g")
.attr("class", "triangle");
triangles.selectAll("path")
.data(data2015NoBlanks)
.enter()
.append("path")
.attr("d", function (d) {
let projectedLng = projection([d.lng, d.lat])[0];
let projectedLat = projection([d.lng, d.lat])[1];
let p = "M" + (projectedLng - (triangeWidth / 2)) + " " + projectedLat + " "
+ "L" + projectedLng + " " + (projectedLat - triangeHeight(d.percentage)) + " "
+ "L" + (projectedLng + (triangeWidth / 2)) + " " + projectedLat + " "
+ "z";
return p;
})
.style("fill", function (d) { return colour(d.le); })
.style("stroke", "#999999")
.style("opacity", 0.7)
.on("mouseover", function (d) {
triangles.selectAll("path")
.transition()
.duration(50)
.style("opacity", function (t) {
return t.name == d.name ? 1 : 0.4
})
labelCountry.text(d.name)
.style("font-weight", "bold")
label1960.text("1960: " + roundNumber(d.startLE))
label2015.text("2016: " + roundNumber(d.le))
labelPercentage.text(formatPercentage(d.percentage))
})
.on("mouseout", function (d) {
triangles.selectAll("path")
.transition()
.duration(50)
.style("opacity", 0.7)
labelCountry.text("Hover over country")
.style("font-weight", "normal")
label1960.text("for details")
label2015.text("")
labelPercentage.text("")
})
let legendTriangles = svg.append("g")
.attr("transform", "translate(20,600)")
let legendDataPercentage = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
legendTrianglesItems = legendTriangles.selectAll("path")
.data(legendDataPercentage)
.enter()
.append("g")
.attr("transform", function (d, i) {
return "translate(" + (i * (triangeWidth + 18)) + "," + 50 + ")"
})
legendTrianglesItems.append("path")
.attr("class", "legend-triangle")
.attr("d", function (d) {
let x = 0
let y = 0
let p = "M" + (x - (triangeWidth / 2)) + " " + y + " "
+ "L" + x + " " + (y - triangeHeight(d)) + " "
+ "L" + (x + (triangeWidth / 2)) + " " + y + " "
+ "z";
return p;
})
.on("mouseover", function(d) {
triangles.selectAll("path")
.style("opacity", function(t){
return t.percentage > d ? 1 : 0;
})
})
.on("mouseout", function(d) {
triangles.selectAll("path")
.style("opacity", 0.7)
})
legendTrianglesItems.append("text")
.attr("class", "legend-text")
.text(function (d) {
return d == 1 ? roundPercentage(d) + "+" : roundPercentage(d)
})
.attr("y", 15)
.style("text-anchor", "middle")
legendTriangles.append("text")
.text("% increase from 1960 to 2015")
.attr("x", 0)
.attr("y", -15)
.style("text-anchor", "start")
const legendWidth = 180;
const legendPadding = 40;
var legendColour = svg.append("g")
.attr("transform", "translate(800,600)")
let legendDataLE = [50, 55, 65, 70, 75, 80]
const legendHeight = legendWidth / legendDataLE.length;
var legendG = legendColour.append("g")
.attr("class", "legendLinear")
.attr("transform", "translate(" + legendPadding + "," + 0 + ")");
legendLEItems = legendG.selectAll("rect")
.data(legendDataLE)
.enter()
.append("g")
.attr("transform", function (d, i) {
return "translate(" + (i * (legendHeight)) + "," + 5 + ")"
})
legendLEItems.append("rect")
.attr("class", "legend-rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", legendHeight)
.attr("height", legendHeight)
.style("fill", function(d){ return colour(d) })
.on("mouseover", function(d) {
triangles.selectAll("path")
.style("opacity", function(t){
return t.le >= d && t.le < (d + 5) ? 1 : 0;
})
})
.on("mouseout", function(d) {
triangles.selectAll("path")
.style("opacity", 0.7)
})
legendG.append("text")
.attr("class", "legend-text")
.text("50")
.attr("x", 0)
.attr("y", legendHeight + 20)
legendG.append("text")
.attr("class", "legend-text")
.text("80+")
.attr("x", legendWidth)
.attr("y", legendHeight + 20)
.style("text-anchor", "end")
legendG.append("text")
.text("Average life expectancy 2015")
.attr("x", 0)
.attr("y", -15)
.style("text-anchor", "start")
});
});
function roundNumber(n) {
return Math.round(n);
};
function roundPercentage(n) {
return roundNumber(n * 100)
};
function formatPercentage(n) {
return roundPercentage(n) + "%";
};
function convertTextToNumbers(d) {
d.le = +d.le;
d.male = +d.male;
d.female = +d.female;
d.lat = +d.lat;
d.lng = +d.lng;
d.percentage = +d.percentage;
return d;
};
</script>
Modified http://d3js.org/topojson.v1.min.js to a secure url
https://d3js.org/d3.v4.min.js
https://d3js.org/topojson.v1.min.js
https://d3js.org/d3-scale-chromatic.v0.3.min.js
https://d3js.org/d3-geo-projection.v2.min.js