Network Chart¶
A network graph is designed to allow the visualization of relational information. ChartFactor has two types of network charts: one that comes in the standard library called Graph and one more flexible and sofisticated called Network that comes in its own package.
Comparison between the Graph Chart and the Network Chart¶
Feature | Graph Chart | Network Chart |
---|---|---|
Simple Graph Visualization | Yes | Yes |
Directed Graph | No | Yes |
Nodes Custom Styles | No | Yes |
Edges Custom Styles | No | Yes |
Static layout (specific x and y) | Yes | Yes |
Force Layout | Yes | Yes |
Circle Layout | No | Yes |
Grid Layout | No | Yes |
CoSE Layout | No | Yes |
Dagre Layout | No | Yes |
Spread Layout | No | Yes |
Concentric Layout | No | Yes |
Custom event listeners | No | Yes |
Creating a Basic Graph Chart with local data¶
In this example we will create a Graph chart and feed it with static data. Note that only edge information is required to render the graph.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | var data = { 'nodes': [ { 'color': '#4f19c7', 'label': 'jquery', 'y': -404.26147, 'x': -739.36383, 'id': 'jquery', 'value': 4.7252817 }, ... { 'color': '#c71969', 'label': 'backbone', 'y': -862.7517, 'x': -134.2215, 'id': 'backbone', 'value': 6.1554675 }], 'edges': [ { 'source': 'jquery', 'target': 'jsdom', 'value': 1 }, ... { 'source': 'jquery', 'target': 'xmlhttprequest', 'value': 1 }] } var edgesInfo = { 'width': 0.6, 'curveness': 0.2, 'opacity': 0.7 }; var nodesInfo = { 'min': 0.1, 'max': 60 }; var chart = cf .create() .graph('Graph') .element('chart') .set('layout', 'force') .set('loadIndicator', 'graph-loading') .set('layout', 'force') .set('edgesInfo', edgesInfo) .set('nodesInfo', nodesInfo) .data(data); chart.execute(); |
The previous code will render the Graph Chart below:
Creating a Graph chart with provider data¶
It is recommended before reading this section, to read the documentation on Multiple Queries (click here). To support graph charts, we make use of Chartfactor's multiple query capabilities as we show below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | var edgesInfo = { width: 0.6, curveness: 0.2, opacity: 0.7 }; var nodesInfo = { min: 0.1, max: 60 }; let metric = cf.Metric('size', 'sum'); let metrics = [metric, cf.Metric('count')]; let grp = cf.Attribute('id').limit(1200).sort('asc', metrics[0]); let nodesHandler = function (result) { result.data.nodes.forEach(n => { n.id = n.group[0]; n.value = n.current.metrics[metric._name][metric._func]; }); result.keep = true; return result; }; let edgesHandler = function (result) { result.data.edges.forEach(e => { e.source = e.group[0]; e.target = e.group[1]; e.value = e.current.metrics[metric._name][metric._func]; }); result.keep = true; return result; }; let myChart = cf .create('nodes') .provider('Elasticsearch') .source('npmdepgraph-nodes') .processWith(nodesHandler.bind(this)) .groupby(grp) .metrics(...metrics) .create('edges') .provider('Elasticsearch') .source('npmdepgraph-links') .processWith(edgesHandler.bind(this)) .rows('source', 'target') .limit(500) // 1000 by default .metrics(...metrics) .graph('Graph') .set('layout', 'force') .set('edgesInfo', edgesInfo) .set('nodesInfo', nodesInfo) .element('chart') .execute(); |
let grp = cf.Attribute('id').limit(1200).sort('asc', metrics[0]);
.
The edges query obtains the relation betwen nodes. It is using a pivot-style query grouping by source and target, as specified in the rows() function above. Here is the code used: .rows('source', 'target')
. The default limit for a rows() query is 1000. You can override this limit by using the limit() function. In the example above, the limit is set to 500.
Note
It is mandatory that the query to retrieve the node is called "nodes" and the query to retrieve edges is called "edges". If you want to see an example of this code in action click here.
Creating a Network Chart with static data¶
In this example we will create a Network chart and feed it with static data. Note that only edge information is required to render the network. Node information is optional.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | var data = { Edges: [ /* is mandatory call it "Edges" and each object have to have a source and a target. */ {source: 'a', target: 'b'}, {source: 'a', target: 'c'}, {source: 'a', target: 'd'}, {source: 'c', target: 'e'}, {source: 'c', target: 'f'} ], Nodes: [] /* is mandatory call it "Nodes" and if no extra information is needed for the node (size, color, image background, etc) the array could by empty */ } cf.create() .graph('Network') // Setting the visual type .element('chart') // Setting the id of the DOM element in where should be render the Network .set('layout', {name: 'dagre'}) // Setting the Network layout (see layout section) .data(data) // Injecting static data .execute(); // Executing the visual |
The previous code will render the Network Chart below:
Adding custom styles¶
The next example uses the same data as the previous example. In addition, it defines a style array to be assigned to the network chart. Each object in the style array must have a selector attribute (node, edge, or id) and a style attribute containing css-like styles to be applied to network chart.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | var style = [ {selector: 'node', style: { 'label': 'data(id)', 'background-color': '#4292B3' } }, {selector: 'edge', style: { 'line-color': '#4292B3' } } ]; cf.create() .graph('Network') .element('chart') .set('layout', {name: 'dagre'}) .set('style', style) // Setting the style to the Network chart. .data(data) .execute(); |
The previous code will render the Network Chart below:
Using the data to style nodes and edges¶
We can use properties of the data to specify styles of nodes and edges. Use the syntax 'data(propertyName)' to assign the value of that property to a style object. Let's modify our previous example to use this feature:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | var style = [ {selector: 'node', style: { 'label': 'data(id)', // Using the id of the node as label 'background-color': 'data(color)', /* Using the color property of the data object like the background color of the node */ 'height': 'data(size)', // Using size for height 'width': 'data(size)' // Using size for width } }, {selector: 'edge', style: { 'line-color': 'data(color)', // Using the property color of the edge data object as line-color 'curve-style': 'unbundled-bezier' // This make the line curved } } ]; var data = { /* Now we will modify a little bit the edges data objects to store a color for each edge */ Edges: [ {source: 'a', target: 'b', color: 'red'}, {source: 'a', target: 'c', color: 'red'}, {source: 'a', target: 'd', color: 'red'}, {source: 'c', target: 'e', color: 'green'}, {source: 'c', target: 'f', color: 'green'} ], /* And modify the nodes data objects to store a color and size for each node */ Nodes: [ {id: 'a', size: 10, color: 'blue'}, {id: 'b', size: 20, color: 'yellow'}, {id: 'c', size: 15, color: 'black'}, {id: 'd', size: 30, color: 'orange'}, {id: 'e', size: 40, color: 'purple'}, {id: 'f', size: 50, color: 'gray'} ] }; cf.create() .graph('Network') .element('chart') .set('layout', {name: 'dagre'}) .set('style', style) .data(data) .execute(); |
The previous code will render the Network Chart below:
Setting arrows to edges¶
To set arrows on the edges we can modify the properties \<position>-arrow-shape, \<position>-arrow-color, \<position>-arrow-fill and \<position>-arrow-scale.
Valid values for \<position> are: source : Pointing towards the source node, at the end of the edge. mid-source : Pointing towards the source node, at the middle of the edge. target : Pointing towards the target node, at the end of the edge. mid-target: Pointing towards the target node, at the middle of the edge.
Valid shapes are: triangle triangle-tee triangle-cross triangle-backcurve vee tee square circle diamond none
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | var style = [ {selector: 'node', style: { 'label': 'data(id)', 'background-color': 'data(color)', 'height': 'data(size)', 'width': 'data(size)' } }, {selector: 'edge', style: { 'line-color': 'data(color)', 'curve-style': 'unbundled-bezier', 'target-arrow-shape': 'triangle', 'target-arrow-color': 'black' } } ]; |
Images in Nodes¶
To put an image in a node you must modify the properties 'background-image' and 'background-fit' in the style of the node as well as specify the path of the image in the data of the node or create a custom selector using the id of the node to specify the path of the background image as shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | var style = [ {selector: 'node', style: { 'label': 'data(label)', 'background-color': 'data(color)', 'height': 'data(size)', 'width': 'data(size)', 'background-image': 'data(backgroundImage)', 'background-fit': 'cover', 'border-color': '#000', 'border-width': 1, 'border-opacity': 0.5 } }, {selector: '#a', style: { 'background-image': 'https://farm8.staticflickr.com/7272/7633179468_3e19e45a0c_b.jpg' } }, {selector: 'edge', style: { 'line-color': 'black', 'target-arrow-shape': 'triangle', 'target-arrow-color': 'black' } } ]; var data = { Edges: [ {source: 'a', target: 'b', color: 'red'}, {source: 'a', target: 'c', color: 'red'} ], Nodes: [{ id: 'a', label: 'bird', size: 60, color: 'blue' }, { id: 'b', size: 60, label: 'cat', color: 'yellow', backgroundImage: 'https://farm2.staticflickr.com/1261/1413379559_412a540d29_b.jpg' }, { id: 'c', size: 60, color: 'black', label: 'rose', backgroundImage: 'https://farm6.staticflickr.com/5109/5817854163_eaccd688f5_b.jpg' } ] }; |
Custom Tooltips¶
Using the same data from the previous example we can develop a small example of how to implement custom tooltips that show the size in addition to the node identifier.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | var tooltipBuilder = function (info) { var result = ''; switch (info.type) { case 'node' : { result = `<b>Node:</b> ${info.data.id}<br />`; result += `<b>Size:</b> ${info.data.size}<br />`; break; } case 'edge' : { result = `<b>Edge:</b> ${info.data.source} > ${info.data.target}<br />`; } } return result; }; cf.create() .graph('Network') .element('chart') .set('layout', {name: 'dagre'}) .set('style', style) .set('tooltipBuilder', tooltipBuilder) .data(data) .execute(); |
The previous code will render the Tooltips below:
Available layouts¶
Layout Name | Node Position | Config. Variables | Best Scenarios |
---|---|---|---|
preset | preset on data | none | when you have the positions that each node will have |
random | random | none | if is a small graph |
force | calculated | Edge length, Node spacing | good to detect association and orphan nodes |
circle | on the perimeter of a circle | none | if is a small graph |
grid | on a grid | spacingFactor | a graph without many connections |
cose | calculated | idealEdgeLength, nodeOverlap, nodeRepulsion, edgeElasticity, gravity, etc. | a graph without many connections |
dagre | calculated | none | non-cyclic graph |
spread | calculated | minDist | a graph without many connections |
concentric | calculated | concentric (func), levelWidth (func) | graphs in which few nodes have relationships with many nodes |
In this demonstration you can try each of the layouts. (click here)
Registering a click listener¶
Setting a listener to an event in a graph is done in a similar way as to any other chartfactor visualization. Here is an example:
1 2 3 4 5 6 7 8 9 10 11 12 | var clickListener = function (event) { alert(event.data.id); }; var chart = cf.create() .graph('Network') .element('chart') .set('layout', {name: 'dagre'}) .set('style', style) .data(data); chart.on('click', clickListener); chart.execute(); |
Creating a Network chart with provider data¶
It is recommended before reading this section, to read the documentation on Multiple Queries (click here). To support network charts, we make use of Chartfactor's multiple query capabilities as shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | var nodesHandler = function (result) { // The callback to be used when executing the query to get the nodes // In this example we set custom color for all nodes. result.data.Nodes.forEach(x => { x.color = '#4292B3'; }); // The data should be persistent (important) result.keep = true; return result; }; var edgesHandler = function (result) { // The callback to be used when executing the query to get the edges // In this example we set a filter for get only the nodes info that is needed, // and a customs color for the edge line. let nodes = []; result.data.Edges.forEach(x => { if (nodes.indexOf(x.source) === -1) nodes.push(x.source); if (nodes.indexOf(x.target) === -1) nodes.push(x.target); x.color = '#4292B3'; }); result.filters = (nodes && nodes.length > 0) ? [cf.Filter('id').values(nodes)] : []; return result; }; cf.create('Edges') // Query to retrieve the edges info. .provider('ProviderName') .source('edgesSourceName') .fields('source', 'target') .limit(1000) // 100 by default .processWith(edgesHandler.bind(this)) .create('Nodes') // Query to retrieve the nodes info. .provider('ProviderName') // Could be a different provider. .source('nodesSourceName') .fields('id', 'value', 'y', 'x', 'label', 'color') // All this attributes will be added to the node data. .limit(1000) // 100 by default .processWith(nodesHandler.bind(this)) .element('chart') .graph('Network') |
The edges query obtains the relation betwen nodes. The example above is using a raw data query to retrieve source and target columns, as specified in the fields() function. Here is the code used: .fields('source', 'target')
. The default limit for raw data queries is 100. You can override this limit by using the limit() function. In the example above, the limit is set to 1000.
The nodes query in the example above is also using a raw data query to retrieve id, value, y, x, label, and color fields. Same as with the edges query, the default limit for raw data queries is 100. You can override this limit by using the limit() function. In the example above, the limit is set to 1000.
Note
It is mandatory that the query to retrieve the node is called "Nodes" and the query to retrieve edges is called "Edges". If you want to see an example of this code in action click here.
Inject urls of dependency libraries:¶
In order to not increase the size of the library with the dependencies of an uncommon visualization, by default, ChartFactor does not load the dependencies of the network graph. These are loaded when the graph is instantiated and are left in cache. These are loaded from the official CDN but if you do not have extranet access or for reasons of speed you want to serve them on your own servers, you can inject the addresses of these libraries and they will be loaded in the following way. Example:
1 2 3 4 | <script> window.colaUrl = 'https://chartfactor.com/wp-content/lib/cola.min.js'; </script> <script src="../../lib/CFToolkit.min.js"></script> |
Note
It is recommended to define global variables to inject the dependencies before loading the library. The following table shows all the dependencies that can be injected
Vairable | Default Url |
---|---|
colaUrl | http://marvl.infotech.monash.edu/webcola/cola.min.js |
cyColaUrl | https://cdn.jsdelivr.net/npm/cytoscape-cola@2.1.0/cytoscape-cola.min.js |
cytoscapeUrl | https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.2.8/cytoscape.min.js |
dagreUrl | https://cdn.rawgit.com/cpettitt/dagre/v0.7.4/dist/dagre.min.js |
cyDagreUrl | https://cdn.rawgit.com/cytoscape/cytoscape.js-dagre/1.5.0/cytoscape-dagre.js |
weaverUrl | https://cdn.rawgit.com/maxkfranz/weaver/v1.2.0/dist/weaver.min.js |
cySpreadUrl | https://cdn.rawgit.com/cytoscape/cytoscape.js-spread/1.3.1/cytoscape-spread.js |
cyCanvasUrl | https://unpkg.com/cytoscape-canvas/dist/cytoscape-canvas.js |
Or you can inject a bundle with all dependencies minified:
Vairable | Default Url |
---|---|
graphBundleUrl | https://chartfactor.com/wp-content/lib/cf_graph_bundle.min.js |