1

I would like to reproduce the process from D3 Sankey chart using circle node instead of rectangle node, however, I would like to select only certain nodes to change from rectangles to circles.

For example, in this jsfiddle used in the example, how would you only select Node 4 and Node 7 to be converted to a circle?

enter image description here

Community
  • 1
  • 1
Clayton
  • 1,525
  • 5
  • 19
  • 35

2 Answers2

3

I updated your fiddle.

Basically you just need some way to select the nodes that you want to make different. I used unique classname so that you can style them with CSS as well. I didn't feel like writing the code to select just 4 and 7 (I'm lazy) so I just selected all of the even nodes instead.

// add in the nodes
var node = svg.append("g").selectAll(".node")
    .data(graph.nodes)
    .enter().append("g")
    .attr("class", function (d, i) { return i % 2 ? "node rect" : "node circle";
})

Then you can use that to select the nodes and add circles instead of rectangles.

svg.selectAll(".node.circle").append("circle")
    .attr("r", sankey.nodeWidth() / 2)
    .attr("cy", function(d) { return d.dy/2; })
    .attr("cx", sankey.nodeWidth() / 2)
    .style("fill", function (d) {
Bill
  • 25,119
  • 8
  • 94
  • 125
2

There is also another similar approach, illustrated in the following jsfiddle.

enter image description here


I started from this fiddle (from another SO question that you merntioned)), where all nodes had already been converted to circles:

enter image description here

Then I modified existing and added some new code that involves filtering during creation of circles:

// add the circles for "node4" and "node7"
node
    .filter(function(d){ return (d.name == "node4") || (d.name == "node7"); })
    .append("circle")
    .attr("cx", sankey.nodeWidth()/2)
    .attr("cy", function (d) {
        return d.dy/2;
    })
    .attr("r", function (d) {
        return Math.sqrt(d.dy);
    })
    .style("fill", function (d) {
        return d.color = color(d.name.replace(/ .*/, ""));
    })
    .style("fill-opacity", ".9")
    .style("shape-rendering", "crispEdges")
    .style("stroke", function (d) {
        return d3.rgb(d.color).darker(2);
    })
    .append("title")
    .text(function (d) {
        return d.name + "\n" + format(d.value);
    });

// add the rectangles for the rest of the nodes
node
    .filter(function(d){ return !((d.name == "node4") || (d.name == "node7")); })
    .append("rect")
    .attr("y", function (d) {
        return d.dy/2 - Math.sqrt(d.dy)/2;
    })
    .attr("height", function (d) {
        return Math.sqrt(d.dy);
    })
    .attr("width", sankey.nodeWidth())
    .style("fill", function (d) {
        return d.color = color(d.name.replace(/ .*/, ""));
    })
    .style("fill-opacity", ".9")
    .style("shape-rendering", "crispEdges")
    .style("stroke", function (d) {
        return d3.rgb(d.color).darker(2);
    })
    .append("title")
    .text(function (d) {
        return d.name + "\n" + format(d.value);
    });

Similar code had to be modified for accurate positioning text beside rectangles.

I believe the final result looks natural, even though it lost some of the qualities of the original Sankey (like wider connections).

Community
  • 1
  • 1
VividD
  • 10,456
  • 6
  • 64
  • 111