~ / teaching / InfoVis / practical works / Interactive BubbleChart
© 2022— - Renaud Blanch
The goal of this practical work is to build a first interactive visualisation using D3.js with data having more than 2 attributes, using the position, color, and size visual variables.
You will build a clone of the BubbleChart used by GapMinder to show development indicators.
Get the archive that contains the dataset; the version of D3.js that you are going to use; and a visualisation template. Unzip it and get used to its content.
The data/gapminder
directory contains an excerpt of the datasets that are made available by GapMinder (see the README.md for licence details):
geo
, the country code; time
, the year; and a last column that gives the indicator value.
The vendor
directory contains the D3.js version 7.8.5 code.
The viz
directory contains a visualisation template that produces an HTML numbered list of the top-20 countries, according to their GDP, for the year selected with a dropdown menu.
To test this template, you need to start a web server. The simplest way to go is using python in a terminal as shown below:
% tar xzvf gapminder.tgz x gapminder/data/ [...] x gapminder/vendor [...] x gapminder/viz/ x gapminder/viz/gapminder.html % cd gapminder/ % python3 -m http.server Serving HTTP on 0.0.0.0 port 8000 ...
You can then open in a new tab your local version of the visualisation that should display the country list, as the online version does.
The first step consists in creating the bubbles using a visual mapping that will project each item for a chosen year (e.g., 2019) onto a mark (circle):
population_total
) is mapped onto the size of the mark;
life_expectancy_at_birth_with_projections
) onto the vertical axis; and
income_per_person_gdppercapita_ppp_inflation_adjusted
) onto the horizontal axis.
The sample visualisation (lines 17-50) shows how to load and merge data from multiple files.
For each country, create a SVG group that has a translation as transform
attribute and choose the translation according to data of the country.
Then add a circle element to this group.
For the horizontal axis, given the distribution of the income data, a log scale is more appropriate than a simple linear mapping. For the size attribute, a square root scale is appropriate, because we want the population mapped onto the surface of the circles (rather than their radius).
In order to prevent occlusion of small circles by large circles, the data must be sorted according to the population attribute before creating the circles.
When this first step is completed, the visualisation should look like this:
The second step consists in adding some decorations:
text
elements that label the ticks and the path
element produced by the axis, then tweak the tickSize
); and
text-anchor
and dominant-baseline
attribute of text
elements allow to control their vertical and horizontal alignment; setting those property to 'middle'
will allow to use the center of the canvas as position for the text and get it centered, whatever values you choose for the font-size
and font-family
attributes).
To change the position and the values of those cursors when the user hovers a bubble, add a listener to the groups representing the countries that will get called upon 'mouseenter'
events by D3's event handling mechanism.
The third step consists in setting attributes of bubbles that depend on countries details:
country
and world_4region
datasets and build indexes from those datasets to allow lookups using iso4
code for the countries and the world_4region
attribute for the regions;
country
dataset allows to lookup the world_4region
code from the iso4
attribute of the country.
This region code can be used to retrieve a corresponding color in the world_4region
dataset; and
title
element to the bubbles with the name of the country (that is available with a lookup of the name
attribute in to the country
dataset using the iso4
code of the countries.
The title element is handled by the browser to add a tooltip when hovering the SVG elements as shown on the capture below:
The first interaction consists in adding a legend for the regions that users can click to alternatively show or hide the bubbles corresponding to the actioned item of the legend. To do so,
selectAll
D3 function;
'click'
events and change the opacity of the relevant bubbles; and
cursor
style from 'default'
to 'pointer'
so that users will get a hint that they are clickable.
On the capture below, Europe is deselected and the corresponding bubbles have their opacity set to .1:
The next interaction consists in adding an interactive slider to select the year displayed on the visualisation. One possibility for making a slider is to:
Once this is done, we need to handle the interaction.
For that we can use D3's support for drag behavior to add a listener to the 'drag'
event that will:
e.x
property of the event by using the invert
method of the years' scale;
d3.least
may be handy for that);
set_year(year)
that will:
At this point, the slider should work, but the bubbles need to be updated to represent the data of the selected year. To do so, you should:
set_data(year)
;
selection.enter().append('g')
call used to create the bubbles by a selection.join('g')
that will work to add new elements, but also to update existing bubbles according to the new data (be sure to give a key
function as explained in the selection.data
documentation, so that the same bubble is reused for each country);
set_data(year)
when the page is first loaded to create the bubbles; and
set_data(year)
from the set_year(year)
function so the slider movements update the visualisation.
In this part you will add animations to the visualisation.
The first animation will step through the years and change accordingly the visualisation. To do so:
' ▶'
for play and '❙❙'
for pause);
'click'
events that will launch or stop the animation and change the button label (you can use a boolean variable to track whether the animation is running or not).
The animation can be stepped through using the d3.interval
function that repeatedly invokes a callback at specified interval (e.g., advance a year every 120ms).
It can call a step_year()
function that will find the next year to display (given the current year that you may need to store in a variable) and call set_year(year)
accordingly.
Special attention should be payed to the stopping conditions for the animation: the user may click the play/pause button; the animation may reach the end of the year scale; or the user may grab the year slider's thumb to choose a year.
To make the animation smoother, use the selection.transition()
mechanism to animate the bubbles radius and positions when they are updated for a new year.
The duration of the animation may be different when the change comes from the animation, or from an interaction with the slider.
Adding a duration
parameter to the set_year
and set_data
functions could help with that.
The visualisation should now look something like that:
Transform the region legend to add a map of the regions (see shape_lores_svg
attribute of the regions dataset).
The regions should be clickable to show/hide the corresponding bubbles.
Add a legend for the population. This legend should also display the population of the current country.
last update: dec. 8, 2022