2012-10-28 87 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