2012-10-28 11 views
15

Sto creando una versione modificata del diagramma gerarchico bundling bordo di Mike Bostock:d3.js - come calcolare automaticamente le lunghezze d'arco in dendrogramma radiale

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

ma voglio fare archi che si estendono su certi gruppi dei dati, come questo:

enter image description here

attualmente sto solo hardcoding la lunghezza dell'arco, ma voglio farlo in modo dinamico. Come posso realizzare questo? Ecco il mio codice corrente:

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

Ciao, sto cercando di fare la stessa cosa ... sono curioso di sapere se hai trovato la soluzione? O sai dov'è il codice sorgente? Penso di averlo visto da qualche parte, ma non riuscivo a trovarlo ora ... Grazie :) – wceo

+0

No, ma ti prometto di farti sapere se trovo qualcosa se fai lo stesso! – mheavers

+0

puoi configurare un jsFiddle o [Tributario] (http://enjalot.com/tributary/) Penso che un'applicazione intelligente del nido e una scala riempiranno i pezzi che stai cercando. Sarei felice di vedere cosa posso fare, ma i dati potrebbero aiutare! – Superboggly

risposta

13

Ho visto la tua struttura dati JSON qui: http://mikeheavers.com/transfers/projects/data/projects.json. In primo luogo, per raggruppare i dati e aggiungere correttamente il tag, sarà meglio modificare i dati in questo modo: https://raw.github.com/gist/4172625/4de3e6a68f9721d10e0068d33d1ebb9780db4ae2/flare-imports.json per creare una struttura gerarchica.

Possiamo quindi utilizzare i gruppi per disegnare gli archi.

Per prima cosa creiamo i gruppi per "selectAll" e filtriamo i nodi. Qui si potrebbe aggiungere altri nomi di gruppi di dati:

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

ho appena controllato che nel mio caso, quindi è meglio verificare il risultato del filtro e fare il cambiamento in base al vostro caso (la nostra struttura dei dati è un un po 'diverso).

Ora abbiamo un elenco di gruppi. Poi passeremo attraverso i bambini di ciascun gruppo e scegliere la x più piccola e più grande come angolo iniziale e finale. Possiamo creare una funzione come questa:

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

E allo stesso modo una funzione findEndAngle sostituendo min max. Poi possiamo creare formato archi:

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

Poi possiamo creare archi in modo "dinamico":

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

Nel mio caso è groupData [0], forse si dovrebbe verificare in il tuo caso. Per aggiungere tag ad archi è sufficiente aggiungere d.key o d.name in base al risultato della selezione.

the arc example

Il codice completo è disponibile qui: https://gist.github.com/4172625. Ogni volta che prendo JSON dal database, quindi se non c'è un modo dinamico per gli archi generici, sarò morto: P Spero che ti aiuti!

+0

Nel tuo 'findStartAngle (d.bambini) 'e' findEndAngle (d.children) 'sopra, dove è definito' d'? – mccannf

+0

@mccannf Spiacente, mio ​​errore, dovrebbe essere 'function (d) {return findStartAngle (d.children);}' – wceo

+0

Questo sembra fantastico. Lo proverò questo fine settimana e accetterò la tua risposta se tutto andrà bene. Grazie! – mheavers