Displaying large files in gojs (1000s of nodes and links)

Hi, we are using GOJS for our requirement of UI visualization. We have a use case where there are close to 10 thousand nodes and 10 thousand links which we need to display. Once we load the data to GOJS library and load it on the browser, the overall experience becomes slow. Precisely, while expanding sub graphs (we have nested graph structure) we see it takes a lot of time to expand and minimize specifically.

Could you please help us with any pointers to solve this?

Have you read GoJS Performance Considerations -- Northwoods Software ?

Please tell us and show us more. What is your app’s Diagram.layout? How complicated are the Nodes? What are your Link template settings?

I am having the same issue, @Bharath64k did you get the solution. My configuration is like this

{
contentAlignment: go.Spot.Center, // align document to the center of the viewport
initialDocumentSpot: go.Spot.Center,
initialViewportSpot: go.Spot.Center,
initialContentAlignment: go.Spot.Center,
initialAutoScale: go.Diagram.UniformToFit, // an initial automatic zoom-to-fit
layout: $(go.ForceDirectedLayout, {
defaultElectricalCharge: 100,
arrangesToOrigin: true,
defaultSpringLength: 100,
maxIterations: 1000
}),
“animationManager.isEnabled”: true,
“animationManager.initialAnimationStyle”: go.AnimationManager.AnimateLocations,
“toolManager.hoverDelay”: 10, // how quickly tooltips are shown
“commandHandler.copiesTree”: true, // for the copy command
“commandHandler.deletesTree”: true, // for the delete command
“draggingTool.dragsTree”: true, // dragging for both move and copy
“undoManager.isEnabled”: true,
“dragSelectingTool.isEnabled”: true,
“animationManager.isEnabled”: true,
// scrollMode: go.Diagram.InfiniteScroll,
allowZoom: true,
allowHorizontalScroll: true,
allowVerticalScroll: true,
hasHorizontalScrollbar: true,
hasVerticalScrollbar: true,
padding: 0,
ObjectDoubleClicked: this.nodeDblClickHandler
}

@walter , Any help would be great. Thanks.

Thanks for showing us your Diagram.layout. Try a smaller value for maxIterations.

How many nodes and how many links do you have?

Thanks for the reply Walter. Approx 3500 nodes and links now, will be more depends on data.

I tried using 10 for maxIterations, seems a bit faster than earlier but the nodes are overlapped.

What structure will your graphs have? Are they mostly tree-like?
If so, maybe you should use Radial & ForceDirected Layout.


This is an example of how my graph looks with just 50 nodes

@walter I am using “addNodeData” method of “diagram.model” each time in the loop. May be that causes the issue.

My model definition is like this

modelData: {
class: “go.GraphLinksModel”,
linkLabelKeysProperty: “labelKeys”,
nodeDataArray: [],
linkDataArray: []
}

and creating node like

diagram.model.addNodeData(nodeObject)

Use the RadialForceDirectedLayout that I just mentioned.

If those calls to Model.addNodeData happened within a loop, that should be OK.

1 Like

@walter, Page becomes unresponsive. The time to render 1000 nodes and links is like 3min. Way much higher than expected, expecting to be in seconds atleast.

In the example the maxIterations are like 250, if I reduced it to 0, it is taking only 8sec but the graph looks pathetic. Any solution?

I’ll try some experiments. Could you share that model with us? You can strip out any proprietary or personal data properties.

Also, you could try fiddling with the parameters of the layouts in the RadialForceDirectedLayout implementation. For example, try increasing the RadialLayout.layerThickness.

RadialLayout, like CircularLayout, doesn’t work well when there are a lot of nodes “in the same circle”. The nodes really need to be spread out more, not put into a circle. And that’s what happens when there are cases where a node has a thousand links/nodes connecting with it.

So I tried just using the standard ForceDirectedLayout. I found that with 10000 nodes and 9999 links in a broad tree structure (i.e. some nodes having a lot of children), that initial load times were between 15 seconds and 45 seconds, depending on the nature of the graph.

Here’s my code:

<!DOCTYPE html>
<html>
<head>
  <title>Radial & ForceDirected Layout</title>
  <!-- Copyright 1998-2022 by Northwoods Software Corporation. -->
  <meta name="description" content="a ForceDirectedLayout that starts the nodes in a radial-layout fashion">
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
  <span id="myStatus"></span>

  <script src="../release/go.js"></script>
  <script id="code">
const $ = go.GraphObject.make;
const Before = Date.now();

myDiagram =
  new go.Diagram("myDiagramDiv",
    {
      initialAutoScale: go.Diagram.Uniform,
      layout: $(go.ForceDirectedLayout,
        { defaultSpringLength: 1600, defaultSpringStiffness: 0.01 }),
      "InitialLayoutCompleted": e => {  // on the page show the total time in seconds
        document.getElementById("myStatus").textContent = ((Date.now() - Before)/1000).toString();
      }
    });

myDiagram.nodeTemplate =
  $(go.Node, "Spot",
    { width: 50, height: 50 },
    $(go.Shape, "Circle",
      { fill: "gray", portId: "" },
      new go.Binding("fill")),
    $(go.TextBlock,
      new go.Binding("text", "key"))
  );

// (taken from samples/tLayout.html)
// Creates a random number (between MIN and MAX) of randomly colored nodes.
function generateNodeData(minNodes, maxNodes, minChil, maxChil) {
  var nodeArray = [];
  if (minNodes === undefined || isNaN(minNodes) || minNodes < 1) minNodes = 1;
  if (maxNodes === undefined || isNaN(maxNodes) || maxNodes < minNodes) maxNodes = minNodes;

  // Create a bunch of node data
  var numNodes = Math.floor(Math.random() * (maxNodes - minNodes + 1)) + minNodes;
  for (var i = 0; i < numNodes; i++) {
    nodeArray.push({
      key: i,  // the unique identifier
      // "parent" is set by code below that assigns children
      text: i.toString(),  // some text to be shown by the node template
      fill: go.Brush.randomColor(),  // a color to be shown by the node template
    });
  }

  // Randomize the node data
  for (i = 0; i < nodeArray.length; i++) {
    var swap = Math.floor(Math.random() * nodeArray.length);
    var temp = nodeArray[swap];
    nodeArray[swap] = nodeArray[i];
    nodeArray[i] = temp;
  }

  // Takes the random collection of node data and creates a random tree with them.
  // Respects the minimum and maximum number of links from each node.
  // The minimum can be disregarded if we run out of nodes to link to.
  if (nodeArray.length > 1) {
    if (minChil === undefined || isNaN(minChil) || minChil < 0) minChil = 0;
    if (maxChil === undefined || isNaN(maxChil) || maxChil < minChil) maxChil = minChil;

    // keep the Set of node data that do not yet have a parent
    var available = new go.Set();
    available.addAll(nodeArray);
    for (var i = 0; i < nodeArray.length; i++) {
      var parent = nodeArray[i];
      available.remove(parent);

      // assign some number of node data as children of this parent node data
      var children = Math.floor(Math.random() * (maxChil - minChil + 1)) + minChil;
      for (var j = 0; j < children; j++) {
        var child = available.first();
        if (child === null) break;  // oops, ran out already
        available.remove(child);
        // have the child node data refer to the parent node data by its key
        child.parent = parent.key;
      }
      if (available.count === 0) break;  // nothing left?
    }
  }
  return nodeArray;
}

myDiagram.model = new go.TreeModel(generateNodeData(10000, 10000, 1, 1000));
  </script>
</body>
</html>

I have set “maxIterations” to 1000 and “layerThickness” to 1000, as I have 1000 nodes (just a random guess) I got the graph rendered in 50sec which is not bad but within 50 sec my browsers becomes unresponsive few times.

You have seen the code I used to generate random trees. Could you please share with us either the models or the way of generating the models that you are using?

let me know the email to send the code?

gojs at our domain, nwoods.com
For personal reasons I might not be able to investigate soon.