D3 Dynamic hierarchical edge bundling - 2 way import

倖福魔咒の 提交于 2019-12-11 07:19:17

问题


I am trying to create a dynamic hierarchical edge bundling based on M Bostock's (http://bl.ocks.org/mbostock/7607999) implementation:

The bl.ocks version of my data set is here: http://bl.ocks.org/ratnakarv/91ace0b5f77fff5ef0ab

Unlike the original, where a relation between Node 1 and Node 2 is one way (i.e either Node 1 can import Node 2 or other way around), in my data set, Node 1 and Node 2 can have two way import relationship. This happens way too often in business process flows, which is where my application of this is going to be.

  • Current challenge:

In this example- 'Assess' imports 'Create QOS degradation Report' and 'Create QOS...' imports 'Assess' as well. But when mouse is over, 'Assess', node - 'Create..' is shown in red, but the line is green. When mouse is over node- 'Create...', the import line as well as 'Assess' is shown red.

  • Desired Requirement:

My requirement is that if a two way import exists, then either - 1. the line as well as other node be shown in a third colour (other than red or green) or 2. the line as well as other node be shown as red.

Any pointers to this would help. I have a basic knowledge of D3, but I am not a data visualization expert and merely try using visualization in my field of work for better communication.


回答1:


There are a few ways to approach this, but this is the easiest one I can think of as of now.

Note that there are two paths between Assess and Create QoS Degradation Report in your bl.ock with equal but opposite values (as they are both a source and a target for each other). One of them is laid exactly on top the other, giving them the appearance of a single path. That's why the link between these two nodes is somewhat jagged, while the others are smooth. Perhaps we can take advantage of this.

If you adjust the definitions for link--source and link--target in your CSS to have an opacity value of less than 1, a partially transparent source link will overlap a partially transparent target link, giving the appearance of a "new", different color.

As for the node color, create a new CSS class node--both with the "new" color, and apply it to the node if both n.source and n.target are true in the mousovered function.

node
    .classed("node--both", function(n) { return n.source && n.target; })
    .classed("node--target", function(n) { return n.target; })
    .classed("node--source", function(n) { return n.source; });

It's not perfect, but here's a fiddle demonstrating this: http://jsfiddle.net/w2rfwokx/

The key is to choose the source and target link colors and opacity values appropriately (which I didn't do) so that you get a new color different enough from the source and target links and also the same color regardless of whether the source or the target link is "on top". In the current fiddle version, you can see that the colors slightly differ depending on which node is active. This thread or something similar might help.

You could also try manipulating the links array to merge the two identical paths into one and adding an attribute to indicate that this is a source-and-target link and use this attribute later while processing.

Update: You have the right idea in your comment. The color trick was more of a hack anyway.

The links array contains two items for the one path between the two-way import nodes. Let's remove one of them and also set an attribute in the other to indicate that this is a two-way import.

var unique_links = links.reduce(function(p,c) {
    var index=p.map(function(d,i) { if(d.source===c.target && d.target===c.source) return i;}).shift();
    if(!isNaN(index)) p[index].both=true; else p.push(c);
    return p;
 },[]);

Now unique_links has only one element per edge and that one element has both=true. Let's also pass the both attribute along to the bundle layout.

link = link
      .data(bundle(unique_links))
    .enter().append("path")
      .each(function(d) {
        d.source = d[0], 
        d.target = d[d.length - 1],
        d.both = unique_links.filter(function(v) { if (v.source===d.source && v.target===d.target) return v.both; }).shift();
      })
      .attr("class", "link")
      .attr("d", line);

The final step is to change the mouseovered function to set a new CSS class with a different color using both:

function mouseovered(d) {
  node
      .each(function(n) { n.target = n.source = false; });

  link
      .classed("link--both", function(l) { if((l.target===d || l.source===d) && l.both) return l.source.source = l.source.target = l.target.source = l.target.target = true;})
      .classed("link--target", function(l) { if (l.target === d && !l.both) return l.source.source = true; })
      .classed("link--source", function(l) { if (l.source === d && !l.both) return l.target.target = true; })
    .filter(function(l) { return l.target === d || l.source === d; })
      .each(function() { this.parentNode.appendChild(this); });

  node
      .classed("node--both", function(n) { return n.target && n.source; })
      .classed("node--target", function(n) { return n.target; })
      .classed("node--source", function(n) { return n.source; });
}

And reset the classes in mouseouted:

function mouseouted(d) {
  link
      .classed("link--both", false)
      .classed("link--target", false)
      .classed("link--source", false);

  node
      .classed("node--both", false)
      .classed("node--target", false)
      .classed("node--source", false);
}

Remember to define the new classes in CSS:

.link--both {
  stroke: orange;
}

.node--both {
  fill: orange;
}

Here's an updated fiddle with the complete code: http://jsfiddle.net/w2rfwokx/1/



来源:https://stackoverflow.com/questions/26164800/d3-dynamic-hierarchical-edge-bundling-2-way-import

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!