2012-10-28 13 views
15

Tworzę zmodyfikowaną wersję hierarchicznej krawędzi Bundling schemacie Mike Bostock za:d3.js - jak automatycznie obliczyć długość łuku w promieniowej dendrogramie

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

ale chcę zrobić łuki, które obejmują pewne grupy danych, na przykład:

enter image description here

jestem obecnie tylko hardcoding długość łuku, ale chcę zrobić to dynamicznie. Jak mogę to zrobić? Oto mój obecny kod:

/* 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

Witam, próbuję zrobić to samo ... Jestem ciekawy, czy znalazłeś rozwiązanie? A może wiesz, gdzie jest kod źródłowy? Chyba gdzieś to widziałem, ale nie mogłem go teraz znaleźć ... Dzięki :) – wceo

+0

Nie, ale obiecuję dać ci znać, jeśli coś znajdę, jeśli zrobisz to samo! – mheavers

+0

możesz ustawić jsFiddle lub [Tributary] (http://enjalot.com/tributary/) Myślę, że sprytne zastosowanie gniazda i skali wypełni poszukiwane elementy. Byłbym szczęśliwy widząc, co mogę zrobić, ale dane pomogłyby! – Superboggly

Odpowiedz

13

Widziałem twoją strukturę danych json tutaj: http://mikeheavers.com/transfers/projects/data/projects.json. Po pierwsze, aby zgrupować dane i poprawnie dołączyć znacznik, lepiej będzie zmienić dane w ten sposób: https://raw.github.com/gist/4172625/4de3e6a68f9721d10e0068d33d1ebb9780db4ae2/flare-imports.json, aby utworzyć hierarchiczną strukturę.

Możemy następnie użyć grup do narysowania łuków.

Najpierw tworzymy grupy według "selectAll" i filtrujemy węzły. Tu można dodać inne nazwy grup danych:

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

Właśnie sprawdziłem, że w moim przypadku, więc lepiej sprawdzić wynik filtra i dokonać zmian w zależności od przypadku (nasza struktura danych jest trochę inaczej).

Teraz mamy listę grup. Następnie przejdziemy przez dzieci z każdej grupy i wybierz najmniejszy i największy x jako kąt początkowy i końcowy. Możemy utworzyć funkcję podobną do tej:

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

I podobnie funkcja findEndAngle zastępując min. Następnie możemy stworzyć formatu łuków:

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

wtedy możemy tworzyć łuki w «dynamicznej» sposób:

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

W moim przypadku jest to groupData [0], być może powinieneś sprawdzić w Twój przypadek. Aby dodać znaczniki do łuków, wystarczy dodać d.key lub d.name zgodnie z wynikiem wyboru.

the arc example

pełny kod jest dostępna tutaj: https://gist.github.com/4172625. Za każdym razem, gdy dostaję json z bazy danych, więc jeśli nie ma dynamicznego sposobu na generyczne łuki, będę martwy: P Mam nadzieję, że to ci pomoże!

+0

W twoim 'findStartAngle (zm.dzieci) 'i' findEndAngle (d.children) 'powyżej, gdzie zdefiniowano' d'? – mccannf

+0

@mccannf Przepraszamy, mój błąd, powinien to być 'function (d) {return findStartAngle (d.children);}' – wceo

+0

To wygląda świetnie. Przetestuję to w ten weekend i zaakceptuję twoją odpowiedź, jeśli wszystko pójdzie dobrze. Dzięki! – mheavers