Building you first Interactive Business Tree of Success with D3.js
Data Visualization


Introduction

Data visualizations are powerful tools for understanding and communicating complex information.  In this tutuorial, we will learn how to build a Tree of Success visualization using D3.js, a popular JavaScript library for data visualizaiton.  Even if you're unfamiliar with JavaScript and D3.js, don't worry! We'll guide you through this step-by-step.  Let's get started!

Prerequisites:

To follow along with this tutorial, you'll need a basic understanding of HTML and CSS. 

Setup the HTML Structure

Start by creating a new html file and open it in a text editor.  Add the following code to set up the basic HTML structure:

<!DOCTYPE html>

<html>

<head>

<script src="https://d3js.org/d3.v5.min.js"></script>

<style>

/* CSS styling goes here */

</style>

</head>

<body>

<svg width="960" height="500"></svg>

<script>

// JavaScript code goes here

</script>

</body>

</html>




Make sure to save the file with a .html extension.

Linking D3.js Library:

To use D3.js, we need to include the library in our HTML file.  We can do this by adding the following script tag within the <head> section.


This script tag fetches the D#.js library from the official D#.js website and makes it avaiable for use in our code.

Defining CSS Styling:

Let's add some CSS rules to style our visualization. Within the <style> section, add the following CSS code:

<



<style>

.node circle {

fill: #fff;

stroke: steelblue;

stroke-width: 1.7px;

transition: fill 0.3s;

}



/* CSS rules for other elements */



svg {

background: linear-gradient(to bottom, #f2f2f2, #d9d9d9);

}

</style>






These CSS rules define the appearance of the nodes, links, and other elements in our visualization.  Feel free to customize the styling according to your preferences.

Creating the SVG Container:

Inside the <body> section, add the following SVG container code:




<

<<svg width='960 height="500"</svg>







This creates an SVG element with a width of 960 pixels and a height of 500 pixels.  We will use this container to render our visualization.

JavaScript Code:

Now comes the exciting part - adding the JavaScript code that leverages D3.js to build our Business Tree of Success visualization.


Let's break down this code snippet:

var svg = d3.select("svg"): We use the D3.js select() method to select the SVG container we created earlier.

width and height variables: These variables store the width and height attributes of the SVG container.

g variable: We create a new <g> (group) element and append it to the SVG container. This group element will serve as the main container for our visualization. We also apply a translation transform to shift the entire visualization by an offset of (40, 40) pixels.

svg.append("text"): This code appends a <text> element to the SVG container. It sets the class as "title" and positions it at the center of the SVG container. The text content is set as "Business Tree of Success", which will serve as the title of our visualization.

Preparing the Data:

In order to visualize our Business Tree of Success, we need to define the hierarchical structure and relationships between the nodes.  We'll use a CSV format to represent this data.  Here's how you can prepare the data:




<

<script>

// ...



var stratify = d3.stratify()

.parentId(function(d) { return d.id.substring(0, d.id.lastIndexOf(".")); });



var csvData = `id,value

Competitive Advantage,

Competitive Advantage.Digital Transformation,

Competitive Advantage.Digital Transformation.Cloud Solutions,

Competitive Advantage.Digital Transformation.Cloud Solutions.Efficiency,

Competitive Advantage.Digital Transformation.Cloud Solutions.Security,

Competitive Advantage.Digital Transformation.Cloud Solutions.Agility

Competitive Advantage.Digital Transformation.Cloud Solutions.Cost Savings,`;



var data = d3.csvParse(csvData);



var root = stratify(data)

.sort(function(a, b) { return (a.height - b.height) || a.id.localeCompare(b.id); });



// ...

</script>







Let's explain the code above:

var stratify = d3.stratify(): We create a stratify layout using D3.js. The stratify layout is responsible for converting our CSV data into a hierarchical structure suitable for visualization.

stratify.parentId(): We define a function that determines the parent ID for each data point in the CSV. In this case, we extract the parent ID by finding the substring of the id column up to the last occurrence of a dot (".") character.

var csvData = ...```: This variable stores the CSV data as a multiline string. Each line represents a node in our Business Tree of Success, and the indentation signifies the hierarchical relationships between the nodes.

var data = d3.csvParse(csvData): We use the d3.csvParse() function to parse the CSV data and convert it into an array of JavaScript objects.

var root = stratify(data): We apply the stratify layout to our parsed data to create the hierarchical structure. The resulting structure is stored in the root variable.

Constructing the Tree Diagram:

Now its time to constuct and render the tree diagram using D3.js:




<

<script>

// ...



var tree = d3.tree().size([width - 80, height - 120]);



var nodes = tree(root).descendants(),

links = tree(root).links();



var nodeGroup = g.selectAll(".node")

.data(nodes)

.enter().append("g")

.attr("class", function(d) { return "node" + (d.children ? " node--internal" : " node--leaf"); })

.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });



// ...

</script>









Here's what the code does:

var tree = d3.tree().size([width - 80, height - 120]): We create a new tree layout using D3.js. The tree layout generates the positions of the nodes in the tree diagram. We set the size of the layout to be slightly smaller than the SVG container size, taking into account the offset of (40, 40).

var nodes = tree(root).descendants(): We generate an array of nodes using the tree layout and the root hierarchy we created earlier. The descendants() method returns an array of all nodes in the hierarchy.

var links = tree(root).links(): Similarly, we generate an array of links using the tree layout and the root hierarchy. The links() method returns an array of objects representing the links between parent and child nodes.

var nodeGroup = g.selectAll(".node"): We select all elements with the class "node" within the g container element. If the elements don't exist yet, they will be created in the next steps.

.data(nodes): We bind the nodes array to the selected elements. This associates each node with a corresponding element.

.enter().append("g"): We use the enter() selection to create new g elements for each node that doesn't have a corresponding element yet.

.attr("class", function(d) { return "node" + (d.children ? " node--internal" : " node--leaf"); }): We set the class of each g element based on whether it represents an internal node or a leaf node. This class will be used for styling the nodes later.

.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }): We apply a translation transform to each g element to position it according to its calculated x and y coordinates. This ensures that the nodes are rendered in the correct locations within the SVG container.

Rendering Nodes and Links:

Now, let's consinue with the code for rendring the nodes and links in our visualization:




<

<script>

// ...



nodeGroup.append("circle")

.attr("r", 10)

.on("mouseover", function(d) {

d3.select(this).transition().duration(200).attr("r", 12);

d3.select(this.parentNode).select(".callout").text("BeCloud Implemented");

})

.on("mouseout", function(d) {

d3.select(this).transition().duration(200).attr("r", 10);

d3.select(this.parentNode).select(".callout").text("");

});



// ...

</script>









Here's what this code snippet does:

nodeGroup.append("circle"): We append a <circle> element to each g element representing a node.

.attr("r", 10): We set the radius of each circle to 10 pixels. This determines the size of the nodes in our visualization.

.on("mouseover", function(d) { ... }): We attach an event listener to the circles for the "mouseover" event. When a circle is hovered over, the following code is executed:

d3.select(this).transition().duration(200).attr("r", 12): The hovered circle is selected and smoothly transitioned to a larger size by changing the radius to 12 pixels.

d3.select(this.parentNode).select(".callout").text("BeCloud Implemented"): The parent <g> element of the hovered circle is selected, and the .callout element within it is updated with the text "BeCloud Implemented". This text will be shown as a callout next to the node.

.on("mouseout", function(d) { ... }): We attach an event listener to the circles for the "mouseout" event. When the mouse moves out of a circle, the following code is executed:

d3.select(this).transition().duration(200).attr("r", 10): The circle is transitioned back to its original size by changing the radius back to 10 pixels.

d3.select(this.parentNode).select(".callout").text(""): The text content of the .callout element is cleared.

Next, lets add code for rendering the node labels and the callout text:




<

<script>

// ...



nodeGroup.append("text")

.attr("dy", 37)

.style("fill", "#333")

.text(function(d) { return d.data.id.substring(d.data.id.lastIndexOf(".") + 1); })

.raise();



nodeGroup.append("text")

.attr("class", "callout")

.attr("dy", 25)

.style("text-anchor", "right");



// ...

</script>





// ...

</script>







Here is an explanation of the code above:

nodeGroup.append("text"): We append a <text> element to each g element representing a node.

.attr("dy", 37): We set the dy attribute to 37 pixels to adjust the vertical position of the text relative to the circle.

.style("fill", "#333"): We set the fill color of the text to "#333" to ensure it's easily readable.

.text(function(d) { return d.data.id.substring(d.data.id.lastIndexOf(".") + 1); }): We set the text content of each <text> element to a substring of the node's id. The substring is obtained by excluding the portion of the id string after the last occurrence of a dot (".") character. This way, we display only the relevant label for each node.

.raise(): We raise the <text> element above the circle element to ensure it's displayed on top and not obscured.

Rendering the links:




<

<script>

// ...



var linkGroup = g.selectAll(".link")

.data(links)

.enter().append("path")

.attr("class", "link")

.attr("d", function(d) {

return "M" + d.source.x + "," + d.source.y

+ "C" + (d.source.x + d.target.x) / 2 + "," + d.source.y

+ " " + (d.source.x + d.target.x) / 2 + "," + d.target.y

+ " " + d.target.x + "," + d.target.y;

});



// ...

</script>








Lets break down the code above:

var linkGroup = g.selectAll(".link"): We select all elements with the class "link" within the g container element. If the elements don't exist yet, they will be created in the next steps.

.data(links): We bind the links array to the selected elements. This associates each link with a corresponding element.

.enter().append("path"): We use the enter() selection to create new <path> elements for each link that doesn't have a corresponding element yet.

.attr("class", "link"): We set the class of each <path> element to "link". This class will be used for styling the links later.

.attr("d", function(d) { ... }): We set the "d" attribute of each <path> element to define the path of the link. The path is defined using a combination of move-to (M) and curve-to (C) commands, based on the source and target node positions.

Animating the Visualization:

To add a smooth animation to our visualizaiton, let's apply transitions to the nodes and links:


L


<

<script>

// ...



nodeGroup.style("display", "none")

.style("opacity", 0)

.transition()

.delay(function(d, i) { return d.depth * 500; })

.style("display", "inline")

.style("opacity", 1)

.duration(1000);



linkGroup.style("display", "none")

.style("opacity", 0)

.transition()

.delay(function(d) { return d.source.depth * 500; })

.style("display", "inline")

.style("opacity", 1)

.duration(1000);



// ...

</script>









nodeGroup.style("display", "none"): Initially, we hide the nodes by setting their display style to "none".

.style("opacity", 0): We set the initial opacity of the nodes to 0, making them transparent.

.transition(): We initiate a transition on the nodes.

.delay(function(d, i) { return d.depth * 500; }): We apply a delay to each node's transition based on its depth in the hierarchy. This creates a staggered effect, with deeper nodes appearing later.

.style("display", "inline"): After the delay, we change the display style of each node to "inline" to make them visible.

.style("opacity", 1): We gradually increase the opacity of the nodes to 1 during the transition, making them fully opaque.

.duration(1000): The transition duration is set to 1000 milliseconds (1 second).

Wrapping Up:

Congratulations! You've successfully built a Business Tree of Success visualization using D3.js. We covered the essential steps, from setting up the HTML structure, linking the D3.js library, preparing the data, constructing the tree diagram, rendering the nodes and links, and animating the visualization. You can further customize the styling, add interactivity, or incorporate additional features to enhance the visualization.

Feel free to experiment, explore the D3.js documentation, and create your own unique data visualizations. The possibilities are endless!

You can see the full code here:

Competitive AdvantageDigital TransformationCloud SolutionsAgilityCost SavingsEfficiencySecurityBusiness Tree of SuccessCompetitive AdvantageDigital TransformationCloud SolutionsAgilityCost SavingsEfficiencySecurityBusiness Tree of Success


Full Code







James Phipps 6 July, 2023
Share this post
Tags
Archive
Sign in to leave a comment

  


Server Migration to EC2 using AWS VM Import