Network Graph with D3js on Canvas

A step-by-step to generate network graphs with D3.js

Ruben Triviño
Towards Data Science

--

Network Graph Simulation SVG and Canvas by Rubén Triviño
  1. How to use D3 with Web Components.
  2. Network Graph with D3js on Canvas.

Introduction

Data visualization is a prism with many faces; where some see a simple bar chart, others see a clear growth trend or even an accomplished goal.

There are many solutions for data graphing; from enterprise applications like Tableau, QlikView, Carto or Datastudio, to do-it-yourself tools such as bokeh, matplotlib or d3.js. The truth is that each situation requires a different approach and many already offer more than enough, there is no need to reinvent the wheel if it is not necessary but… If you are in need of a DIY solution you have come to the right place.

Problem Statement: Network Graphics

This time, we are going to explore an example of a network graphic using the HTML Canvas element. The Canvas element allows us to draw content without the need to store all the visual elements in the DOM, as it is the case when using an SVG for the graphing. This allows the creation of graphics with a high volume of elements without loosing performane caused by the browser rendering process.

The graph that we are going to see is a diagram of nodes linked by lines that represent the relationship between them. The dataset used is from BuscoExtra where I work as operation analyst. In a nutshell, BuscoExtra is a staffing agency focused on hospitality. In Spain, employers need to draft a legal employment contract to hire employees even for small jobs that last only a few hours.

So, the data structure stores different employment contracts that were generated in a certain period of time and these will be the lines while the restaurants, or employers, and waiters, or employees, will be the nodes of our diagram.

Therefore, there are two types of nodes: Worker and Establishment. We will use two colors to distinguish between them and a smaller radius for the Worker nodes as they appear in a bigger amount.

Data structures definition

Nodes of type Establishment have a radius that varies with the number of workers that they have hired. So, the smaller nodes are related to those with less amount of Worker nodes linked to them.

In addition, as a contract between Worker and Establishment may occur multiple times we will add a variable named interactions so lines between nodes will be wider or thinner depending on the number of interactions. We may see an example in the next picture:

Ejemplo de Nodos de distinto tamaño y conexiones diferentes
Example of nodes with different connections. Own elaboration.

All these features are in fact different dimensions that enrich the graph and as someone said: a picture worth more than a thousand words. I believe that too.

In another data structure we could use a range of colors to represent the nodes according to some other criterion; use different shapes according to the type of node (squares, triangles, etc..) or better still, change the type of line that joins the nodes according to the type of connection: dashed, punctuated line, different color, etc..

We could use a variety of properties to arrange the information and add additional dimensions, as some do with Spotify to display the music styles, representing the more distant styles on opposite poles. The possibilities are simply endless, limited only by our creativity.

Spotify artists network graph by number of fans. From: Reddit: u/Enguzelharf

Getting Started

First we create a base project in Stencil and provide the data structure in the input parameters (here is how can do it super fast).

Once we have the data in the component, we will begin to define the drawing strategy. Unlike SVG, in Canvas we will use the properties of a brush: thickness, color, etc.. In addition, we will have to be updating the position of the brush within the canvas as we draw. Therefore, in Canvas we don’t put elements into the painting, there is only what we have already painted, while in SVG the elements are placed through the properties of labels <g>, <image>, <text>, etc..

Canvas offers painting operations to make basic geometries: lines, circles, arcs, squares, etc. We will have to do everything with these shapes. But don’t worry, if you think about it, for this graphic we only have to draw circles and lines. It’s easy, isn’t it? It is.

Drawing Nodes

In the next code snippet we may see the steps required to draw a node:

Draw node snippet

Three main things happen in this function: we begin the path of the drawing, set up the painting parameters (color, width, etc..) and the brush movement that generates a shape.

All operations over Canvas are made through its context which we get when selecting HTML element using D3.js.

  • beginPath()
    This functions is equivalent to lie the brush over the canvas so when we move its position a line will appear after it.
  • lineWidth, fillStyle y strokeStyle
    These are the properties that allow us to customize line type. For instance, depending on the node type we would change the color. In this case, we will use a circle filled with solid color to draw a node.
  • moveTo(x,y)
    With this function we locate the brush where we want to start drawing. In this case, we will move it to the node position using X and Y parameters.
  • arc(x,y,radius,originalAngle, finalAngle)
    This is responsible for drawing a circle following a clock wise arc from 0 to 2π. The radius would be set depending on the node type as we have said before.
  • fill()
    This function would fill the shape with the color set in fillStyle parameter described earlier.
  • stroke()
    This will draw a line with shape of the element.

Drawing the Links

Next code snippet shows the steps required to draw a link:

Función para dibujar una línea

This funciton is even easier, a part from the brush set up the difference between previus steps is that we call lineTo instead of arc.

  • lineTo(x,y)
    This function allows us to draw a line with the origin in the current position of the brush and the destination X and Y that are passed to it. That is, we move our position to the source node and make a line to the target node. In this case, we condition the width of the line to a scale to widen it as the number of interactions increases.

Complete Drawing Function

Once we have the functions to draw the nodes and lines we can define the general drawing operation:

Drawing Function

In this function we call the drawing methods for each node and link. In addition, with the functions clearRect, save and restore we clean the canvas and restore it in each iteration, so that the previous operations are not being overlapped.

Remember, until now we haven’t used D3.js, we have only created the logic to draw our elements. With D3 we will obtain the information we are missing yet: the actual position of the elements.

Positioning Elements

All the painting operations make reference to the position of the nodes. However, the initial data structure only contains references between nodes, number of iterations, relationships, and so on, and does not contain anything related to the actual position of the elements.

To get the elements’ position we will use D3, specifically the function forceSimulation (full reference here). This function performs a simulation in which the position of the elements is updated and it modifies the original data structure.

Generating Elements positions through forceSimulation

forceSimulation performs a force simulation from a list of nodes. Initially our nodes have no position and this is the one in charge of establishing the Xand Y values of each node in each iteration. For this reason, in the GIF at the begining of this article it is seen as an explosion of nodes from the center, it is the beginning of the simulation.

This function allows us to provide a list of nodes, declare a series of forces and subscribe to each iteration of the simulation, where we will have the updated positions of the nodes and will allow us to draw. The forces we can declare are:

  • link
    They are union forces between nodes, it allows us to establish a distance restriction between nodes by using D3 function forceLink to which we pass our list of links and the desired distance between nodes.
  • charge
    This force creates a similar effect of the charge of electrons. Among the options offered by D3 we will use forceManyBody with a negative force, which establishes a force field like gravity but in the opposite direction. This is, all nodes will repel each other with a force that decreases with the distance until it desapears completely when the distanceMax is reached.
  • center
    On the other hand, we have established a radial force field to keep all the networks together so that the isolated nodes do not scatter too much.

Function tick is called for each iteration of the simulation and we can take advantage of it to draw our nodes thus obtaining an animation effect.

Why Canvas over SVG

HTML Canvas element introduces a series of complexities as it does not have a reference to the drawn elements, having to perform iterative operations on the data and store the position of the elements in order to update their status. However, the data structure is stored exclusively in the VirtualDOM, unlike SVG, whose data structure is rendered in the DOM and redrawn after any user interaction with the visual elements. This generates an important reduction in painting operations, specially when operating with thousands or hundreds of thousands of visual elements. However, when the surface to be painted is very large, Canvas loses overwhelmingly against the SVG.

Drawing thousands of data points may not be every case or a pre-processing of the data may be done on the server before the drawing pocess. However, sometimes it is unavoidable and in these cases the Canvas element could be a solution when the SVG simply freezes the browser.

Conclusions

If you would like to reproduce the process completely by your own you may check this full project with interactive functionalities using both Canvas and SVG. In addition, I include dataset files so you may explore the data structure or change it with one of yours.

--

--