{"id":24889,"date":"2024-04-30T22:01:26","date_gmt":"2024-04-30T22:01:26","guid":{"rendered":"https:\/\/www.highcharts.com\/blog\/?p=24889"},"modified":"2026-01-13T11:56:16","modified_gmt":"2026-01-13T11:56:16","slug":"drawing-with-data-part-3","status":"publish","type":"post","link":"https:\/\/www.highcharts.com\/blog\/tutorials\/drawing-with-data-part-3\/","title":{"rendered":"Drawing with Data: Part 3"},"content":{"rendered":"<p>In <a href=\"https:\/\/www.highcharts.com\/blog\/tutorials\/drawing-with-data-part-2\/\">Drawing with Data: Part 2<\/a> of our drawing with data tutorial series, we used <code>styledMode<\/code> 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\u2019s <code>defs<\/code> object. Now it\u2019s time to add our network graph series, aka the jellypus.<\/p>\n<h2><b>Network Graph Refresher<\/b><\/h2>\n<p>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.<\/p>\n<p>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.<\/p>\n<p>The network graph\u2019s 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.<\/p>\n<p>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.<\/p>\n<h2><b>The Barycenter<\/b><\/h2>\n<p>Barycenter (from the Greek <i>bar\u00fas <\/i>for \u201cheavy\u201d and k\u00e9ntron for \u201ccenter\u201d) 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.<\/p>\n<p>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.<\/p>\n<p>You can affect the strength of the barycenter forces by adjusting the layout algorithm\u2019s 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\u2019 journey to the barycenter, but they eventually get there and glom together in a kind of network graph singularity.<\/p>\n<p>To further control barycenter forces, you can adjust the layout algorithm\u2019s repulsive and attractive forces.<\/p>\n<h2><b>Forces at play<\/b><\/h2>\n<p>The layout algorithm\u2019s repulsive force setting determines how much individual nodes repel each other. The higher the setting, the farther the nodes will scurry from each other.<\/p>\n<p class=\"demo-container\"><iframe style=\"width: 100%;\" title=\"A side-by-side comparison of 2 network graphs, one with a massive node that shifts the location of the barycenter\" src=\"https:\/\/codepen.io\/nasin\/embed\/qBwBzjV?default-tab=result&amp;theme-id=light\" height=\"500\" frameborder=\"no\" scrolling=\"no\" allowfullscreen=\"allowfullscreen\"><span style=\"display: inline-block; width: 0px; overflow: hidden; line-height: 0;\" data-mce-type=\"bookmark\" class=\"mce_SELRES_start\">\ufeff<\/span><span style=\"display: inline-block; width: 0px; overflow: hidden; line-height: 0;\" data-mce-type=\"bookmark\" class=\"mce_SELRES_start\">\ufeff<\/span><span style=\"display: inline-block; width: 0px; overflow: hidden; line-height: 0;\" data-mce-type=\"bookmark\" class=\"mce_SELRES_start\">\ufeff<\/span><br \/>\nSee the Pen <a href=\"https:\/\/codepen.io\/nasin\/pen\/qBwBzjV\"><br \/>\nNetwork Graph: Gravitational Force<\/a> by Nancy Dillon (<a href=\"https:\/\/codepen.io\/nasin\">@nasin<\/a>)<br \/>\non <a href=\"https:\/\/codepen.io\">CodePen<\/a>.<br \/>\n<\/iframe><\/p>\n<p>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.<br \/>\nAlong with gravity, repulsion and attraction, another way to affect the behavior of the layout algorithm\u2019s barycenter forces is by adjusting the masses of individual nodes.<\/p>\n<h2><b>More Weight<\/b><\/h2>\n<p>By default, a network graph\u2019s 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\u2019s <code>mass<\/code> configuration setting, and when you do, the layout algorithm does some interesting stuff.<\/p>\n<p>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.<\/p>\n<p>If you give one of the nodes a mass 10 times that of all the other nodes, you\u2019ll 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.<\/p>\n<p>However, the heavier node seems to be surrounded by a \u201cforce field\u201d that causes the lighter nodes to keep their distance. This is because the increased mass of the heavier node has amplified its repulsive force.<\/p>\n<p class=\"demo-container\"><iframe style=\"width: 100%;\" title=\"A side-by-side comparison of 2 network graphs with different layout alogirthm forces\" src=\"https:\/\/codepen.io\/nasin\/embed\/ZEZEdap?default-tab=result&amp;theme-id=light\" height=\"500\" frameborder=\"no\" scrolling=\"no\" allowfullscreen=\"allowfullscreen\"><span style=\"display: inline-block; width: 0px; overflow: hidden; line-height: 0;\" data-mce-type=\"bookmark\" class=\"mce_SELRES_start\">\ufeff<\/span><span style=\"display: inline-block; width: 0px; overflow: hidden; line-height: 0;\" data-mce-type=\"bookmark\" class=\"mce_SELRES_start\">\ufeff<\/span><br \/>\nSee the Pen <a href=\"https:\/\/codepen.io\/nasin\/pen\/qBwBzjV\"><br \/>\nNetwork Graph: Gravitational Force<\/a> by Nancy Dillon (<a href=\"https:\/\/codepen.io\/nasin\">@nasin<\/a>)<br \/>\non <a href=\"https:\/\/codepen.io\">CodePen<\/a>.<br \/>\n<\/iframe><\/p>\n<p>Drag the big node around. Notice how the little nodes reorient themselves toward the big node\u2019s barycenter while simultaneously being repulsed. This phenomenon creates the illusion of a head that\u2019s controlling the smaller nodes with its movement, just like a jellypus!<\/p>\n<h2><b>Time to make the Jellypus<\/b><\/h2>\n<p>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.<\/p>\n<pre>&lt;script src=\"https:\/\/code.highcharts.com\/modules\/networkgraph.js\"&gt;&lt;\/script&gt;<\/pre>\n<p>Next, I\u2019ll add a network graph series and give it\u00a0 a className of \u201cjellypus\u201d so I can style it later.<\/p>\n<p>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.<\/p>\n<p>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.<\/p>\n<p>In addition to these settings, I\u2019ll set the initialPositions\u00a0 to \u2018random\u2019 to give the layout more of an organic feel. I\u2019ll also set enableSimulation to true since this is what causes the network graph to animate.<\/p>\n<pre>{\r\n    type: 'networkgraph',\r\n    className: 'jellypus',\r\n    zIndex: 2,\r\n    marker: {\r\n        enabled: true,\r\n        radius: 5,\r\n        symbol: 'circle'\r\n    },\r\n    layoutAlgorithm: {\r\n        enableSimulation: true,\r\n        initialPositions: 'random',\r\n        \/\/ Applied only to links, should be 0\r\n        attractiveForce: function() {\r\n            return 0;\r\n        },\r\n        repulsiveForce: function() {\r\n            return 1;\r\n        },\r\n        integration: 'euler',\r\n        gravitationalConstant: 2\r\n    }\r\n}\r\n<\/pre>\n<p>For the data, I\u2019ll 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&#8217;s the result:<\/p>\n<p class=\"demo-container\"><iframe style=\"width: 100%;\" title=\"First itereation of the network graph, aka the jellypus\" src=\"https:\/\/codepen.io\/nasin\/embed\/qBwBzRP?default-tab=result&amp;theme-id=light\" height=\"700\" frameborder=\"no\" scrolling=\"no\" allowfullscreen=\"allowfullscreen\"><br \/>\nSee the Pen <a href=\"https:\/\/codepen.io\/nasin\/pen\/qBwBzRP\"><br \/>\nJellypus 13<\/a> by Nancy Dillon (<a href=\"https:\/\/codepen.io\/nasin\">@nasin<\/a>)<br \/>\non <a href=\"https:\/\/codepen.io\">CodePen<\/a>.<br \/>\n<\/iframe><\/p>\n<p>Hello jellypus!<\/p>\n<p>You\u2019ll notice when you hover over the network graph, all the other series dim, creating an annoying flicker effect. To prevent this from happening, I\u2019ll add .highcharts-series-inactive to my CSS file and set its opacity property to 1.<\/p>\n<h2><strong>Styling the Jellypus<\/strong><\/h2>\n<p>To make the jellypus look more like an iridescent sea creature, let\u2019s configure another SVG gradient. But instead of defining the gradient in the defs object, we\u2019ll set it up in the HTML file. This method is great if you\u2019re using an online gradient generator that offers cut and paste SVG code.<\/p>\n<pre>&lt;svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"600px\" height=\"200px\"&gt;\r\n    &lt;defs&gt;\r\n        &lt;radialGradient id=\"rgrad\" cx=\"50%\" cy=\"50%\" r=\"50%\"&gt;\r\n            &lt;stop offset=\"0%\" style=\"stop-color:rgb(30, 19, 154);stop-opacity:0.60\" \/&gt;\r\n            &lt;stop offset=\"100%\" style=\"stop-color:rgb(191, 242, 232);stop-opacity:1.00\" \/&gt;\r\n        &lt;\/radialGradient&gt;\r\n    &lt;\/defs&gt;\r\n&lt;\/svg&gt;\r\n<\/pre>\n<p>Next, I create styles for the jellypus\u2019 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.)<\/p>\n<pre>.jellypus .highcharts-point {\r\n    fill-opacity: 1;\r\n    stroke-width:0.5px;\r\n    stroke-dasharray: 3px; \/* for the links *\/\r\n    stroke: white;\r\n}\r\n\r\n.jellypus.highcharts-markers .highcharts-point {\r\n    stroke: white;\r\n    fill: url(#rgrad); \/* svg gradient *\/\r\n    fill-opacity: 0.8;\r\n    stroke-dasharray: 0;\r\n}\r\n<\/pre>\n<p>One more thing\u2026the entire scene looks a bit washed out, so I\u2019ll apply a CSS filter on my container div to bump up the overall saturation.<\/p>\n<pre>#container {\r\n    filter: saturate(1.5);\r\n}\r\n<\/pre>\n<p class=\"demo-container\"><iframe style=\"width: 100%;\" title=\"Styled Jellypus inside its underwater forest\" src=\"https:\/\/codepen.io\/nasin\/embed\/LYvYwZM?default-tab=result&amp;theme-id=light\" height=\"700\" frameborder=\"no\" scrolling=\"no\" allowfullscreen=\"allowfullscreen\"><span style=\"display: inline-block; width: 0px; overflow: hidden; line-height: 0;\" data-mce-type=\"bookmark\" class=\"mce_SELRES_start\">\ufeff<\/span><br \/>\nSee the Pen <a href=\"https:\/\/codepen.io\/nasin\/pen\/LYvYwZM\"><br \/>\nJellypus 14<\/a> by Nancy Dillon (<a href=\"https:\/\/codepen.io\/nasin\">@nasin<\/a>)<br \/>\non <a href=\"https:\/\/codepen.io\">CodePen<\/a>.<br \/>\n<\/iframe><\/p>\n<h2><b>Conclusion<\/b><\/h2>\n<p>In this article, we took a deep dive into Highcharts\u2019 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.<\/p>\n<p>In the next tutorial, we will teach the jellypus how to swim.<\/p>\n<h2><strong>Related posts<\/strong><\/h2>\n<ul>\n<li style=\"margin-left: 20px; margin-bottom: 20px;\"><a href=\"https:\/\/www.highcharts.com\/blog\/inspirations\/javascript-library-by-highcharts\/\">JavaScript library by Highcharts<\/a><\/li>\n<li style=\"margin-left: 20px; margin-bottom: 20px;\"><a href=\"https:\/\/www.highcharts.com\/blog\/inspirations\/javascript-map-library-by-highcharts\/\">JavaScript map library by Highcharts<\/a><\/li>\n<li style=\"margin-left: 20px; margin-bottom: 20px;\"><a href=\"https:\/\/www.highcharts.com\/blog\/inspirations\/map-library-by-highcharts\/\">Map library by Highcharts<\/a><\/li>\n<li style=\"margin-left: 20px; margin-bottom: 20px;\"><a href=\"https:\/\/www.highcharts.com\/blog\/inspirations\/data-visualization-library-by-highcharts\/\">Data visualization library by Highcharts<\/a><\/li>\n<li style=\"margin-left: 20px; margin-bottom: 20px;\"><a href=\"https:\/\/www.highcharts.com\/blog\/inspirations\/javascript-graph-visualization-library-by-highcharts\/\">JavaScript graph visualization library by Highcharts<\/a><\/li>\n<li style=\"margin-left: 20px; margin-bottom: 20px;\"><a href=\"https:\/\/www.highcharts.com\/blog\/inspirations\/data-visualization-framework-by-highcharts\/\">Data visualization framework by Highcharts<\/a><\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Learn how to draw with data<\/p>\n","protected":false},"author":250,"featured_media":24906,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"meta_title":"","meta_description":"","hc_selected_options":[],"footnotes":""},"categories":[210],"tags":[1063,1094],"coauthors":[786],"class_list":["post-24889","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tutorials","tag-data-visualization","tag-highcharts-core"],"_links":{"self":[{"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/posts\/24889","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/users\/250"}],"replies":[{"embeddable":true,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/comments?post=24889"}],"version-history":[{"count":4,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/posts\/24889\/revisions"}],"predecessor-version":[{"id":26029,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/posts\/24889\/revisions\/26029"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/media\/24906"}],"wp:attachment":[{"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/media?parent=24889"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/categories?post=24889"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/tags?post=24889"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/coauthors?post=24889"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}