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.)