2

I'm working on a real-time visualization of incoming data stored in a json file. I use D3 for the visualization. This is the chart I use: http://mbostock.github.io/d3/talk/20111116/pack-hierarchy.html

enter image description here

This is all the code page:

<body onload="visualize()">

<h2>
    <input type="button" value="Get new data"
        onclick='ajaxSyncRequest("get-current-time")' /> <br /> <br />
    Message from server :: <span id="message"></span>
</h2>

<script type="text/javascript">

function ajaxSyncRequest(reqURL) {
    //Creating a new XMLHttpRequest object
    var xmlhttp;
    if (window.XMLHttpRequest) {
        xmlhttp = new XMLHttpRequest(); //for IE7+, Firefox, Chrome, Opera, Safari
    } else {
        xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); //for IE6, IE5
    }
    //Create a asynchronous GET request
    xmlhttp.open("GET", reqURL, false);
    xmlhttp.send(null);

    //Execution blocked till server send the response
    if (xmlhttp.readyState == 4) {
        if (xmlhttp.status == 200) {
            document.getElementById("message").innerHTML = xmlhttp.responseText;
            //alert(xmlhttp.responseText);
            update();

        } else {
            alert('Something is wrong !!');
        }
    }
}
</script>



<script type="text/javascript" src="d3/d3.js"></script>
<script type="text/javascript" src="d3/d3.layout.js"></script>
<script type="text/javascript">
    function visualize() {

        var w = 1280, h = 800, r = 720, x = d3.scale.linear().range(
                [ 0, r ]), y = d3.scale.linear().range([ 0, r ]), node, root;

        var pack = d3.layout.pack().size([ r, r ]).value(function(d) {
            return d.size;
        })

        var vis = d3.select("body").insert("svg:svg", "h2")
                .attr("width", w).attr("height", h).append("svg:g").attr(
                        "transform",
                        "translate(" + (w - r) / 2 + "," + (h - r) / 2
                                + ")");

        d3.json("flare.json", function(data) {
            node = root = data;

            var nodes = pack.nodes(root);

            vis.selectAll("circle").data(nodes).enter()
                    .append("svg:circle").attr("class", function(d) {
                        return d.children ? "parent" : "child";
                    }).attr("cx", function(d) {
                        return d.x;
                    }).attr("cy", function(d) {
                        return d.y;
                    }).attr("r", function(d) {
                        return d.r;
                    }).on("click", function(d) {
                        return zoom(node == d ? root : d);
                    });

            vis.selectAll("text").data(nodes).enter().append("svg:text")
                    .attr("class", function(d) {
                        return d.children ? "parent" : "child";
                    }).attr("x", function(d) {
                        return d.x;
                    }).attr("y", function(d) {
                        return d.y;
                    }).attr("dy", ".35em").attr("text-anchor", "middle")
                    .style("opacity", function(d) {
                        return d.r > 20 ? 1 : 0;
                    }).text(function(d) {
                        return d.name;
                    });

            d3.select(window).on("click", function() {
                zoom(root);
            });
        });

        function zoom(d, i) {
            var k = r / d.r / 2;
            x.domain([ d.x - d.r, d.x + d.r ]);
            y.domain([ d.y - d.r, d.y + d.r ]);

            var t = vis.transition().duration(d3.event.altKey ? 7500 : 750);

            t.selectAll("circle").attr("cx", function(d) {
                return x(d.x);
            }).attr("cy", function(d) {
                return y(d.y);
            }).attr("r", function(d) {
                return k * d.r;
            });

            t.selectAll("text").attr("x", function(d) {
                return x(d.x);
            }).attr("y", function(d) {
                return y(d.y);
            }).style("opacity", function(d) {
                return k * d.r > 20 ? 1 : 0;
            });

            node = d;
            d3.event.stopPropagation();
        }
    }
</script>

<script type="text/javascript">
    function update() {

        var w = 1280, h = 800, r = 720, x = d3.scale.linear().range(
                [ 0, r ]), y = d3.scale.linear().range([ 0, r ]), node, root;

        var pack = d3.layout.pack().size([ r, r ]).value(function(d) {
            return d.size;
        })

        var vis = d3.select("svg").attr("width", w).attr("height", h)
                .append("svg:g").attr(
                        "transform",
                        "translate(" + (w - r) / 2 + "," + (h - r) / 2
                                + ")");

        d3.json("flare.json", function(data) {

            node = root = data;

            var nodes = pack.nodes(root);
            // DATA JOIN
            // Join new data with old elements, if any.
            var newG = vis.selectAll("circle").data(nodes);

            // UPDATE
            // Update old elements as needed.
            newG.attr("class", "update").transition();

            // ENTER
            // Create new elements as needed.
            newG.enter().append("svg:circle").attr("class", function(d) {
                return d.children ? "parent" : "child";
            }).attr("cx", function(d) {
                return d.x;
            }).attr("cy", function(d) {
                return d.y;
            }).attr("r", function(d) {
                return d.r;
            }).on("click", function(d) {
                return zoom(node == d ? root : d);
            });

            newG.enter().append("svg:text")
            .attr("class", function(d) {
                return d.children ? "parent" : "child";
            }).attr("x", function(d) {
                return d.x;
            }).attr("y", function(d) {
                return d.y;
            }).attr("dy", ".35em").attr("text-anchor", "middle")
            .style("opacity", function(d) {
                return d.r > 20 ? 1 : 0;
            }).text(function(d) {
                return d.name;
            });

            // EXIT
            // Remove old elements as needed.
            newG.exit().attr("class", "exit").transition().remove();

            d3.select(window).on("click", function() {
                zoom(root);
            });
        });

        function zoom(d, i) {
            var k = r / d.r / 2;
            x.domain([ d.x - d.r, d.x + d.r ]);
            y.domain([ d.y - d.r, d.y + d.r ]);

            var t = vis.transition().duration(d3.event.altKey ? 7500 : 750);

            t.selectAll("circle").attr("cx", function(d) {
                return x(d.x);
            }).attr("cy", function(d) {
                return y(d.y);
            }).attr("r", function(d) {
                return k * d.r;
            });

            t.selectAll("text").attr("x", function(d) {
                return x(d.x);
            }).attr("y", function(d) {
                return y(d.y);
            }).style("opacity", function(d) {
                return k * d.r > 20 ? 1 : 0;
            });

            node = d;
            d3.event.stopPropagation();
        }
    }
</script>

I wanted to dynamically update the chart from the new file that I get from the server. But with this function it draw a new chart on the old. I have tried different solution to update the chart but none of them worked. How I have to modify the code to get the dynamically update?

VividD
  • 10,456
  • 6
  • 64
  • 111
Mario Lepore
  • 307
  • 2
  • 8
  • 18

1 Answers1

4

The following changes are necessary to make this work. First, you need to select the existing SVG element (and its descendant g) as vis in the update function:

var vis = d3.select("svg > g");

Then, you need to compute and handle enter, update and exit selections for circles and text separately:

var newG = vis.selectAll("circle").data(nodes);

newG.enter().append("svg:circle");
newG.exit().remove();
newG.attr("class", function(d) {
    return d.children ? "parent" : "child";
  }).attr("cx", function(d) {
    return d.x;
  }).attr("cy", function(d) {
    return d.y;
  }).attr("r", function(d) {
    return d.r;
  }).on("click", function(d) {
    return zoom(node == d ? root : d);
  });

var texts = vis.selectAll("text").data(nodes);

texts.enter().append("svg:text");
texts.exit().remove();
texts.append("svg:text")
  .attr("class", function(d) {
    return d.children ? "parent" : "child";
  }).attr("x", function(d) {
    return d.x;
  }).attr("y", function(d) {
    return d.y;
  }).attr("dy", ".35em")
  .attr("text-anchor", "middle")
  .style("opacity", function(d) {
    return d.r > 20 ? 1 : 0;
  }).text(function(d) {
    return d.name;
  }); 

You could also merge your functions as they both do almost the same thing and there's a lot of redundant code. But the above should be sufficient to make it work.

hobs
  • 18,473
  • 10
  • 83
  • 106
Lars Kotthoff
  • 107,425
  • 16
  • 204
  • 204
  • Thanks, now the updating works well. There is only a problem: when I increase one node size in the json file, in the visualization it automatically increase the size of the node I specified but it decrease the radius of the others node, even if their sizes in the file haven't change. How I can avoid this behavior? – Mario Lepore Feb 11 '14 at 10:57
  • This is what the pack layout does. If you want to maintain the relative sizes, you also need to increase the value of the other nodes by the same relative amount. – Lars Kotthoff Feb 11 '14 at 11:02
  • ok. With this pack layout is it possible to change the size of the parent node? I have tried this {"name":"food","size":900000,"children":[{"name":"dish","size":8000... but it doesn't work. – Mario Lepore Feb 11 '14 at 14:10
  • The root node will cover the entire layout. – Lars Kotthoff Feb 11 '14 at 14:21
  • In the end, this chart doesn't fit with what I have to do. I am searching for a chart that shows the hierarchical relationships between the nodes and that let me specify all their absolute size. I have just tried the Collapsible Force Layout, here I can specify the absolute size of leaf nodes but I want to set even the size of the parent nodes. In your opinion which chart I can use? – Mario Lepore Feb 11 '14 at 15:42
  • Hmmm, it doesn't sound as if one of the existing D3 layouts would do that for you. – Lars Kotthoff Feb 11 '14 at 16:15