Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

modifying the d3 force-directed graph example

I'm new to javascript and D3.js, and I am trying to understand how it all works. I have been playing with the force-directed graph example here: http://bl.ocks.org/mbostock/4062045

What I want to do, is to change the JSON links from being array numbers to node names. I'm trying to visualize a small network topology, and I have the node neighbors all set up. Here is the JSON data I would like to use:

{
  "nodes":[
    {"name":"stkbl0001","group":1},
    {"name":"stkbl0002","group":1},
    {"name":"stkbl0003","group":1},
    {"name":"stkbl0004","group":1},
    {"name":"stkbl0005","group":1}
  ],
  "links":[
    {"source":"stkbl0001","target":"stkbl0005","value":3},
    {"source":"stkbl0002","target":"stkbl0005","value":3},
    {"source":"stkbl0003","target":"stkbl0005","value":3},
    {"source":"stkbl0004","target":"stkbl0005","value":3}
  ]

I really don't know how to alter the D3 code to tie all this together. I fail to see the section where the array numbers is fetched and glued together as links. This is probably a stupid question, but it will help me a lot!

EDIT:

Here is the javascript code I have so far based on the input from Lars Kotthoff:

<!DOCTYPE html>
<meta charset="utf-8">
<style>

.node {
  stroke: #fff;
  stroke-width: 1.5px;
}

.link {
  stroke: #999;
  stroke-opacity: .6;
}

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

var width = 960,
    height = 500;

var color = d3.scale.category20();

var force = d3.layout.force()
    .charge(-120)
    .linkDistance(30)
    .size([width, height]);

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

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

  var link = svg.selectAll(".link")
      .data(graph.links)
      .enter().append("line")
      .attr("class", "link")
      .style("stroke-width", function(d) { return Math.sqrt(d.value); });

  var node = svg.selectAll(".node")
      .data(graph.nodes)
      .enter().append("circle")
      .attr("class", "node")
      .attr("r", 5)
      .style("fill", function(d) { return color(d.group); })
      .call(force.drag);

  var nodeMap = {};
  nodes.forEach(function(d) { nodeMap[d.name] = d; });

  links.forEach(function(l) {
      l.source = nodeMap[l.source];
      l.target = nodeMap[l.target];
  })

  node.append("title")
      .text(function(d) { return d.name; });

  force.on("tick", function() {
    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; });
  });
});
</script>

This fails at line 55 (nodes.forEach(function(d) { nodeMap[d.name] = d; });) with the error:

Uncaught ReferenceError: nodes is not defined
like image 355
ThomasV Avatar asked Jun 03 '14 13:06

ThomasV


1 Answers

This link links to a working example based on the your example.

The critical code is placed just before initialization of force layout:

var nodeMap = {};

graph.nodes.forEach(function(d) { nodeMap[d.name] = d; });

graph.links.forEach(function(l) {
    l.source = nodeMap[l.source];
    l.target = nodeMap[l.target];
})

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

That way you will be able to use your data format in the same fashion as the original format is used (and many examples on the net follow that original format, so you will be able to adapt many of them to your format without problems).

(json file is not used in my example, due to restrictions of jsfiddle; instead, function getData() is made to return the data; but this is not essential to your question; you can use this solution with json files too)

Hope this helps.

like image 56
VividD Avatar answered Sep 24 '22 04:09

VividD