简体   繁体   中英

how to draw lines/edges in a force directed graph?

Iam trying to implement forced directed graph for a dataset using d3.js.My code outputs nodes,as tiny dot at top of the page,while edges fail to be drawn.If links are left out of the simulation,nodes are drawn correctly,any hint on where i might be doing it wrong will be helpful!

link to codepen

dataset:

{ nodes": [
        { "country": "East Timor", "code": "tl" },
        { "country": "Canada", "code": "ca" },
        { "country": "Turkmenistan", "code": "tm" },
        { "country": "United States of America", "code": "us" },
        { "country": "Lithuania", "code": "lt" },
        { "country": "Cambodia", "code": "kh" },
        { "country": "Ethiopia", "code": "et" },
        { "country": "Swaziland", "code": "sz" },
        ],
"links": [
        { "target": 66, "source": 0 },
        { "target": 3, "source": 1 },
        { "target": 100, "source": 2 },
        { "target": 40, "source": 2 },
        { "target": 125, "source": 2 },
        { "target": 147, "source": 2 },
        { "target": 159, "source": 3 },
        { "target": 18, "source": 3 },
        }


let request = new XMLHttpRequest();
request.addEventListener("load", loaded);

function loaded() {
  const data = JSON.parse(request.responseText);

  var nodes = data.nodes;
  var links = data.links;

  // sets up svg
  var svg = d3.select("svg"),
      width = +svg.attr("width"),
      height = +svg.attr("height");


  // starts simulation
  var simulation = d3
  .forceSimulation()
  .force(
    "link",
    d3.forceLink().id(function(d) {
      return d.country;
    })
  )
  .force("charge", d3.forceManyBody())
  .force("center", d3.forceCenter(width / 2, height / 2));

  // creates lines in graph,
  var link = svg
  .append("g")
  .attr("class", "links")
  .selectAll("line")
  .data(links)
  .enter()
  .append("line");


  var node = svg
  .append("g")
  .attr("class", "nodes")
  .selectAll("circle")
  //pass node data
  .data(nodes)

  .enter()
  .append("circle")
  .attr("r", 5)

  .attr("fill", "red")

  .call(
    d3
    .drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended)
  );

  simulation
  // pass nodes,on tick call function ticked
    .nodes(nodes)
    .on("tick", ticked);
  // pass links
  simulation.force("link").links(links);

  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 d.x;
    })
      .attr("cy", function(d) {
      return d.y;
    });
  }

  function dragstarted(d) {
    if (!d3.event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
  }

  function dragged(d) {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
  }

  function dragended(d) {
    if (!d3.event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
  }
}
request.open(
  "GET",
  "https://www.cs.mun.ca/~h65ped/Public/country%20data%20for%20force%20directed%20graph/countries.json",
  true
);
request.send(null);

Look at your links array:

"links": [
    { "target": 66, "source": 0 },
    { "target": 3, "source": 1 },
    { "target": 100, "source": 2 },
    //etc..

As you can see, there is no country as value, neither for the target nor for the source. The links are using the nodes' indices instead.

Therefore, this...

.force("link", d3.forceLink().id(function(d) {
    return d.country;
}));

... is wrong. Just remove the id :

.force("link", d3.forceLink());

Here is the updated Codepen: https://codepen.io/anon/pen/NwKvbg?editors=0010

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM