D3- How do I draw dots on a multi line graph? (Iterate through array)
D3- How do I draw dots on a multi line graph? (Iterate through array)
I have a multi line chart on which I would like to draw dots ( eventually to add my tooltip to.) I just can't seem to iterate through the data correctly and draw them all. My data is an array of arrays, each sub array being it's own line on which i'd like to draw the dots. x(inspected_at), y(flow_data)
The full code is here, around line 388: https://codepen.io/lahesty/pen/aKQjVK?editors=0011
var dots =
svg.selectAll('.dots').data(data).enter().append("g").attr("class",
"dot");
dots.selectAll('.dot')
.data(data)
.enter()
.append('circle')
.attr("r", 2.5)
.attr("cx", function(d) { return x(d.inspected_at); })
.attr("cy", function(d) { return y(d.flow_data); })
.style("fill", "blue").style("opacity", ".5")
I assume it's something similar because when I change the second .data(data) to .data(data[0]) The first array of dots will appear, but I'd like them all to. I also tried something like this, instead of the above:
svg.selectAll("g.dot")
.data(data)
.enter().append("g")
.attr("class", "dot")
.selectAll("circle")
.data(function(d, i) { return d[i]; })
.enter().append("circle")
.attr("r", 6)
.attr("cx", function(d,i) { return x(d.inspected_at); })
.attr("cy", function(d,i) { return y(d.flow_data); })
I use v4. Any thoughts? Your help is very much appreciated!
2 Answers
2
I'd also say you're almost there - here's how you can move forward: The first argument to data()
needs to be either
data()
You can (and poss. should) give a key function.
So, the outer call to data()
becomes
data()
var dots = svg.selectAll('.dots').data(data, function(d, i) { return d[0]; }).enter() // and so on
the inner call draws upon each row of the outer (i.e. data[0]
, data[1]
,
etc.) and selects the collection for inner. Conveniently, this is what you get - but unless you use a function, you'll never be able to tell D3 what to do. So, the following is a little crazy:
data[0]
data[1]
dots.selectAll('.dot')
.data(function(d) { return d; }, function(d_, i_) { return i_})
.enter() // and so on
but you need a function returning its own param (and a key generator for good measure). I did fork your pen - c here: https://codepen.io/sebredhh/pen/VdVOxQ?editors=0010 and changed lines 389 and 391. Looks like what you were looking for - hope you find this helpful...
Nevermind! Just figured out that I needed to add a ('g') group and append my clip-path to that, right before I select all my dots. I like both of these methods!
– LaurenAH
2 days ago
"The first argument to data() needs to be either an object or a function resolving to an object"... well, that's unfortunately incorrect:
data
doesn't accept objects, it accepts only 3 things: an array, a function or nothing. It does accept a function that returns an object, though, as you said, but not just an object.– Gerardo Furtado
2 days ago
data
I just put in underscores to clarify what is the first level VS the second so the variables are a little more descriptive. There is no functionality associated with it. As for 'object', Gerardo is right: it's not just any object but an array. Sorry for the confusion (will fix above).
– Sebastian Rothbucher
2 days ago
You're almost done. Once you've entered the g.dot
selection, you have to keep the same binding in order to propagate the data to the circles. This can be done with the identity function, either function(d) { return d; }
or Object
.
g.dot
function(d) { return d; }
Object
The inner iteration will be implicitly done within the Selection
.
Selection
svg.selectAll("g.dot")
.data(data)
.enter().append("g")
.attr("class", "dot")
.selectAll("circle")
.data(Object) // <--- identity function here
.enter().append("circle")
.attr("r", 6)
.attr("cx", function(d,i) { return x(d.inspected_at); })
.attr("cy", function(d,i) { return y(d.flow_data); })
Interesting! I like the brevity of this. If you don't mind follow up, how might I add a clip path to this? I'm not sure how I can fit that in with the g.dot selection. Thank you!
– LaurenAH
2 days ago
Nevermind! I actually figured it out for this method. I changed: svg.selectAll("g.dot") to: svg.append('g').attr('clip-path', 'url(#clipper)').selectAll("g.dot") Looks like I needed to add a ('g') group and append my clip-path to that.
– LaurenAH
2 days ago
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Ah! Somehow I didn't realize that data() should resolve to an object. That makes sense. Would you mind clarifying what the underscores accomplish? Furthermore a follow up question, what about my clip-path? It used to be appended to a 'g' object (as it is for my lines) but on the dots it just cuts them off. Thanks!
– LaurenAH
2 days ago