Before you go, check out these stories!

0
Hackernoon logoCreating Composite Node of a Graph using D3.js by@argone

Creating Composite Node of a Graph using D3.js

Author profile picture

@argoneafrin

fullstackdev

Lately I have been using D3 for visualizing data for a React project and it got my attention for a while. I was especially interested as to the scope of this very powerful tool that has a great problem solving ability range related to any kind of data visualization.

It gives you a feeling that you can do

__ANYTHING__
. Okay, lets talk about it.

What is D3.jsย ?

D3.js is a javascript library used for visualizing data. It is very powerful with the combination of

__SVG__ 
(Support Vector Graphics) and
__HTML__
.

Now it depends on the creativity of the developer using it to actually visualize it in the most beautiful way.ย 

Since this is data drivenย , d3.js works on your pure data and transforms it in a graphical way. While applying d3.js, you will doing lot of CSS, at the same time solving some Mathematical Coordinate Geometry problems. For instance, applying Pythagorean theorem, calculating the distance between two coordinates `

(x1, y1)
` and `
(x2, y2)
` on a graph etc.

This article focuses on creating a complex node of a graph that is a node having lot of elements or information attached to it, instead of just an empty circle.

You will find so many example snippets and gists especially on bl.ocks.org, stackoverflow or observablehq for creating graphs or trees with

v3 
version but not many on the latest
v5 
version.

Pre-requisite

Html, CSSย , Javascript, Coordinate Geometry.

Let's Start

We will be writing a simple working script for creating a complex SVG node.

You need to include the following

<script>
inside your html
<body>
for using d3
v5 
library.

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

</body>

Canvas and data container element

Specify some

width
and
height
for the canvas where all your SVG elements will reside. We then call
d3.select()
on
body
element, appending
svg
to it, and specifying properties like
width
and
height
We currently have our json data as
nodes 
with some params that we may need to visualize.

<script>
      var width = 500, height = 400;
      const nodes = [
        {
          id: 0,
          name: "ServiceGroup",
          description: "Port : 80",
          connection_count: 3
        }
      ];

      const svg = d3
        .select("body")
        .append("svg")
        .attr("width", width)
        .attr("height", height);
</script>

Lets append new element

g 
into
svg
variable (canvas) and insert node data into it, indexing with
id
.

let circle = svg
            .append("svg:g")
            .selectAll("g")
            .data(nodes, d => d.id);
const g = circle.enter();

Rectangular node

Now we append

rect
element into our
svg
with
(x,y)
coordinates as
(0,0)
.

const rectangularNode = g
        .append("svg:rect")
        .attr("class", "node")
        .attr("x", d => {
          return 0;
        })
        .attr("y", d => {
          return 0;
        });

Now here it gets tricky for deciding the positions of elements to be placed inside the rectangular node. We have our basic node ready and will place inner elements with reference to the rectangular node and not the canvas. One of the ways for doing that is to get the coordinates

rect
element using
getBBox()
.

var outerNodebbox = rectangularNode.node().getBBox();

Image element

Since we have placement coordinates of this box, we can place the things inside.

Say I want to place below logo.

It's simple

const images = g
        .append("svg:image")
        .attr(
          "xlink:href",
          "https://img.icons8.com/ios-glyphs/30/000000/superman.png"
        )
        .attr("x", function(d) {
          let X = outerNodebbox.x;
          return X + 10;
        })
        .attr("y", function(d) {
          let Y = outerNodebbox.y;
          let HEIGHT = outerNodebbox.height;
          return Y + HEIGHT / 3 ;
        });

As you can see we have tweaked variables

X
and
Y
a little bit for inner positions. I wanted the image to be in middle, tabbed (
10
) from the left.

Text element

Similarly to add a text, we use the node data using keys and append

text
to
svg
like below:

const label = g
        .append("svg:text")
        .attr("class", "name")
        .attr("dx", function(d) {
          return outerNodebbox.width / 3;
        })
        .attr("dy", function(d) {
          return outerNodebbox.height / 3;
        })
        .attr("dominant-baseline", "central")
        .text(d => {
          return d["name"];
        });

Another example to add text

const description = g
        .append("svg:text")
        .attr("class", "description")
        .attr("dx", function(d) {
          return outerNodebbox.width / 3;
        })
        .attr("dy", function(d) {
          return (2 * outerNodebbox.height) / 3;
        })
        .attr("dominant-baseline", "central")
        .text(d => {
          return d["description"];
        });

Circular counter element

Now what if I want to insert a circle inside the rectangular node and maintain a text inside it. We do it as follows:

const count_circle = g
        .append("svg:circle")
        .attr("class", "countCircle")
        .style("visibility", "unset")
        .attr("r", 10)
        .attr("cx", function(d) {
          let X = outerNodebbox.x;
          let WIDTH = outerNodebbox.width;
          return X + (2.5 * WIDTH) / 3;
        })
        .attr("cy", function(d) {
          let Y = outerNodebbox.y;
          let HEIGHT = outerNodebbox.height;
          return Y + (2 * HEIGHT) / 3;
        });

and text

const count_text = g
        .append("svg:text")
        .attr("class", "countText")
        .attr("r", 10)
        .attr("dx", function(d) {
          let X = outerNodebbox.x;
          let WIDTH = outerNodebbox.width;
          return X + (2.5 * WIDTH) / 3;
        })
        .attr("dy", function(d) {
          let Y = outerNodebbox.y;
          let HEIGHT = outerNodebbox.height;
          return Y + (2 * HEIGHT) / 3;
        })
        .attr("dominant-baseline", "central")
        .text(d => {
          return d["connection_count"];
        });

At last simply merge all the elements into one.

circle = g.merge(circle);

If you wonder how does it look like while browser inspecting, see for yourself.

Cool right!! You can find the entire code here

Happy learning!!!

Previously published at https://medium.com/@afrinchakure12/a-composite-node-of-a-graph-using-d3-js-v5-146a0e0a0473

Tags

The Noonification banner

Subscribe to get your daily round-up of top tech stories!