Javascript 使用圆形节点代替矩形节点的D3 Sankey图表

Javascript 使用圆形节点代替矩形节点的D3 Sankey图表,javascript,svg,d3.js,transformation,sankey-diagram,Javascript,Svg,D3.js,Transformation,Sankey Diagram,我想使用Sankey图表,但要使用圆形而不是矩形 我是在效仿美国的例子 我通过设置半径将代码更改为使用圆形,但如何在圆形周围放置连接节点的线 任何线索 谢谢。首先,我想告诉你,我喜欢你的想法 我将引导您完成几个简单的步骤,以获得一个带有圆圈的像样的Sankey图表。最终的结果可能不适合您的应用程序,但我想它可能对您的起点有用。一旦您了解了d3 Sankey插件的内部和外部功能,您应该能够准确地构建您所设计和希望的内容 起点 这只是一个基本的Sankey示例。我在JSFIDLE中包含了数据和

我想使用
Sankey
图表,但要使用圆形而不是矩形

我是在效仿美国的例子

我通过设置半径将代码更改为使用圆形,但如何在圆形周围放置连接节点的线

任何线索


谢谢。

首先,我想告诉你,我喜欢你的想法

我将引导您完成几个简单的步骤,以获得一个带有圆圈的像样的Sankey图表。最终的结果可能不适合您的应用程序,但我想它可能对您的起点有用。一旦您了解了d3 Sankey插件的内部和外部功能,您应该能够准确地构建您所设计和希望的内容


起点

这只是一个基本的Sankey示例。我在JSFIDLE中包含了数据和Sankey插件代码。这只是为了方便起见,因为jsfidle没有合适的方法来包含多个文件。这就是:


第一步 现在我们将执行您已经执行的操作-将矩形转换为圆

让我们更改此代码:

// add the rectangles for the nodes
node.append("rect")
    .attr("height", function (d) {
        return d.dy;
    })
    .attr("width", sankey.nodeWidth())
// add the circles for the nodes
node.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("stroke-width", function (d) {
        return Math.max(1, d.dy);
    })
    .style("stroke-width", function (d) {
        return Math.max(1, Math.sqrt(d.dy));
    })
var path = d3.svg.diagonal()
    .source(function(d) { return {"x":d.source.y, "y":d.source.x}; })            
    .target(function(d) { return {"x":d.target.y, "y":d.target.x}; })
    .projection(function(d) { return [d.y, d.x]; });
    .attr("width", sankey.nodeWidth())
var path = d3.svg.diagonal()
    .source(function(d) {
        return {"x":d.source.y + d.source.dy / 2,
                "y":d.source.x + sankey.nodeWidth()/2};
    })            
    .target(function(d) {
        return {"x":d.target.y + d.target.dy / 2,
                "y":d.target.x + sankey.nodeWidth()/2};
    })
    .projection(function(d) { return [d.y, d.x]; });
根据本守则:

// add the rectangles for the nodes
node.append("rect")
    .attr("height", function (d) {
        return d.dy;
    })
    .attr("width", sankey.nodeWidth())
// add the circles for the nodes
node.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("stroke-width", function (d) {
        return Math.max(1, d.dy);
    })
    .style("stroke-width", function (d) {
        return Math.max(1, Math.sqrt(d.dy));
    })
var path = d3.svg.diagonal()
    .source(function(d) { return {"x":d.source.y, "y":d.source.x}; })            
    .target(function(d) { return {"x":d.target.y, "y":d.target.x}; })
    .projection(function(d) { return [d.y, d.x]; });
    .attr("width", sankey.nodeWidth())
var path = d3.svg.diagonal()
    .source(function(d) {
        return {"x":d.source.y + d.source.dy / 2,
                "y":d.source.x + sankey.nodeWidth()/2};
    })            
    .target(function(d) {
        return {"x":d.target.y + d.target.dy / 2,
                "y":d.target.x + sankey.nodeWidth()/2};
    })
    .projection(function(d) { return [d.y, d.x]; });
我选择使用Math.sqrt(),因为这样圆的面积将与其表示的值成比例。我认为这是circle最自然的选择

结果如下:


步骤2 链接现在是不自然的宽。让它们的宽度和它们所代表的流量的平方根成比例

让我们更改此代码:

// add the rectangles for the nodes
node.append("rect")
    .attr("height", function (d) {
        return d.dy;
    })
    .attr("width", sankey.nodeWidth())
// add the circles for the nodes
node.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("stroke-width", function (d) {
        return Math.max(1, d.dy);
    })
    .style("stroke-width", function (d) {
        return Math.max(1, Math.sqrt(d.dy));
    })
var path = d3.svg.diagonal()
    .source(function(d) { return {"x":d.source.y, "y":d.source.x}; })            
    .target(function(d) { return {"x":d.target.y, "y":d.target.x}; })
    .projection(function(d) { return [d.y, d.x]; });
    .attr("width", sankey.nodeWidth())
var path = d3.svg.diagonal()
    .source(function(d) {
        return {"x":d.source.y + d.source.dy / 2,
                "y":d.source.x + sankey.nodeWidth()/2};
    })            
    .target(function(d) {
        return {"x":d.target.y + d.target.dy / 2,
                "y":d.target.x + sankey.nodeWidth()/2};
    })
    .projection(function(d) { return [d.y, d.x]; });
根据本守则:

// add the rectangles for the nodes
node.append("rect")
    .attr("height", function (d) {
        return d.dy;
    })
    .attr("width", sankey.nodeWidth())
// add the circles for the nodes
node.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("stroke-width", function (d) {
        return Math.max(1, d.dy);
    })
    .style("stroke-width", function (d) {
        return Math.max(1, Math.sqrt(d.dy));
    })
var path = d3.svg.diagonal()
    .source(function(d) { return {"x":d.source.y, "y":d.source.x}; })            
    .target(function(d) { return {"x":d.target.y, "y":d.target.x}; })
    .projection(function(d) { return [d.y, d.x]; });
    .attr("width", sankey.nodeWidth())
var path = d3.svg.diagonal()
    .source(function(d) {
        return {"x":d.source.y + d.source.dy / 2,
                "y":d.source.x + sankey.nodeWidth()/2};
    })            
    .target(function(d) {
        return {"x":d.target.y + d.target.dy / 2,
                "y":d.target.x + sankey.nodeWidth()/2};
    })
    .projection(function(d) { return [d.y, d.x]; });
结果如下:


步骤3 现在让我们修复链接的端点

我将使用来自此的代码

此代码:

var path = sankey.link();
将替换为以下内容:

var path = d3.svg.diagonal()
    .source(function(d) { return {"x":d.source.y, "y":d.source.x}; })            
    .target(function(d) { return {"x":d.target.y, "y":d.target.x}; })
    .projection(function(d) { return [d.y, d.x]; });
结果如下:


步骤4 现在连接节点的原点,但我们需要它们来连接圆的中心

这就是我们将更改此代码的原因:

// add the rectangles for the nodes
node.append("rect")
    .attr("height", function (d) {
        return d.dy;
    })
    .attr("width", sankey.nodeWidth())
// add the circles for the nodes
node.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("stroke-width", function (d) {
        return Math.max(1, d.dy);
    })
    .style("stroke-width", function (d) {
        return Math.max(1, Math.sqrt(d.dy));
    })
var path = d3.svg.diagonal()
    .source(function(d) { return {"x":d.source.y, "y":d.source.x}; })            
    .target(function(d) { return {"x":d.target.y, "y":d.target.x}; })
    .projection(function(d) { return [d.y, d.x]; });
    .attr("width", sankey.nodeWidth())
var path = d3.svg.diagonal()
    .source(function(d) {
        return {"x":d.source.y + d.source.dy / 2,
                "y":d.source.x + sankey.nodeWidth()/2};
    })            
    .target(function(d) {
        return {"x":d.target.y + d.target.dy / 2,
                "y":d.target.x + sankey.nodeWidth()/2};
    })
    .projection(function(d) { return [d.y, d.x]; });
根据本守则:

// add the rectangles for the nodes
node.append("rect")
    .attr("height", function (d) {
        return d.dy;
    })
    .attr("width", sankey.nodeWidth())
// add the circles for the nodes
node.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("stroke-width", function (d) {
        return Math.max(1, d.dy);
    })
    .style("stroke-width", function (d) {
        return Math.max(1, Math.sqrt(d.dy));
    })
var path = d3.svg.diagonal()
    .source(function(d) { return {"x":d.source.y, "y":d.source.x}; })            
    .target(function(d) { return {"x":d.target.y, "y":d.target.x}; })
    .projection(function(d) { return [d.y, d.x]; });
    .attr("width", sankey.nodeWidth())
var path = d3.svg.diagonal()
    .source(function(d) {
        return {"x":d.source.y + d.source.dy / 2,
                "y":d.source.x + sankey.nodeWidth()/2};
    })            
    .target(function(d) {
        return {"x":d.target.y + d.target.dy / 2,
                "y":d.target.x + sankey.nodeWidth()/2};
    })
    .projection(function(d) { return [d.y, d.x]; });
结果如下:

步骤5 差不多完成了。在上一张图中,仍然困扰我的是节点标签的位置。如果圆较大,它将与其标签重叠。我在最后一个版本中修复了这个问题。结果是:


这里是

分享你的代码会让别人更容易帮助你。很棒的解释。这正是我想要的。非常感谢,非常感谢。完美答案!解释很清楚,非常感谢!在您所做的节点大小缩减中只有一个小问题:dragmove仍然根据原始节点大小进行操作(即约束)。特别是在node7上。我试图破解它,但没有成功。你对此有什么想法吗?