Drawing with Data: Part 3

Side-by-side network graphs illustrating different algorithmic forces

In Drawing with Data: Part 2 of our drawing with data tutorial series, we used styledMode to transform our area spline range series into underwater trees and created some ethereal sunbeams using numerous line series and an SVG gradient we defined in our chart configuration’s defs object. Now it’s time to add our network graph series, aka the jellypus.

Network Graph Refresher

Network graphs visualize relationships between elements in a network, like devices on a LAN, connections in a transportation system or even more abstract things like ideas in a mind map.

The network graph series consists of nodes and links, or lines, where the nodes represent the elements (devices, train stations, ideas) and the links show the connections between the individual nodes. Individual nodes can also have different sizes and weights (i.e. mass) that help visualize their importance in the network.

The network graph’s layout algorithm then simulates a physical system that uses the forces of attraction, repulsion and gravity to arrange the nodes in a way that best illustrates the nature of the network and the hierarchy of its nodes.

But how does the layout algorithm determine where the nodes should go and how they should behave in relation to each other? A big part of the magic is a concept called the barycenter and its associated forces.

The Barycenter

Barycenter (from the Greek barús for “heavy” and kéntron for “center”) is the common center of mass between two or more bodies in a system and the point at which all bodies in the system are balanced or stable in relation to one another.

In a network graph, the Highcharts layout algorithm finds the barycenter based on the weight or mass of each node and its connection to other nodes and then simulates barycenter forces that pull the nodes toward or push them away from the barycenter. This simulation runs until the nodes reach an equilibrium and settles into their final positions.

You can affect the strength of the barycenter forces by adjusting the layout algorithm’s gravitational constant. The higher the gravitational constant, the faster the nodes will rush toward the barycenter and pile on top of each other. A lower gravitation constant will slow the nodes’ journey to the barycenter, but they eventually get there and glom together in a kind of network graph singularity.

To further control barycenter forces, you can adjust the layout algorithm’s repulsive and attractive forces.

Forces at play

The layout algorithm’s repulsive force setting determines how much individual nodes repel each other. The higher the setting, the farther the nodes will scurry from each other.

The attractive force applies to the links or lines between nodes and affects the strength of the force that pulls connected nodes together. Note the attractive force does not apply to individual nodes. To control the position of individual nodes, change the repulsive force settings.
Along with gravity, repulsion and attraction, another way to affect the behavior of the layout algorithm’s barycenter forces is by adjusting the masses of individual nodes.

More Weight

By default, a network graph’s nodes derive their mass from the radius of their markers. For example, if a node marker has a radius of 10, its mass is 10. But you can override this behavior by assigning an explicit mass through the node’s mass configuration setting, and when you do, the layout algorithm does some interesting stuff.

For example, take a network graph where the gravitational constant and repulsive forces are balanced in such a way that the nodes form a uniform ball.

If you give one of the nodes a mass 10 times that of all the other nodes, you’ll notice that the lighter nodes want to orient themselves around the heavier node. This is because the increased mass of the heavier node has relocated the barycenter of the system closer to the heavier node.

However, the heavier node seems to be surrounded by a “force field” that causes the lighter nodes to keep their distance. This is because the increased mass of the heavier node has amplified its repulsive force.

Drag the big node around. Notice how the little nodes reorient themselves toward the big node’s barycenter while simultaneously being repulsed. This phenomenon creates the illusion of a head that’s controlling the smaller nodes with its movement, just like a jellypus!

Time to make the Jellypus

Before adding the network graph series to my chart configuration, I have to add the module for the network graph series to my HTML file.

<script src="https://code.highcharts.com/modules/networkgraph.js"></script>

Next, I’ll add a network graph series and give it  a className of “jellypus” so I can style it later.

Notice in the layoutAlgorithm object how the attractiveForce and repulsiveForce settings are functions that return values. These values tell the layout algorithm how much to apply these forces between individual nodes.

For our jellypus, we want the repulsiveForce to be half the gravitationalConstant. This will ensure the nodes of the jellypus body follow the head node from a respectable distance while maintaining their clustered configuration.

In addition to these settings, I’ll set the initialPositions  to ‘random’ to give the layout more of an organic feel. I’ll also set enableSimulation to true since this is what causes the network graph to animate.

{
    type: 'networkgraph',
    className: 'jellypus',
    zIndex: 2,
    marker: {
        enabled: true,
        radius: 5,
        symbol: 'circle'
    },
    layoutAlgorithm: {
        enableSimulation: true,
        initialPositions: 'random',
        // Applied only to links, should be 0
        attractiveForce: function() {
            return 0;
        },
        repulsiveForce: function() {
            return 1;
        },
        integration: 'euler',
        gravitationalConstant: 2
    }
}

For the data, I’ll use the data from my original network graph animation I discussed in part 1 of this tutorial series. The data consists of both nodes and links. Here’s the result:

Hello jellypus!

You’ll notice when you hover over the network graph, all the other series dim, creating an annoying flicker effect. To prevent this from happening, I’ll add .highcharts-series-inactive to my CSS file and set its opacity property to 1.

Styling the Jellypus

To make the jellypus look more like an iridescent sea creature, let’s configure another SVG gradient. But instead of defining the gradient in the defs object, we’ll set it up in the HTML file. This method is great if you’re using an online gradient generator that offers cut and paste SVG code.

<svg xmlns="http://www.w3.org/2000/svg" width="600px" height="200px">
    <defs>
        <radialGradient id="rgrad" cx="50%" cy="50%" r="50%">
            <stop offset="0%" style="stop-color:rgb(30, 19, 154);stop-opacity:0.60" />
            <stop offset="100%" style="stop-color:rgb(191, 242, 232);stop-opacity:1.00" />
        </radialGradient>
    </defs>
</svg>

Next, I create styles for the jellypus’ node markers and links in my CSS and use the gradient as a fill color by referencing its ID. (I also gave the links a stroke-dasharray of 3px to make them look more gangly.)

.jellypus .highcharts-point {
    fill-opacity: 1;
    stroke-width:0.5px;
    stroke-dasharray: 3px; /* for the links */
    stroke: white;
}

.jellypus.highcharts-markers .highcharts-point {
    stroke: white;
    fill: url(#rgrad); /* svg gradient */
    fill-opacity: 0.8;
    stroke-dasharray: 0;
}

One more thing…the entire scene looks a bit washed out, so I’ll apply a CSS filter on my container div to bump up the overall saturation.

#container {
    filter: saturate(1.5);
}

Conclusion

In this article, we took a deep dive into Highcharts’ network graph layout algorithm to better understand how node mass, the barycenter and repulsive forces work. We also learned how to add an SVG gradient via HTML.

In the next tutorial, we will teach the jellypus how to swim.