【问题标题】:d3.js - how to automatically calculate arc lengths in radial dendrogramd3.js - 如何自动计算径向树状图中的弧长
【发布时间】:2012-10-28 11:38:21
【问题描述】:

我正在创建 Mike Bostock 的分层边缘捆绑图的修改版本:

http://mbostock.github.com/d3/talk/20111116/bundle.html

但我想制作跨越某些数据组的弧线,如下所示:

我目前只是对弧的长度进行硬编码,但我想动态地进行。我怎样才能做到这一点?这是我当前的代码:

/* MH - USER DEFINED VARIABLES */
var chartConfig = { "Tension" : .85, "canvasSize" : 800, "dataFile" : "../data/projects.json", "linePadding" : 160, "textPadding" : 30, "arcPadding" : 5, "arcWidth" : 30 }
var pi = Math.PI;

var radius = chartConfig.canvasSize / 2,
    splines = [];

var cluster = d3.layout.cluster() //Cluster is the diagram style, a node to link dendrogram dendrogram (tree diagram)
    .size([360, radius - chartConfig.linePadding]); //MH - sets the size of the circle in relation to the size of the canvas

var bundle = d3.layout.bundle(); //Bundles the node link lines so that they spread at the end but keep close initially

var arcInner = radius - chartConfig.linePadding + chartConfig.arcPadding;
var arcOuter = arcInner + chartConfig.arcWidth;
var arc = d3.svg.arc().innerRadius(arcInner).outerRadius(arcOuter);

var line = d3.svg.line.radial()
    .interpolate("bundle")
    .tension(chartConfig.Tension) //How tightly to bundle the lines. No tension creates straight lines
    .radius(function(d) { return d.y; })
    .angle(function(d) { return d.x / 180 * Math.PI; });

var vis = d3.select("#chart").append("svg")
    .attr("width", radius * 2)
    .attr("height", radius * 2)
    .attr("class","svg")
    .append("g")
    .attr("class","chart")
    .attr("transform", "translate(" + radius + "," + radius + ")");


d3.json(chartConfig.dataFile, function(classes) {
  var nodes = cluster.nodes(packages.root(classes)),
      links = packages.imports(nodes),
      splines = bundle(links);

  var path = vis.selectAll ("path.link")
      .data(links)
      .enter().append("path")
      .attr("class", function(d){ return "link source-" + d.source.key + " target-" + d.target.key; })
      .attr("d", function(d,i){ return line(splines[i]); });

  vis.selectAll("g.node")
      .data(nodes.filter(function(n) { return !n.children; }))
    .enter().append("g")
      .attr("class", "node")
      .attr("id",function(d){ return "node-" + d.key; })
      .attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; })
    .append("text")
      .attr("dx", function(d) { return d.x < 180 ? chartConfig.textPadding : -chartConfig.textPadding; }) //dx Moves The text out away from the lines in a positive or negative direction, depending on which side of the axis it is on
      .attr("dy", ".31em") //moves the text up or down radially around the circle
      .attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
      .attr("transform", function(d) { return d.x < 180 ? null : "rotate(180)"; })
      .text(function(d) {
        textString = d.key;
        textString = textString.split('_').join(' '); //MH replace underscores with spaces
        return textString; 
      })
      .on("mouseover",textOver)
      .on("mouseout",textOut);

});


/* ARCS ARE HARDCODED, SHOULD BE DYNAMIC */

var arcData = [
  {aS: 0, aE: 45,rI:radius - chartConfig.linePadding + chartConfig.arcPadding,rO:radius - chartConfig.linePadding + chartConfig.textPadding-chartConfig.arcPadding}
];

var arcJobsData = d3.svg.arc().innerRadius(arcData[0].rI).outerRadius(arcData[0].rO).startAngle(degToRad(1)).endAngle(degToRad(15));
var g = d3.select(".chart").append("svg:g").attr("class","arcs");
var arcJobs = d3.select(".arcs").append("svg:path").attr("d",arcJobsData).attr("id","arcJobs").attr("class","arc");
g.append("svg:text").attr("x",3).attr("dy",15).append("svg:textPath").attr("xlink:href","#arcJobs").text("JOBS").attr("class","arcText"); //x shifts x pixels from the starting point of the arc. dy shifts the text y units from the top of the arc

... 

function degToRad(degrees){
  return degrees * (pi/180);
}

function updateNodes(name,value){
  return function(d){
    if (value) this.parentNode.appendChild(this);
    vis.select("#node-"+d[name].key).classed(name,value);
  }
}

【问题讨论】:

  • 嗨,我正在尝试做同样的事情......我只是好奇你是否找到了解决方案?或者你知道源代码在哪里?我想我在某个地方看到过,但现在找不到了……谢谢 :)
  • 不,但我保证如果你也找到了东西,我会通知你!
  • 你能设置一个 jsFiddle 或Tributary 我认为一个聪明的嵌套应用程序和一个规模将填补你正在寻找的作品。我很高兴看到我能做些什么,但数据会有所帮助!
  • 这是一个 js fiddle,但由于某种原因它没有绘制包:jsfiddle.net/QNtHr。您可以在此处查看工作示例:mikeheavers.com/transfers/projects/html。如您所见,弧线目前是硬编码的,但它们需要是动态的。

标签: javascript svg d3.js dendrogram bundle-layout


【解决方案1】:

我在这里看到了你的 json 数据结构:http://mikeheavers.com/transfers/projects/data/projects.json。首先,为了对数据进行分组并正确附加标签,最好将数据更改为:https://raw.github.com/gist/4172625/4de3e6a68f9721d10e0068d33d1ebb9780db4ae2/flare-imports.json 以创建分层结构。

然后我们可以使用这些组来绘制弧线。

首先,我们通过“selectAll”创建组并过滤您的节点。您可以在此处添加数据的其他组名:

var groupData = svg.selectAll("g.group")
  .data(nodes.filter(function(d) {return (d.key=='Jobs' || d.key == 'Freelance' || d.key == 'Bayard') && d.children; }))
.enter().append("group")
  .attr("class", "group");

我只是在我的情况下检查过,因此您最好验证过滤器的结果并根据您的情况进行更改(我们的数据结构有点不同)。

现在我们得到了一个组列表。然后我们将遍历每个组的孩子,并选择最小和最大的 x 作为开始和结束角度。我们可以这样创建一个函数:

function findStartAngle(children) {
    var min = children[0].x;
    children.forEach(function(d){
       if (d.x < min)
           min = d.x;
});
return degToRad(min);
}

同样的 findEndAngle 函数,将 min 替换为 max。然后我们可以创建弧的格式:

var groupArc = d3.svg.arc()
  .innerRadius(arcData[0].rI)
  .outerRadius(arcData[0].rO)
  .startAngle(function(d){return findStartAngle(d.children);})
  .endAngle(function(d){return findEndAngle(d.children);});

然后我们可以以“动态”的方式创建弧:

svg.selectAll("g.arc")
  .data(groupData[0])
.enter().append("arc")
  .attr("d", groupArc)
  .attr("class", "arc")
    .append("svg:text")
      ...;

在我的情况下,它是 groupData[0],也许你应该在你的情况下检查它。 要为弧添加标签,您只需根据选择的结果添加 d.key 或 d.name。

完整代码可在此处获得:https://gist.github.com/4172625。每次我从数据库中获取 json 时,如果没有动态的方法来通用弧,我会死的:P 希望它对你有帮助!

【讨论】:

  • 在您上面的findStartAngle(d.children)findEndAngle(d.children) 中,d 是在哪里定义的?
  • @mccannf 对不起,我错了,应该是function(d){return findStartAngle(d.children);}
  • 这看起来很棒。这个周末我会测试它,如果一切顺利,我会接受你的回答。谢谢!
  • @mheavers 你不会给我你的赏金吗? :)
  • 是的!抱歉 - 很忙,但这是一个很好的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多