Andrew
Andrew

Reputation: 1653

Trouble with d3 line charts with points

So I guess my main issue is that my data isn't EXACTLY like all of the d3 demos out there. I made a plugin that normalizes all my data into the form:

[{"name":"someName", "values":[{x1, y1}, {x2, y2}, ... {xn, yn}, ...]

So this code works just fine for the line:

var line = d3.svg.line()
    .x(function(d) { return x(d.x); })
    .y(function(d) { return y(d.y); });

// data is a list of objects, each of which respresents a line
var series = svg.selectAll(".series")
    .data(dataSimplified)
    .enter().append("g")
    .attr("class", "series");

series.append("path")
    .attr("class", "line")
    // the following function requires a list of things, like [{x0, y0}, {x1, y1}, ... {xn, yn}]
    .attr("d", function(d) { return line(d.values); })
    .style("stroke", function(d) { return color(d.name); });

So it basically iterates over each "values" field and does the line function to get the x and y. But if I do a similar thing for the points, it doesn't work! The following, however, DOES work:

// add points
for(var i=0; i<dataSimplified.length; i++){
    for(var j=0; j<dataSimplified[i].values.length; j++){
        svg.append("circle")
            .attr("r", 3.5)
            .attr("cx", function(d){ return x(dataSimplified[i].values[j].x); })
            .attr("cy", function(d){ return y(dataSimplified[i].values[j].y); })
            .attr("fill", function(d){ return color(dataSimplified[i].name); });
    }
}

Is there a better way to do this than adding the points one by one? Why doesn't cx accept a list like d did for the path?

Upvotes: 1

Views: 498

Answers (1)

Steve P
Steve P

Reputation: 19377

You can call .data again on for nested selections (in this case the <circle> tags nested inside the <g> tag). Something like this:

series.selectAll('circle')
    .data(function(d, i, j) { return d.values; })
    .enter().append('circle')
        .attr("r", 3.5)
        .attr("cx", function(nd){ return x(nd.x); })
        .attr("cy", function(nd){ return y(nd.y); })
        .attr("fill", function(nd, i, j){ return color(dataSimplified[j].name); });

The reason it seems inconsistent between line and circle is that D3 is only a thin layer over the underlying DOM nodes (html or svg), and doesn't abstract that away from you. So in svg, <path> is a single tag which contains an internal list of points, while <circle> is just one circle. So your structure for one series will be something like this (assuming you have three data points).

<g><path/><circle/><circle/><circle/></g>

And so your code will reflect the same kind of structure.

Upvotes: 4

Related Questions