2013-01-11 24 views
13

Etiketleri. Ben D3'te yeniyim ve bunun mümkün olduğundan bile emin değilim? Bir grafiğin etiketlerini başka bir şekilde kullanabilir misiniz? Eğer yapabiliyorsan, bana bir örnek gösterebilir misin?D3 - Pasta Grafik & Kuvveti Yönlendirilmiş G D3 kullanarak kayan etiketle bir pasta grafik oluşturmak için arıyorum

kısa Açıklama: Bir pasta grafiği üzerinde olması http://bl.ocks.org/1691430

enter image description here ...: Ben etiketleri istiyorum.

İşte aşağıda koşuyordu kod: Veya JSBIN içinde: http://jsbin.com/awilak/1/edit

Ben doğru onun kodu anlamak, bu etiketleri ekler bölümdür. LabelForce.update'in ne yaptığını anlamıyorum. Oradan, geçiş umurumda değil, bu yüzden çizgi gerekli değil. O zaman kalanlar sadece çevreleri çiziyor ve bir link/hat ekliyor mu? Eğer birisi bunu entegre edebilirse, bu harika olurdu ama ne olup bittiğini ve neyi kaçırdığımı anlamama yardım ederseniz minnettar olacağım.

// Now for the labels 
// This is the only function call needed, the rest is just drawing the labels 
anchors.call(labelForce.update) 

labels = svg.selectAll(".labels") 
    .data(data, function(d,i) {return i;}) 
labels.exit() 
    .attr("class","exit") 
    .transition() 
    .delay(0) 
    .duration(500) 
    .style("opacity",0) 
    .remove(); 

// Draw the labelbox, caption and the link 
newLabels = labels.enter().append("g").attr("class","labels") 

newLabelBox = newLabels.append("g").attr("class","labelbox") 
newLabelBox.append("circle").attr("r",11) 
newLabelBox.append("text").attr("class","labeltext").attr("y",6) 
newLabels.append("line").attr("class","link") 

labelBox = svg.selectAll(".labels").selectAll(".labelbox") 
links = svg.selectAll(".link") 
labelBox.selectAll("text").text(function(d) { return d.num}) 
} 

<!DOCTYPE html> 
<html> 
<head>  
    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> 
    <title>Testing Pie Chart</title> 
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?2.1.3"></script> 
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.geom.js?2.1.3"></script> 
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js?2.1.3"></script> 

    <style type="text/css"> 
    .slice text { 
     font-size: 16pt; 
     font-family: Arial; 
    } 
    </style> 
</head> 
<body> 
    <button id="button"> Test </button> 
    <br> 
    <form id="controls"> 
     <div> 
      <h2>Y axis</h2> 
      <ul id="y-axis"> 
       <li><label><input checked="checked" type="radio" name="y-axis" value="Component">Component</label></li> 
       <li><label><input type="radio" name="y-axis" value="Browser">Browser</label></li> 
       <li><label><input type="radio" name="y-axis" value="Version">Version</label></li> 
      </ul> 
     </div> 
    </form> 
    <script type="text/javascript"> 
    // return a list of types which are currently selected 
    function plottableTypes() { 
     var types = [].map.call (document.querySelectorAll ("#coaster-types input:checked"), function (checkbox) { return checkbox.value;}); 
     return types; 
    } 


    var w = 600,      //width 
    h = 600,       //height 
    r = 100, 
    r2 = 200,       //radius 
    axis = getAxis(),     //axes 
    color = d3.scale.category20c();  //builtin range of colors 

    data = [ 
     {"Browser":"Internet Explorer ","Version":"8.0","Toatl":2000,"Component":"6077447412293130422"}, 
     {"Browser":"Internet Explorer ","Version":"9.0 ","Toatl":1852,"Component":"6077447412293130422"}, 
     {"Browser":"Internet Explorer ","Version":"6.0 ","Toatl":1754,"Component":"6077447412293130422"}, 
     {"Browser":"Firefox ","Version":"16.0 ","Toatl":1020,"Component":"6077447412293130422"}, 
     {"Browser":"Chrome ","Version":"23.0 ","Toatl":972,"Component":"6077447412293130422"}, 
     {"Browser":"Internet Explorer ","Version":"7.0 ","Toatl":700,"Component":"6077447412293130422"}, 
     {"Browser":"Mobile Safari ","Version":"6.0 ","Toatl":632,"Component":"6077447412293130422"}, 
     {"Browser":"BOT ","Version":"BOT ","Toatl":356,"Component":"6077447412293130422"}, 
     {"Browser":"Firefox ","Version":"8.0 ","Toatl":196,"Component":"6077447412293130422"}, 
     {"Browser":"Mobile Safari ","Version":"5.1 ","Toatl":184,"Component":"6077447412293130422"} 
    ]; 

    var vis = d3.select("body") 
     .append("svg:svg")    //create the SVG element inside the <body> 
     .data([data])     //associate our data with the document 
     .attr("width", w)   //set the width and height of our visualization (these will be attributes of the <svg> tag 
     .attr("height", h) 
     .append("svg:g")    //make a group to hold our pie chart 
     .attr("transform", "translate(" + r2 + "," + r2 + ")") //move the center of the pie chart from 0, 0 to radius, radius 

    var arc = d3.svg.arc()    //this will create <path> elements for us using arc data 
     .outerRadius(r); 


    var pie = d3.layout.pie()   //this will create arc data for us given a list of values 
     .value(function(d) { return d.Toatl; }); //we must tell it out to access the value of each element in our data array 

    var arcs = vis.selectAll("g.slice")  //this selects all <g> elements with class slice (there aren't any yet) 
     .data(pie)       //associate the generated pie data (an array of arcs, each having startAngle, endAngle and value properties) 
     .enter()       //this will create <g> elements for every "extra" data element that should be associated with a selection. The result is creating a <g> for every object in the data array 
     .append("svg:g")    //create a group to hold each slice (we will have a <path> and a <text> element associated with each slice) 
     .attr("class", "slice"); //allow us to style things in the slices (like text) 


    arcs.append("svg:path") 
     .attr("fill", function(d, i) { return color(i); }) //set the color for each slice to be chosen from the color function defined above 
     .attr("d", arc);         //this creates the actual SVG path using the associated data (pie) with the arc drawing function 


    arcs.append("svg:text")          //add a label to each slice 
     .attr("transform", function(d) {     //set the label's origin to the center of the arc 
      //we have to make sure to set these before calling arc.centroid 
      d.innerRadius = r2; 
      d.outerRadius = r; 
      return "translate(" + arc.centroid(d) + ")";  //this gives us a pair of coordinates like [50, 50] 
     }) 
     .attr("text-anchor", "middle")       //center the text on it's origin 
     .text(function(d, i) { 
      if(axis.yAxis == "Component"){ 
       return data[i].Component; 
      } 
      return data[i].Browser;  //get the label from our original data array 
     });  

     d3.select('#button').on('click', reColor); 

     var arcOver = d3.svg.arc() 
      .outerRadius(r + 30) 
      .innerRadius(0); 
     var arc = d3.svg.arc() 
      .outerRadius(r) 
      .innerRadius(0); 

     var arcs = vis.selectAll("g.slice") 
      .attr("class", "slice") 
      .on("mouseover", function(d) { 
       getAxis(); 
       d3.select(this) 
        .select("path") 
        .transition() 
        .duration(500) 
       .attr("d", arcOver); 
       d3.select(this).select("text") 
        .text(function(d, i) { 
         if(axis.yAxis == "Component"){ 
          return data[i].Component; 
         } 
        return data[i].Browser;  //get the label from our original data array 
       });  
      }) 
      .on("mouseout", function(d) { 
       getAxis(); 
       d3.select(this) 
        .select("path") 
        .transition() 
        .duration(500) 
        .attr("d", arc); 
       d3.select(this) 
        .select("text") 
        .text(function(d, i) { 
         if(axis.yAxis == "Component"){ 
          return data[i].Component; 
         } 
         return data[i].Browser;  //get the label from our original data array 
        }); 
       }); 


     function reColor(){ 
      var slices = d3.select('body').selectAll('path'); 
      slices.transition() 
       .duration(2000) 
       .attr("fill", function(d, i) { return color(i+2); }); 
      slices.transition() 
       .delay(2000) 
       .duration(2000) 
       .attr("fill", function(d, i) { return color(i+10); }) 
     } 
     function makeData(){ 

     } 
     // return an object containing the currently selected axis choices 
     function getAxis() { 
      var y = document.querySelector("#y-axis input:checked").value; 
      return { 
       yAxis: y, 
      }; 
     } 
     function update() { 
      axis = getAxis() 
      arcs.selectAll("text")   //add a label to each slice    
       .text(function(d, i) { 
        if(axis.yAxis == "Component"){ 
         return data[i].Component; 
        } 
        return data[i].Browser;  //get the label from our original data array 
       }); 
      } 

     document.getElementById("controls").addEventListener ("click", update, false); 
     document.getElementById("controls").addEventListener ("keyup", update, false); 
    </script> 
</body> 
</html> 
+0

Evet, bu mümkün. Gönderdiğiniz örnek, yapmak istediğinizi düşündüğüm kadar yakın. Denediğin şeyin kodunu yazabilir misin lütfen? –

+0

Etiketleri ne zaman aramam gerektiği konusunda kafam karıştı, yukarıdaki pasta grafiğimde düzenlerim. Tam olarak ne çağrı (labelForce.update) nedir? Yardımlarınız için teşekkürler! – gbam

+1

Örnekte “ankrajlar” etiketli olanlardır. Bunlar senin dilim parçaların olurdu. Başlangıçta pasta grafiğini bağımsız olarak çizmek ve pasta segmentlerindeki bir yerdeki noktalar için olduğu gibi verileri örnekte olduğu gibi kullanmak daha kolay olabilir. –

cevap

-1

İki yay oluşturmalısınız. Pasta grafik çizimi için bir tane, ve etiketlerin oturması için büyük olanı.

// first arc used for drawing the pie chart 
var arc = d3.svg.arc() 
    .outerRadius(radius - 10) 
    .innerRadius(0); 

// label attached to first arc 
g.append("text") 
    .attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; }) 
    .attr("dy", ".35em") 
    .style("text-anchor", "middle") 
    .text(function(d) { return d.data.age; }); 

// second arc for labels 
var arc2 = d3.svg.arc() 
    .outerRadius(radius + 20) 
    .innerRadius(radius + 20); 

// label attached to second arc 
g.append("text") 
    .attr("transform", function(d) { return "translate(" + arc2.centroid(d) + ")"; }) 
    .attr("dy", ".35em") 
    .style("text-anchor", "middle") 
    .text(function(d) { return d.data.age; }); 
0

Evet, kesinlikle kuvvet etiketlerini bir pasta grafikle birleştirebilirsiniz! Başladığınız pasta grafik etiketleri hakkında özel bir şey yoktur, bunlar yalnızca dönüşüm veya x/y kullanarak başka herhangi bir şey gibi konumlandırılabilen metin öğeleridir. Bu etiketleri başlangıçta etiketledikleri yayların merkezlerine göre konumlandırıyormuş gibi görüyorsunuz, ancak başka bir kriteri (bir güç düzeninin çıktısı gibi) kolayca kullanabilirsiniz.

D3 kuvvet düzeni giderilen sorunlar hakkında kısıtlamaları kümesi temelinde şeyler için pozisyonlarını hesaplar ne hareketli olduğunu ve hangilerinin bağlanır. Ahmet'in bl.ocks örnekten labelForce.update yöntem sabit "çapa" noktalarıdır nerede konumlandırılmış olması gerekir ve kaç nesneleri hakkında kuvvet düzenini bilgilendirmek için kullanılıyor. Daha sonra diyagram veri modeline etiketler için hesaplanan pozisyonları kaydeder ve bunlar redrawLabels işlevi daha sonra kullanılır. başkalarının giriş-sonrası için yorum belirtildiği gibi

2

bunu niteleyen gibi bir çözüm elde etmek mümkündür ve bunun kodunuzu artı "hareketli-etiket" -example parçalarını kullanarak mümkündür. Eğer sizi doğru anlarsam, güç düzenini kullanarak örtüşmeyen etiketler elde etmek istersiniz, ki bu da daha önce hiç rastlamadığım güzel bir fikir.

örneğinden yapıştırdığınız kod parçası, etiketlerini ve satırları doğru şekilde açıkladığınız gibi çizer. Bir sonraki adım yeniden düzenleyin, etiketleri pasta grafiğinizin çevresindeki kuvvet benzeri bir düzende.

function redrawLabels() { 
    labelBox 
     .attr("transform",function(d) { return "translate("+d.labelPos.x+" "+d.labelPos.y+")"}) 

    links 
     .attr("x1",function(d) { return d.anchorPos.x}) 
     .attr("y1",function(d) { return d.anchorPos.y}) 
     .attr("x2",function(d) { return d.labelPos.x}) 
     .attr("y2",function(d) { return d.labelPos.y}) 
}   

// Initialize the label-forces 
labelForce = d3.force_labels() 
    .linkDistance(0.0) 
    .gravity(0) 
    .nodes([]).links([]) 
    .charge(-60) 
    .on("tick",redrawLabels) 

fonksiyon etiket ve birbirlerine göre durumunu değiştirir biridir:

örnekte etiket (ve bağlantılar) yeniden düzenler bölümü aşağıda. Kuvvet, D3 ile hesaplanır ve d3.force_labels()... ile başlatılır. Gördüğünüz gibi, kene olayları için bir etkinlik işleyicisi olarak atanır. Başka bir deyişle: Kuvveti hesaplamanın her adımından sonra, D3 her etiket için 'çekmeceyi' çağırır ve konumları günceller.

Maalesef, D3'ün force_labels() yöntemine aşina değilim, ancak normal olarak force() gibi çalışır. Bir çapa, sizin durumunuzda, her bir etiket için her bir turta parçasına yerleştirilir. Her turta parçasının içinde daha ortalanmış (pasta kendisi değil), daha iyi. Ne yazık ki, bu ankraj pozisyonunu bir şekilde hesaplamak zorundasınız (günah ve cos şeyleri) ve hat sonlarını redrawLabels() içinde bu sabit konuma ayarlayın.

Bunu yaptıktan sonra ilk sonucu göreceksiniz. İyi sonuçlar elde etmek için kuvvetin yerçekimi, linkDistance vb değerleri ile oynamak zorunda kalabilirsiniz. (Yani örnekteki silders bunu yapar.)

fazla bilgi için d3 dokümanlar bakın: https://github.com/mbostock/d3/wiki/Force-Layout Sonra

belki etiketler üzerine gelmeden ancak bazı garip sırayla pasta etrafında sıralanır sorun takılmaları . Bunu, etiketleri başlangıçta panelin etrafında rasgele yerleştirmek yerine, pastanın etrafındaki büyük bir daireye doğru sırayla yerleştirerek çözebilirsiniz, bu da sorunun nedenidir. Bu şekilde daha az titreme ve yanlış yerleştirmeler yaşayacaksınız.

başka bir blok örnekte anlatılan fikir: Bu örnekte http://bl.ocks.org/mbostock/7881887

, düğümler ilk sanal çember üzerine yerleştirilir. konumlandırma aşağıdaki işlevleri ile hesaplanır:

x: Math.cos (I/m * 2 * Math.PI) * 200 + genişlik/2 + Math.random(),

y: Math. sin (i/m * 2 * Math.PI) * 200 + yükseklik/2 + Math.random()

Çizim panelinin ortasındaki 200'lük bir yarıçapa sahip bir daireyi temsil ederler. Daire, eşit büyüklükteki m parçalara bölünmüştür. i/m sadece 0 ile m-1 arasında değişen 'parça konumlarını' hesaplar.

Umarım yardımcı olabilirim!

İlgili konular