1

I have a graph structure that stored in json format that looks like this:

{
    "links": [
        {
            "source": 1, 
            "target": 0, 
            "value": 1
        }, 
        {
            "source": 2, 
            "target": 0, 
            "value": 1
        }, 
        {
            "source": 3, 
            "target": 0, 
            "value": 1
        }
    ],
    "nodes": [
            {
                "group": 3, 
                "name": "justintimberlake"
            }, 
            {
                "group": 2, 
                "name": "Anastacia Lyn Newton"
            }, 


     {
            "group": 2, 
            "name": "Maria Do Carmo"
        }
],
"time": [
        {
            "source": 1, 
            "time": 6.854456018518518
        }, 
        {
            "source": 2, 
            "time": 6.320115740740741
        }, 
        {
            "source": 3, 
            "time": 5.962986111111111
        }
]
}

And I have D3 code that draws this network:

<!DOCTYPE html xmlns:xlink="http://www.w3.org/1999/xlink">
<meta charset="utf-8">
<style>
// style here
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<div id="animviz"></div>
<script>

    d3.json("post000.json", function(error, graph) {

        var vv = window,
            w = vv.innerWidth,
            h = vv.innerHeight;

        var svg = d3.select("#animviz")
                .append("svg")
                .attr("width", w)
                .attr("height", h)
                .append("g")
                .call(d3.behavior.zoom().scaleExtent([0, 8]).on("zoom", zoom))
                .append("g");

        var color = d3.scale.category10();

        var force = d3.layout.force()
                .charge(-200)
                .linkDistance(50)
                .size([w, h]);

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

        var link = svg.selectAll(".link")
                .data(graph.links)
                .enter().append("line")
                .attr("class", "link")
                .attr("transform", function(d) { return "translate(" + d + ")"; });

        function zoom() {
          svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
        }

        var myMouseoverFunction = function() {
            var circle = d3.select(this);
            circle.transition().duration(100)
                    .attr("r", 20 )
            node.append("title")
                .text(function(d) { return d.name});
        }

        var myMouseoutFunction = function() {
            var circle = d3.select(this);
            circle.transition().duration(500)
                    .attr("r", 10 );
        }
        var node = svg.selectAll(".node")
                .data(graph.nodes)
                .enter().append("circle")
                .attr("class", "node")
                .attr("r", 10)
                .style("fill", function(d) { return color(d.group); })
                .call(force.drag)
                .on("mouseover", myMouseoverFunction)
                .on("mouseout", myMouseoutFunction);


        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>

</body>

What I want is to draw this graph node by node according to time parameter (i.e. source: 1 should be drawn after 6.854456018518518 sec after node = 0 was drawn).

If it's not possible to draw them after special number of seconds, I'd like at least to draw them in order, so that I can see how nodes appear one after the other.

I checked similar questions (here, here, and here) and this tutorial but wasn't able to solve my problem. Ideally I would love to have similar to this but for my data from json file and not in infinite loop.

How can I draw a graph stored in json node by node?

Community
  • 1
  • 1
Sergey Ivanov
  • 3,719
  • 7
  • 34
  • 59

1 Answers1

1

one way to achieve this is to create nodes with radius = 0, and then use delay for showing each node (giving it radius = 12):

node.attr("r", 0);

var totalDelay = 0;  
    node
      .transition()
      .duration(0)
      .delay(function(d, i) {
        totalDelay += graph.time[i].time * 1000;
        return totalDelay
      })
      .attr("r", 12);

See this jsFiddle

The problem with this solution is that all the links appear immediately, without waiting for its nodes to appear.

Added: to deal with links problem, you may want to redraw graph after each interval, every time adding one node, and calculating the array of links for the nodes, displayed in each iteration:

var i = 0;
function redraw() {
    if (i === graph.time.length) return;
    setTimeout(function() {
        var nodes = graph.nodes.slice(0, i + 1);
        var links = graph.links.filter(function(link) {
            return (link.source <= i && link.target <= i) 
        });

        draw(nodes, links);
        i += 1;

        redraw();    
    }, graph.time[i].time * 1000);
}

See improved jsFiddle

For big datasets might be more efficient to keep the same nodes array and do nodes.push(graph.nodes[i]), instead of creating a new array in each iteration.

eagor
  • 9,150
  • 8
  • 47
  • 50