Home > Mobile >  Is there a d3 network graph that includes nodes that don't have links?
Is there a d3 network graph that includes nodes that don't have links?

Time:01-05

I'm creating a network graph in which I want to display both nodes that have links and those that don't. I can create the JSON for this, but don't know how to get .force (or something else) to display the "unlinked" nodes near the linked nodes. Right now the "orphan" nodes just don't show up.

At the moment, I'm trying to build off of d3-graph-gallery.com/graph/network_basic.html, but I'd be happy to be pointed to a more appropriate example for my use case.

CodePudding user response:

There's absolutely no problem in having a node without links: D3 doesn't need that for running the simulation (but the other way around, a link without a node, will throw an error). Here I'm simply copying the code you linked and adding a node...

data.nodes.push({id:11, name:"Z"});

... which is the one in red, this is the demo:

// set the dimensions and margins of the graph
const margin = {
    top: 10,
    right: 30,
    bottom: 30,
    left: 40
  },
  width = 400 - margin.left - margin.right,
  height = 400 - margin.top - margin.bottom;

// append the svg object to the body of the page
const svg = d3.select("#my_dataviz")
  .append("svg")
  .attr("width", width   margin.left   margin.right)
  .attr("height", height   margin.top   margin.bottom)
  .append("g")
  .attr("transform",
    `translate(${margin.left}, ${margin.top})`);

const data = {
  "nodes": [{
      "id": 1,
      "name": "A"
    },
    {
      "id": 2,
      "name": "B"
    },
    {
      "id": 3,
      "name": "C"
    },
    {
      "id": 4,
      "name": "D"
    },
    {
      "id": 5,
      "name": "E"
    },
    {
      "id": 6,
      "name": "F"
    },
    {
      "id": 7,
      "name": "G"
    },
    {
      "id": 8,
      "name": "H"
    },
    {
      "id": 9,
      "name": "I"
    },
    {
      "id": 10,
      "name": "J"
    }
  ],
  "links": [

    {
      "source": 1,
      "target": 2
    },
    {
      "source": 1,
      "target": 5
    },
    {
      "source": 1,
      "target": 6
    },

    {
      "source": 2,
      "target": 3
    },
    {
      "source": 2,
      "target": 7
    },

    {
      "source": 3,
      "target": 4
    },
    {
      "source": 8,
      "target": 3
    },
    {
      "source": 4,
      "target": 5
    },

    {
      "source": 4,
      "target": 9
    },
    {
      "source": 5,
      "target": 10
    }
  ]
}

data.nodes.push({
  id: 11,
  name: "Z"
});

// Initialize the links
const link = svg
  .selectAll("line")
  .data(data.links)
  .join("line")
  .style("stroke", "#aaa")

// Initialize the nodes
const node = svg
  .selectAll("circle")
  .data(data.nodes)
  .join("circle")
  .attr("r", 20)
  .style("fill", d => d.id === 11 ? "red" : "#69b3a2")

// Let's list the force we wanna apply on the network
const simulation = d3.forceSimulation(data.nodes) // Force algorithm is applied to data.nodes
  .force("link", d3.forceLink() // This force provides links between nodes
    .id(function(d) {
      return d.id;
    }) // This provide  the id of a node
    .links(data.links) // and this the list of links
  )
  .force("charge", d3.forceManyBody().strength(-400)) // This adds repulsion between nodes. Play with the -400 for the repulsion strength
  .force("center", d3.forceCenter(width / 2, height / 2)) // This force attracts nodes to the center of the svg area
  .on("tick", ticked);

// This function is run at each iteration of the force algorithm, updating the nodes position.
function ticked() {
  link
    .attr("x1", function(d) {
      return d.source.x;
    })
    .attr("y1", function(d) {
      return d.source.y;
    })
    .attr("x2", function(d) {
      return d.target.x;
    })
    .attr("y2", function(d) {
      return d.target.y;
    });

  node
    .attr("cx", function(d) {
      return Math.max(0, Math.min(width, d.x   6));
    })
    .attr("cy", function(d) {
      return Math.max(0, Math.min(height, d.y - 6));
    });
}
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v6.js"></script>

<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>

The real problem is that, without any link, that node will be pushed far away from the SVG boundaries given that simulation parameters. For showing it here I'm just creating a boundary on the ticked function, but a proper solution is more complicated.

  • Related