2012-10-28 47 views
15

我创建迈克·博斯托克的层次边缘捆绑图的修改版本:d3.js - 如何自动计算弧长径向树状

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

,但我想让它跨越某些群体弧的数据,这样的:

enter image description here

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

/* 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); 
    } 
} 
+0

嗨,我试图做同样的事情......我只是好奇,如果你找到了解决方案?或者你知道源代码可能在哪里?我想我已经看到了它的某个地方,但现在找不到它......谢谢:) – wceo

+0

不 - 但我保证让你知道,如果我找到了一些东西,如果你这样做! – mheavers

+0

你可以设置一个jsFiddle或[支流](http://enjalot.com/tributary/)我认为一个聪明的应用程序的巢和一个比例将填补你正在寻找的作品。我很乐意看到我能做什么,但数据会有所帮助! – Superboggly

回答

13

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

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

首先我们通过“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。然后,我们可以创建弧格式:

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

在我的情况下,它groupData [0],也许你应该看看在:

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);}); 

然后我们就可以在‘动态’的方式来创建圆弧你的情况。 为了给弧添加标签,您只需根据选择的结果添加d.key或d.name。

the arc example

完整的代码可以在这里找到:https://gist.github.com/4172625。每当我从数据库中获取json时,如果没有动态方法来处理泛型弧,我将会死掉:P希望它对你有所帮助!

+0

在你的'findStartAngle(d。儿童)'和'findEndAngle(d.children)',在哪里定义了'd'? – mccannf

+0

@mccannf对不起,我的错误,应该是'function(d){return findStartAngle(d.children);}' – wceo

+0

这看起来不错。我会在本周末测试它,如果一切顺利,请接受你的回答。谢谢! – mheavers