Skip to content

Instantly share code, notes, and snippets.

@eetuko
Last active October 6, 2020 19:16
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 eetuko/4535086c3fabe76a173b432c44b254c6 to your computer and use it in GitHub Desktop.
Save eetuko/4535086c3fabe76a173b432c44b254c6 to your computer and use it in GitHub Desktop.
Covid-19 world-wide totals per million population - D3js maps
license: gpl-3.0
height: 540
scrolling: no
border: yes

This map uses D3.js v.5 and includes:

  • Dynamic loading of data from Worldometer
  • The use of topojson to load the shape file
  • The addition of a legend with Susie Lu's legend
  • Some degree of responsiveness
  • Some css work for a smoother experience
// Define size related variables
const dimension = d3.select(".visualisation")
.node()
.parentNode
.getBoundingClientRect();
const margin = 90;
const width = dimension.width;
const height = 500;
const aspect = width / (height - margin);
const rotate = -9.9;
const zoom = d3.zoom()
.scaleExtent([1, 30])
.translateExtent([[0,0],[width, height]])
.on('zoom', function () {
d3.select('g').attr('transform', d3.event.transform)
});
// Add the core svg block
const svg = d3.select(".visualisation")
.append("svg")
.attr("width", width)
.attr("height", height)
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", `0 0 ${width} ${height}`)
.call(zoom);
const globe = svg.append("g");
const tooltip = d3.select('.visualisation').append('div')
.attr('class', 'hidden tooltip');
const projection = d3.geoMercator()
.rotate([rotate,0])
.scale(height / (1.4 * Math.PI))
.translate([width / 2, (height - margin) / 1.2]);
const geoPath = d3.geoPath()
.projection(projection);
const colorScale = d3.scaleSqrt(["#ddd", "#777" ,"#000"]);
const map = {};
Promise.all([
d3.json('./world.topojson'),
d3.json('https://corona.lmao.ninja/v3/covid-19/countries'),
]).then(function([shapes, data]) {
var shapes = topojson.feature(shapes, "world");
// save in a global context and remove antarctic.
map.features = shapes.features.filter((d) => d.properties.ISO_A3 !== "ATA");
map.data = data;
map.metric = d3.select("#metrics").property("value");
selectData();
colorScale.domain([0,
d3.median(map.features, d => d.properties.dataPoint),
d3.max(map.features, d => d.properties.dataPoint)]);
draw();
drawLegend();
d3.select("#metrics").on("change",change);
});
function selectData() {
map.features.forEach((d) => {
var entry1 = map.data.filter(t => t.countryInfo.iso3 == d.properties.ISO_A3)[0];
if(entry1) {
d.properties.dataPoint = entry1[map.metric];
d.properties.country = entry1.country;
} else {
d.properties.dataPoint = 0;
d.properties.country = "Unknown";
}
})
};
function draw() {
globe.selectAll("path.country").remove();
globe.selectAll("path.country")
.data(map.features)
.enter()
.append("path")
.attr("class","country")
.attr('d', geoPath)
.style("fill", d => colorScale(d.properties.dataPoint))
.on('mousemove', function (d) {
tooltip.classed('hidden', false)
.html("<h6>" + d.properties.country + ": " + d.properties.dataPoint + "</h6>")
.attr('style', 'left:' + (d3.event.pageX + 15) + 'px; top:' + (d3.event.pageY + 20) + 'px');
})
.on('mouseout', function () {
tooltip.classed('hidden', true);
});
};
function drawLegend() {
svg.select(".legendLinear").remove();
svg.append("g")
.attr("class", "legendLinear")
.attr("transform", "translate(10," + (height - margin) + ")");
var shapeWidth = 40,
cellCount = 5,
shapePadding = 2,
legendTitle = map.metric.replace("PerOneMillion", "") + " per million population: ";
var legendLinear = d3.legendColor()
.title(legendTitle)
.shape("rect")
.shapeWidth(shapeWidth)
.cells(cellCount)
.labelFormat(d3.format(".3s"))
.orient('horizontal')
.shapePadding(shapePadding)
.scale(colorScale);
svg.select(".legendLinear")
.append("rect")
.attr("class", "legendBackground")
.attr("x", -5)
.attr("y", -22)
.attr("opacity", 0.9)
.attr("rx", 8)
.attr("ry", 8)
.attr("width", legendTitle.length*7.4)
.attr("height", margin);
svg.select(".legendLinear")
.call(legendLinear);
};
function change() {
map.metric = d3.select("#metrics").property("value")
selectData();
colorScale.domain([0,
d3.median(map.features, d => d.properties.dataPoint),
d3.max(map.features, d => d.properties.dataPoint)]);
draw();
drawLegend();
}
d3.select(window)
.on("resize", function() {
var targetWidth = d3.select(".visualisation").node().parentNode.getBoundingClientRect();
svg.attr("width", targetWidth);
svg.attr("height", targetWidth / aspect);
svg.attr("viewBox", `0 0 ${width} ${height}`)
});
<!doctype html>
<html>
<head>
<title>Covid-19 world-wide totals per million population maps</title>
<style>
p, .legendTitle, .label {
font-family: sans-serif;
}
.country {
-webkit-transition: all 0.5s ease-out 0s;
-moz-transition: all 0.5s ease-out 0s;
-ms-transition: all 0.5s ease-out 0s;
-o-transition: all 0.5s ease-out 0s;
transition: all 0.5s ease-out 0s;
}
.country:hover {
fill: #D90429!important;
}
.hidden {
display: none;
}
div.tooltip {
background-color: #fff;
padding: 5px 5px;
text-shadow: #f5f5f5 0 1px 0;
font: 14pt sans-serif;
border: 1px solid;
border-color: grey;
border-radius: 8px;
opacity: 0.95;
position: absolute;
box-shadow: rgba(0, 0, 0, 0.3) 0 2px 5px;
-webkit-transition: all 0.5s ease-out 0s;
-moz-transition: all 0.5s ease-out 0s;
-ms-transition: all 0.5s ease-out 0s;
-o-transition: all 0.5s ease-out 0s;
transition: all 0.5s ease-out 0s;
}
div.tooltip h6 {
margin: 0;
color: #212121;
}
.legendTitle {
font-size: 12pt;
opacity: 0.8;
text-transform: capitalize;
}
.label {
font-size: 10pt;
opacity: 0.8;
}
.legendBackground {
fill: #ffffff!important;
}
</style>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://unpkg.com/topojson@3"></script>
<script src="https://unpkg.com/topojson-client@3"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.25.6/d3-legend.min.js"></script>
</head>
<body>
<p>
Choose your metric from the list:
<select id="metrics">
<option value="casesPerOneMillion" selected="selected">Cases</option>
<option value="deathsPerOneMillion">Deaths</option>
<option value="testsPerOneMillion">Tests</option>
<option value="activePerOneMillion">Active</option>
<option value="recoveredPerOneMillion">Recovered</option>
<option value="criticalPerOneMillion">Critical</option>
</select>
</p>
<div class="visualisation"> </div>
<script src = "./covid_cases.js"></script>
</body>
</html>
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