Another take on world population as bubble areas, this time with almost no geography at all. Countries are grouped and colored according to their continent.
Data from Natural Earth & data.worldbank.org.
Another take on world population as bubble areas, this time with almost no geography at all. Countries are grouped and colored according to their continent.
Data from Natural Earth & data.worldbank.org.
svg = d3.select 'body' | |
.append 'svg' | |
width = d3.select('svg').node().getBoundingClientRect().width | |
height = d3.select('svg').node().getBoundingClientRect().height | |
# ZOOM | |
zoomable_layer = svg.append 'g' | |
zoom = d3.zoom() | |
.scaleExtent([-Infinity,Infinity]) | |
.on 'zoom', () -> | |
zoomable_layer | |
.attrs | |
transform: d3.event.transform | |
# SEMANTIC ZOOM | |
# scale back all objects that have to be semantically zoomed | |
zoomable_layer.selectAll '.label > text' | |
.attrs | |
transform: "scale(#{1/d3.event.transform.k})" | |
# LOD & OVERLAPPING | |
lod(d3.event.transform.k) | |
svg.call zoom | |
# PACK | |
pack = d3.pack() | |
.size([width - 2, height - 2]) | |
.padding(3) | |
# COLORS | |
color = d3.scaleOrdinal(d3.schemeCategory10) | |
.domain ['North America', 'Africa', 'South America', 'Asia', 'Europe', 'Oceania', 'Seven seas (open ocean)'] | |
d3.json 'ne_50m_admin_0_countries.topo.json', (geo_data) -> | |
countries_data = topojson.feature(geo_data, geo_data.objects.countries).features | |
d3.csv 'population.csv', (data) -> | |
# use ISO a3 code as ID | |
# WARNING some records do not match | |
index = {} | |
data.forEach (d) -> | |
index[d['Country Code']] = d | |
population_data = [] | |
countries_data.forEach (d) -> | |
if d.properties.iso_a3 of index | |
population_data.push { | |
id: d.properties.iso_a3 | |
parent: d.properties.continent | |
country: d | |
value: +index[d.properties.iso_a3]['2016'] | |
} | |
# adding dummy root since d3 stratify does not handle multiple roots | |
population_data.push {id: "root", parent: ""} | |
# also add continents | |
population_data.push {id: "North America", parent: "root"} | |
population_data.push {id: "Africa", parent: "root"} | |
population_data.push {id: "South America", parent: "root"} | |
population_data.push {id: "Asia", parent: "root"} | |
population_data.push {id: "Europe", parent: "root"} | |
population_data.push {id: "Oceania", parent: "root"} | |
population_data.push {id: "Seven seas (open ocean)", parent: "root"} | |
# tree construction | |
root = (d3.stratify() | |
.id((d) -> d.id) | |
.parentId((d) -> d.parent) | |
)(population_data) | |
root | |
.sum (d) -> d.value | |
.sort (a, b) -> b.value - a.value | |
pack(root) | |
# bubbles | |
bubbles = zoomable_layer.selectAll '.bubble' | |
.data root.leaves() | |
en = bubbles.enter().append 'circle' | |
.attrs | |
class: 'bubble' | |
cx: (d) -> d.x | |
cy: (d) -> d.y | |
r: (d) -> d.r | |
fill: (d) -> color d.parent.id | |
en.append 'title' | |
.text (d) -> "#{d.data.country.properties.name_long}\nPopulation: #{d3.format(',')(d.value)}" | |
# labels | |
labels = zoomable_layer.selectAll '.label' | |
.data root.leaves() | |
en_labels = labels.enter().append 'g' | |
.attrs | |
class: 'label' | |
transform: (d) -> "translate(#{d.x},#{d.y})" | |
en_labels.append 'text' | |
.text (d) -> d.data.country.properties.name_long | |
.attrs | |
dy: '0.35em' | |
# lod | |
lod(1) | |
lod = (z) -> | |
zoomable_layer.selectAll '.label' | |
.classed 'hidden', (d) -> d.r < 18/z |
body, html { | |
width: 100%; | |
height: 100%; | |
padding: 0; | |
margin: 0; | |
} | |
body { | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
} | |
svg { | |
width: 100%; | |
height: 100%; | |
} | |
.bubble { | |
fill-opacity: 0.3; | |
stroke: black; | |
stroke-width: 0.5; | |
vector-effect: non-scaling-stroke; | |
} | |
.bubble:hover { | |
fill-opacity: 0.5; | |
} | |
.label { | |
font-family: sans-serif; | |
font-size: 10px; | |
pointer-events: none; | |
text-anchor: middle; | |
} | |
.label.hidden { | |
display: none; | |
} |
<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>World population - circle Packing</title> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="https://d3js.org/d3-selection-multi.v0.4.min.js"></script> | |
<script src="//d3js.org/topojson.v2.min.js"></script> | |
<link rel="stylesheet" href="index.css"> | |
</head> | |
<body> | |
<script src="index.js"></script> | |
</body> | |
</html> |
// Generated by CoffeeScript 1.10.0 | |
(function() { | |
var color, height, lod, pack, svg, width, zoom, zoomable_layer; | |
svg = d3.select('body').append('svg'); | |
width = d3.select('svg').node().getBoundingClientRect().width; | |
height = d3.select('svg').node().getBoundingClientRect().height; | |
zoomable_layer = svg.append('g'); | |
zoom = d3.zoom().scaleExtent([-Infinity, Infinity]).on('zoom', function() { | |
zoomable_layer.attrs({ | |
transform: d3.event.transform | |
}); | |
zoomable_layer.selectAll('.label > text').attrs({ | |
transform: "scale(" + (1 / d3.event.transform.k) + ")" | |
}); | |
return lod(d3.event.transform.k); | |
}); | |
svg.call(zoom); | |
pack = d3.pack().size([width - 2, height - 2]).padding(3); | |
color = d3.scaleOrdinal(d3.schemeCategory10).domain(['North America', 'Africa', 'South America', 'Asia', 'Europe', 'Oceania', 'Seven seas (open ocean)']); | |
d3.json('ne_50m_admin_0_countries.topo.json', function(geo_data) { | |
var countries_data; | |
countries_data = topojson.feature(geo_data, geo_data.objects.countries).features; | |
return d3.csv('population.csv', function(data) { | |
var bubbles, en, en_labels, index, labels, population_data, root; | |
index = {}; | |
data.forEach(function(d) { | |
return index[d['Country Code']] = d; | |
}); | |
population_data = []; | |
countries_data.forEach(function(d) { | |
if (d.properties.iso_a3 in index) { | |
return population_data.push({ | |
id: d.properties.iso_a3, | |
parent: d.properties.continent, | |
country: d, | |
value: +index[d.properties.iso_a3]['2016'] | |
}); | |
} | |
}); | |
population_data.push({ | |
id: "root", | |
parent: "" | |
}); | |
population_data.push({ | |
id: "North America", | |
parent: "root" | |
}); | |
population_data.push({ | |
id: "Africa", | |
parent: "root" | |
}); | |
population_data.push({ | |
id: "South America", | |
parent: "root" | |
}); | |
population_data.push({ | |
id: "Asia", | |
parent: "root" | |
}); | |
population_data.push({ | |
id: "Europe", | |
parent: "root" | |
}); | |
population_data.push({ | |
id: "Oceania", | |
parent: "root" | |
}); | |
population_data.push({ | |
id: "Seven seas (open ocean)", | |
parent: "root" | |
}); | |
root = (d3.stratify().id(function(d) { | |
return d.id; | |
}).parentId(function(d) { | |
return d.parent; | |
}))(population_data); | |
root.sum(function(d) { | |
return d.value; | |
}).sort(function(a, b) { | |
return b.value - a.value; | |
}); | |
pack(root); | |
bubbles = zoomable_layer.selectAll('.bubble').data(root.leaves()); | |
en = bubbles.enter().append('circle').attrs({ | |
"class": 'bubble', | |
cx: function(d) { | |
return d.x; | |
}, | |
cy: function(d) { | |
return d.y; | |
}, | |
r: function(d) { | |
return d.r; | |
}, | |
fill: function(d) { | |
return color(d.parent.id); | |
} | |
}); | |
en.append('title').text(function(d) { | |
return d.data.country.properties.name_long + "\nPopulation: " + (d3.format(',')(d.value)); | |
}); | |
labels = zoomable_layer.selectAll('.label').data(root.leaves()); | |
en_labels = labels.enter().append('g').attrs({ | |
"class": 'label', | |
transform: function(d) { | |
return "translate(" + d.x + "," + d.y + ")"; | |
} | |
}); | |
en_labels.append('text').text(function(d) { | |
return d.data.country.properties.name_long; | |
}).attrs({ | |
dy: '0.35em' | |
}); | |
return lod(1); | |
}); | |
}); | |
lod = function(z) { | |
return zoomable_layer.selectAll('.label').classed('hidden', function(d) { | |
return d.r < 18 / z; | |
}); | |
}; | |
}).call(this); |
(Sorry about that, but we can’t show files that are this big right now.)