Casmo
Casmo

Reputation: 25

How to use d3 to plot/add node on scatter plot one by one?

I'm pretty new to D3.js and also Javascript. sorry if asking something stupid. I just went through the tutorial of D3, and now can plot all the data which saved in my csv-file.(load by d3.csv function)

I'm curious of that is it possible to plot the dot one by one instead of plotting them all by once?

var dataset;
d3.csv("testcase.csv", function(data) { 
    dataset = data;
    var w = $(window).width();
    var h = $(window).height();

    var svg = d3.select("body")
                .append("svg")
                .attr({
                    width: w,
                    height: h,
                });

    function draw(data){
        var circle = svg.selectAll("circle")
                    .data(data);

        circle.enter().append("circle")
              .attr({
                  "cx": data["x"],
                  "cy": data["y"],
                  "r": data["r"],
                  "fill": "rgb(0,0,0)",
              });
    }

    draw(dataset[0]);

    var index = 1;
    setInterval(function() {
        if(index<dataset.length){
          draw(dataset[index]);
          index++;
        }
     }, 1500);

This is what I get for plotting dots at this moment, in the csv-file it saved the coordinate of the dot, and also its radius. For example:

x,y,r 100,100,50 200,100,30 400,300,20 500,400,50 470,800,40 400,600,40

I'm trying to use setInterval function to let it plot the dot one by one, but it didn't plot anything (Including the first one which should trigger bydraw(dataset[0])) And also when using console.log function to check whether the value is right, it seems totally normal. the output of console pad

what's is going wrong? Thank for your helping :/

Upvotes: 2

Views: 270

Answers (2)

mgraham
mgraham

Reputation: 6207

Following on from Matthew's answer, if you use d3 transitions rather than setInterval then you don't have to build a loop to process nodes individually and also avoid the problem he described (at least on the first pass, add anymore nodes later and you'll need the data key again)

     var data = "x,y,r\n"+
"100,100,50\n"+
"200,100,30\n"+
"400,300,20\n"+
"500,400,50\n"+
"470,800,40\n"+
"400,600,40\n"
;

var dataset = d3.csv.parse (data);

var svg = d3.select("body")
.append("svg")
.attr({
  width: 600,
  height: 400,
});

var circle = svg.selectAll("circle")
.data(dataset);

circle.enter().append("circle")
  .each (function (d,i) {
  d3.select(this).transition()
    .delay(i * 1200)  // <- this does what you intended setinterval to do
    .attr({
    "cx": d.x,
    "cy": d.y,
    "r": d.r,
    "fill": "rgb(0,0,0)",
  });

});
;

http://jsfiddle.net/Qh9X5/7807/

Upvotes: 3

Matthew Herbst
Matthew Herbst

Reputation: 32013

So, you've come across a fairly common logic error being new to d3. Every time you call your draw function, you also re-bind the data. However, in addition to telling d3 about the new data, this also wipes out d3's memory of the old data. Thus, you are effectively telling d3: this new data is truth, forget any data I've told you about in the past.

Ok, the above is a little simplistic.

d3 doesn't actually forget about what you already told it. However, it needs a way of knowing what is new, what already exists, and what is old. To do this, .data assigns each datum a key based on its index in the data array. Since every time you bind just a single element, it will always get a key of 0. Thus, d3 never thinks you are asking it to bind something new.

To solve, this, pass a key function to data, as described in in the documentation.

Your code should then look something along the lines of:

function draw(data, key){
    var circle = svg.selectAll("circle")
                .data(data, function(d){ return key; });

    circle.enter().append("circle")
          .attr({
              "cx": data["x"],
              "cy": data["y"],
              "r": data["r"],
              "fill": "rgb(0,0,0)",
          });
}

draw(dataset[0], 0);

var index = 1;
setInterval(function() {
    if(index<dataset.length){
      draw(dataset[index], index);
      index++;
    }
 }, 1500);

Does that make some sense? I highly suggest a thorough read of the documentation. It's how I learned and as documentation goes it's very well laid out, easy to follow, as has tons of examples.

Upvotes: 0

Related Questions