Skip to content

08. Visualizing the data

Chazzers edited this page Nov 18, 2019 · 1 revision

Visualizing the data

Having no experience at all using D3.js, I decided to start looking at examples of D3. The examples seemed pretty hard to understand at first. Eventually i found a pretty simple example of a bubble chart data visualisation, of which i could understand the code a bit.

The example i found was: Simple Bubble Chart D3 v4.

Getting the example working on my own project

Nesting the data into a parent object

Since i was stressing out about finishing my visualisation on time, i decided to copy-paste the code onto my JavaScript file and tried adding my own data to it. It didn't work at first. After console logging everything and not seeming to get understandable feedback from it, I tried to understand every bit of code to see why it wouldn't work with my own data. It seemed that in the example the data was nested into another object with the property:

	let dataset = {
		children: [{
			data: data
		}, etc...]
	}

So i tried to do the exact same by nesting my data into an object with the key: children.

// To make a bubblechart the data needs to be nested into a parent object.
const dataset = {
	children: data
}

Scaling the svg size to the window size of the browser

To make my chart scale with the size of the browser, i used window.Innerheight and window.innerWidth. I didn't want to make the dimensions too big so i reduced these sizes by 100.

// Scale the svg dimensions based on the width and height of the browser
const height = window.innerHeight - 100;
const width = window.innerWidth - 100;

Getting the colors for the bubbles

To get the colors of the bubbles the example used schemeCategory20, but apparently that didn't exist in my imported D3 object so I had to use schemeCategory10.

// Use a color scheme from D3
const color = scaleOrdinal(schemeCategory10);

Packing the information into a bubble

To pack the information into a bubble the example used the following code:

// This packs the bubble
const bubble = pack(dataset)
	.size([width, height])
	.padding(10);

Determine the size of the svg (again)

To determine the size of the svg containing the bubbles the example used a variable called diameter const diameter = 600. I wanted to make the height and width based on the browser window so I used the variables width, height:

// This determines the size of the svg
const svg = select("#svgcontainer")
	.append("svg")
	.attr("width", width)
	.attr("height", height)
	.attr("class", "bubble");

Select the grouped data

To select all the grouped data and put it in an array the example used the following code:

// this selects all the nodes and puts it in an array
const nodes = hierarchy(dataset)
	.sum(function(d){ return d.amount });

Select the individual objects

To select an individual object and data and add "g" elements to it the example used the following code:

const node = svg.selectAll(".node")
	.data(bubble(nodes).descendants())
	.enter()
	.filter(function(d){
		return !d.children;
	})
	.append("g")
	.attr("class", "node")
		.attr("transform", function(d) {
			return "translate(" + d.x + "," + d.y + ")";
		});

Adding a title to the bubbles

To add a title that can be viewed when hovering over an individual bubble the example used the following code:

// This adds a title to every individual bubble
node.append("title")
	.text(function(d) {
		return d.data.key + ": " + d.data.amount;
	});

Nesting the bubble contents

I wanted to add an animation to a hover and when i only selected the circle the text wouldnt do anything so I grouped the bubble contents into a container like this

// this nests the bubble contents into a container. I did this so i could add transitions to my entire bubble
node.append("g").attr("class", "bubble-container").append("circle")
	.attr("r", function(d) {
		return d.r;
	})
	.style("fill", function(d,i) {
		return color(i);
	});

Adding text to individual bubbles

The following piece of code selects the container of an individual bubble and adds the text of the weapon type to it. This piece of code came from the example.

// This selects the container of an individual bubble and adds the text of the weapon type to it.
node.select(".bubble-container").append("text")
	.attr("dy", ".2em")
	.style("text-anchor", "middle")
	.text(function(d) {
		return d.data.key.substring(0, d.r / 1);
	})
	.attr("font-family", "sans-serif")
	.attr("font-size", function(d){
		return d.r/3;
	})
	.attr("fill", "white")

Make bubbles clickable

The following function is a function that i got from another example. This piece of code makes the bubbles clickable and displays the weapon type and value of the data on the website.

Make code functional

The example was mutating a variable like this:

let currentBubble = undefined
const selectBubble = d => {
	if(currentBubble !== undefined){
		svg.selectAll("#details-popup").remove();
	}
	currentBubble = select(this);
}

Since this is not allowed in functional programming i transformed the code to look like this:

const selectBubble = d => {

	const currentBubble = select(this);

	if(currentBubble !== this){
		svg.selectAll("#details-popup").remove();
	}
}

Creating a textblock on click

The following code snippet creates a textblock and adds the weapon type and value to that textblock.

const textblock = svg.selectAll("#details-popup")
	.data([d])
	.enter()
	.append("g")
	.attr("id", "details-popup")
	.attr("font-size", 14)
	.attr("font-family", "sans-serif")
	.attr("text-anchor", "start")
	.attr("transform", data => `translate(0, 20)`);

textblock.append("text")
	.text(d.data.key + ": " + d.data.amount)
	.attr("y", "16")

The complete code of the click function

So the entire function looks like this:

const selectBubble = d => {

	const currentBubble = select(this);

	if(currentBubble !== this){
		svg.selectAll("#details-popup").remove();
	}

	const textblock = svg.selectAll("#details-popup")
		.data([d])
		.enter()
		.append("g")
		.attr("id", "details-popup")
		.attr("font-size", 14)
		.attr("font-family", "sans-serif")
		.attr("text-anchor", "start")
		.attr("transform", data => `translate(0, 20)`);

	textblock.append("text")
		.text(d.data.key + ": " + d.data.amount)
		.attr("y", "16")
}
node.on("click", selectBubble);

Complete code of visualizeData function

My complete code for the visualizeData function looks like this:

// This function visualizes the transformed data
function visualizeData(data) {
	// To make a bubblechart the data needs to be nested into a parent object.
	const dataset = {
		children: data
	}

	// Scale the svg dimensions based on the width and height of the browser
	const height = window.innerHeight - 100;
	const width = window.innerWidth - 100;

	// Use a color scheme from D3
	const color = scaleOrdinal(schemeCategory10);

	// This packs the bubble
	const bubble = pack(dataset)
		.size([width, height])
		.padding(10);

	// This decides the size of the svg
	const svg = select("#svgcontainer")
		.append("svg")
		.attr("width", width)
		.attr("height", height)
		.attr("class", "bubble");


	// this selects all the nodes and puts it in an array
	const nodes = hierarchy(dataset)
		.sum(function(d){ return d.amount });

	// this selects all of the individual nodes, adds a "g" element to all the nodes and adds a class as well
	const node = svg.selectAll(".node")
		.data(bubble(nodes).descendants())
		.enter()
		.filter(function(d){
			return !d.children;
		})
		.append("g")
		.attr("class", "node")
            .attr("transform", function(d) {
                return "translate(" + d.x + "," + d.y + ")";
            });

	// This adds a title to every individual bubble
    node.append("title")
        .text(function(d) {
            return d.data.key + ": " + d.data.amount;
        });

	// this nests the bubble contents into a container. I did this so i could add transitions to my entire bubble
    node.append("g").attr("class", "bubble-container").append("circle")
        .attr("r", function(d) {
            return d.r;
        })
        .style("fill", function(d,i) {
            return color(i);
        });

	// This selects the container of an individual bubble and adds the text of the weapon type to it.
    node.select(".bubble-container").append("text")
        .attr("dy", ".2em")
        .style("text-anchor", "middle")
        .text(function(d) {
            return d.data.key.substring(0, d.r / 1);
        })
        .attr("font-family", "sans-serif")
        .attr("font-size", function(d){
			return d.r/3;
        })
        .attr("fill", "white")

		// This is a click function that when clicked displays the weapon type and value of the clicked bubble
		const selectBubble = d => {

			const currentBubble = select(this);

			if(currentBubble !== this){
				svg.selectAll("#details-popup").remove();
			}

			const textblock = svg.selectAll("#details-popup")
				.data([d])
				.enter()
				.append("g")
				.attr("id", "details-popup")
				.attr("font-size", 14)
				.attr("font-family", "sans-serif")
				.attr("text-anchor", "start")
				.attr("transform", data => `translate(0, 20)`);

			textblock.append("text")
				.text(d.data.key + ": " + d.data.amount)
				.attr("y", "16")
		}
		node.on("click", selectBubble);

        select(self.frameElement)
            .style("height", height + "px");
	}