Reputation: 688
I'm making a chart that shows different types of medical events on a timeline, with each "track" of events (medications, labs, and imaging) connected with (red) lines. (Note that the bottom track, "Other", purposely has no line.)
They render just fine (in Typescript) using:
lineGen = d3.svg.line()
.x((d:PatientEvent) => timeScaleAbs(d.time))
.y((d:PatientEvent) => ordScaleTmln(d.type) + ordScaleTmln.rangeBand()/2);
clipAreaGroup.append('path')
.attr('d', lineGen(data.filter((val:PatientEvent) => {return val.type == 'Med'})))
.classed('event-group-line', true);
clipAreaGroup.append('path')
.attr('d', lineGen(data.filter((val:PatientEvent) => {return val.type == 'Lab'})))
.classed('event-group-line', true);
clipAreaGroup.append('path')
.attr('d', lineGen(data.filter((val:PatientEvent) => {return val.type == 'Img'})))
.classed('event-group-line', true);
The chart has a navigation brush at the top, but I don't know how to properly write the code to update each of the line paths after a brush event. I've tried hacking together the code below (which is virtually identical to the code above, except for the addition of the .data() lines) and it almost works except that the top line (for Medication events) won't redraw (as shown below after a brush selection has been made).
lineGen = d3.svg.line()
.x((d:PatientEvent) => timeScaleAbs(d.time))
.y((d:PatientEvent) => ordScaleTmln(d.type) + ordScaleTmln.rangeBand()/2);
clipAreaGroup.selectAll('path')
.data(data.filter((val:PatientEvent) => {return val.type == 'Med'}))
.attr('d', lineGen(data.filter((val:PatientEvent) => {return val.type == 'Med'})))
.classed('event-group-line', true);
clipAreaGroup.selectAll('path')
.data(data.filter((val:PatientEvent) => {return val.type == 'Lab'}))
.attr('d', lineGen(data.filter((val:PatientEvent) => {return val.type == 'Lab'})))
.classed('event-group-line', true);
clipAreaGroup.selectAll('path')
.data(data.filter((val:PatientEvent) => {return val.type == 'Img'}))
.attr('d', lineGen(data.filter((val:PatientEvent) => {return val.type == 'Img'})))
.classed('event-group-line', true);
It seems to have to do with the ordering of the calls. When I rearrange them and call "Img" -> "Med" -> "Lab", then only the "Lab" line will redraw.
I know I'm not doing this very elegantly, but I don't really understand how d3 keeps track of which lines are which for update purposes. I get the dots to redraw just fine, but the line paths really confuse me.
Upvotes: 0
Views: 769
Reputation: 36827
When you call clipAreaGroup.append('path')
, you are creating a new <path>
inside of your <svg>
. You do this three times, so you will end up with three <path>
in your <svg>
.
When you call clipAreaGroup.selectAll('path').data(...)
, you are telling d3 to select each <path>
and then you are binding each datum in the filtered data to an element in the selection (creating new <path>
s for any extra datum). You are doing this three times, updating the d
value each time based on the filtered data from that call, which is why the order you arrange them in is changing the output.
There are a number of ways that you can handle updating your lines, but the simplest would probably be to avoid data binding and just store the <path>
s that you create:
var medPath = clipAreaGroup.append('path')
.attr('d', lineGen(data.filter((val:PatientEvent) => {return val.type == 'Med'})))
.classed('event-group-line', true);
Then, when you want to update that path, you just update its d
attr:
medPath.attr('d', lineGen(data.filter((val:PatientEvent) => {return val.type == 'Med'})))
Upvotes: 2