How to create d3.js Foldable power layout with non-wood data?

I have a d3 oriented layout with data in a similar structure below. Can markup be applied to it like http://bl.ocks.org/mbostock/1062288 ? I want the node to collapse / expand on click.

{
  "nodes": [
    {"x": 469, "y": 410},
    {"x": 493, "y": 364},
    {"x": 442, "y": 365},
    {"x": 467, "y": 314},
  ],
  "links": [
    {"source":  0, "target":  1},
    {"source":  1, "target":  2},
    {"source":  2, "target":  0},
    {"source":  1, "target":  3},
    {"source":  3, "target":  2},
  ]
}

      

+3


source to share


2 answers


If I understand correctly, perhaps this is what you are looking for. I edited the demo you linked to. Now that the source node is collapsed, we iterate over all the edges and look for other nodes to which it has edges.

for every target node that the source node has an edge, we increase its flip count. If a node has a drop count greater than zero, it is not displayed.

When we expand a node, we do the same, except that we are decreasing from the rolling count.

We need this collapsing count because since we are not in the tree, nodes can have more than one node, which should cause them to be dropped.

I made this work for oriented graphs, although I'm not sure what you wanted.



Let me know what you think!

The json used:

  {
    "nodes": [
     {"x": 469, "y": 410},
     {"x": 493, "y": 364},
     {"x": 442, "y": 365},
     {"x": 467, "y": 314}
 ],
     "links": [
      {"source":  0, "target":  1},
      {"source":  1, "target":  2},
      {"source":  2, "target":  0},
      {"source":  1, "target":  3},
      {"source":  3, "target":  2}
  ]
 }

      

Modified tutorial code:

<!DOCTYPE html>
<meta charset="utf-8">
<title>Force-Directed Graph</title>
<style>

.node {
  cursor: pointer;
  stroke: #3182bd;
  stroke-width: 1.5px;
}

.link {
  fill: none;
  stroke: #9ecae1;
  stroke-width: 1.5px;
}

</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>

var width = 960,
    height = 500,
    root;

var force = d3.layout.force()
    .size([width, height])
    .on("tick", tick);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

//Added markers to indicate that this is a directed graph
svg.append("defs").selectAll("marker")
    .data(["arrow"])
    .enter().append("marker")
    .attr("id", function(d) { return d; })
    .attr("viewBox", "0 -5 10 10")
    .attr("refX", 15)
    .attr("refY", -1.5)
    .attr("markerWidth", 4)
    .attr("markerHeight", 4)
    .attr("orient", "auto")
    .append("path")
    .attr("d", "M0,-5L10,0L0,5");

var link = svg.selectAll(".link"),
    node = svg.selectAll(".node");

d3.json("graph.json", function(json) {
  root = json;
  //Give nodes ids and initialize variables
  for(var i=0; i<root.nodes.length; i++) {
    var node = root.nodes[i];
    node.id = i;
    node.collapsing = 0;
    node.collapsed = false;
  }
  //Give links ids and initialize variables
  for(var i=0; i<root.links.length; i++) {
    var link = root.links[i];
    link.source = root.nodes[link.source];
    link.target = root.nodes[link.target];
    link.id = i;
  }

  update();
});

function update() {
  //Keep only the visible nodes
  var nodes = root.nodes.filter(function(d) {
    return d.collapsing == 0;
  });
  var links = root.links;
  //Keep only the visible links
  links = root.links.filter(function(d) {
    return d.source.collapsing == 0 && d.target.collapsing == 0;
  });

  force
      .nodes(nodes)
      .links(links)
      .start();

  // Update the links…
  link = link.data(links, function(d) { return d.id; });

  // Exit any old links.
  link.exit().remove();

  // Enter any new links.
  link.enter().insert("line", ".node")
      .attr("class", "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; })
      .attr("marker-end", "url(#arrow)");

  // Update the nodes…
  node = node.data(nodes, function(d){ return d.id; }).style("fill", color);

  // Exit any old nodes.
  node.exit().remove();

  // Enter any new nodes.
  node.enter().append("circle")
      .attr("class", "node")
      .attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; })
      .attr("r", function(d) { return Math.sqrt(d.size) / 10 || 4.5; })
      .style("fill", color)
      .on("click", click)
      .call(force.drag);
}

function tick() {
  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; });
}

// Color leaf nodes orange, and packages white or blue.
function color(d) {
  return d.collapsed ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c";
}

// Toggle children on click.
function click(d) {
  if (!d3.event.defaultPrevented) {
    //check if link is from this node, and if so, collapse
    root.links.forEach(function(l) {
      if(l.source.id == d.id) {
        if(d.collapsed){
          l.target.collapsing--;
        } else {
          l.target.collapsing++;
        }
      }
    });
    d.collapsed = !d.collapsed;
  }
  update();
}

</script>

      

+2


source


Try the following:

    var width = 960,height = 500;

    var force = d3.layout.force().size([width, height]).charge(-400)
                .linkDistance(40)
                .on("tick", tick);

         var drag = force.drag().on("dragstart", dragstart);

           var svg = d3.select("body").append("svg").attr("width", width)
                        .attr("height", height);

           var link = svg.selectAll(".link"),
                  node = svg.selectAll(".node");

            d3.json("graph.json", function(error, graph) {
                     force.nodes(graph.nodes).links(graph.links)
                         .start();

        link = link.data(graph.links).enter().append("line")
                      .attr("class", "link");

                  node = node.data(graph.nodes)
                 .enter().append("circle")
                 .attr("class", "node")
                 .attr("r", 12)
                 .call(drag);
         });

         function tick() {
              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 dragstart(d) {
                  d3.select(this).classed("fixed", d.fixed = true);
              }

      

You have to use json file like this:



graph.json

      {
        "nodes": [
         {"x": 469, "y": 410},
         {"x": 493, "y": 364},
         {"x": 442, "y": 365},
         {"x": 467, "y": 314},
     ],
         "links": [
          {"source":  0, "target":  1},
          {"source":  1, "target":  2},
          {"source":  2, "target":  0},
          {"source":  1, "target":  3},
          {"source":  3, "target":  2},
      ]
     }

      

0


source







All Articles