simouns
simouns

Reputation: 25

d3-js › Centroid issue in a pie chart

I'm relatively new to D3.js

I'm trying to stick a legendLabel to the edge of each arc slice of a Pie Chart.

I'm using the centroid(d,i) function to achieve that. The issue is that the first 2 labels angle are totally not "centered" like the others, and I cant figure out why...

Thanks for taking the time to reply

Here is a jsFiddle that shows my issue : http://jsfiddle.net/simouns/y7tq50o7/1/

HTML:

<div id="MyGraph"></div>

JS :

      var myData = [{"label": "Label not ok", "importance": 100, "color": "#FF5256"},
                {"label": "Label not ok", "importance": 100, "color": "#FF5256"},
                {"label": "Label ok", "importance": 100, "color": "#FF5256"},
                {"label": "Label ok", "importance": 100, "color": "#B9A031"},
                {"label": "Label ok", "importance": 100, "color": "#B9A031"},
                {"label": "Label ok", "importance": 100, "color": "#B9A031"},
                {"label": "Label ok", "importance": 100, "color": "#00566D"},
                {"label": "Label ok", "importance": 100, "color": "#00566D"},
                {"label": "Label ok", "importance": 100, "color": "#00566D"},
                {"label": "Label ok", "importance": 100, "color": "#73C5BF"},
                {"label": "Label ok", "importance": 100, "color": "#73C5BF"},
                {"label": "Label ok", "importance": 100, "color": "#73C5BF"},
                {"label": "Label ok", "importance": 100, "color": "#a74116"},
                {"label": "Label ok", "importance": 100, "color": "#a74116"},
                {"label": "Label ok", "importance": 100, "color": "#a74116"},
                {"label": "Label ok", "importance": 100, "color": "#86338F"},
                {"label": "Label ok", "importance": 100, "color": "#86338F"},
                {"label": "Label ok", "importance": 100, "color": "#86338F"}
        ];

  var outlineColor = [ {"color": "#FF5256"} , 
                      {"color": "#B9A031"} , 
                      {"color": "#00566D"} , 
                      {"color": "#73C5BF"} , 
                      {"color": "#CCCDCF"} , 
                      {"color": "#86338F"}
                     ];
      var angleColor = (Math.PI * 2) / 6 ; // calculate the radian angle applied to each slice





       /**Canvas**/
                    var nbElement = myData.length;
                    var angle = (Math.PI * 2) / nbElement ; // calculate the radian angle applied to each slice

                var width = 650;
                var height = 340;
                var r = 150;  //radius



                var canvas = d3.select("#MyGraph")
                        .append("svg") //create the SVG element inside the <MoodsGraph>
                    /*.data([myData]) */  //associate the data with the document // see var arcs !!!
                    .attr("height", height)
                    .attr("width", width);


            /**Canvas**/




            /**Background - Circles**/


        var circle1 = canvas.append("circle")
                .attr("cx" , 330)
                .attr("cy" , 155)
                .attr("r" , 30)
                .attr("fill","rgba(138, 138, 138, 0.5)");
        var circle2 = canvas.append("circle")
                .attr("cx" , 330)
                .attr("cy" , 155)
                .attr("r" , 60)
                .attr("fill","rgba(138, 138, 138, 0.3)");
        var circle3 = canvas.append("circle")
                .attr("cx" , 330)
                .attr("cy" , 155)
                .attr("r" , 120)
                .attr("fill","rgba(138, 138, 138, 0.2)");
        /**Background - Circles**/



        /** Pie Chart - Dash **/

    var group = canvas.append("g") //make a group to hold the pie chart
              .attr("transform","translate(330, 155)");



  var arc = d3.svg.arc()//  This will create <path> elements for us using arc data...
          .innerRadius(0)     
          .outerRadius(function (d,i) { return (d.data.importance*1.5); })
          .startAngle(function (d,i) { return (i*angle);})
          .endAngle(function (d,i) { return (i*angle)+(1*angle); });


  var pie = d3.layout.pie() //this will create arc data for us given a list of values
        .value(function (d) {/*console.log(d);*/ return d.importance; })  // Binding each value to the pie
        .sort( function(d) { return null; } );

  var arcs = group.selectAll(".slice")
        .data(pie(myData)) //associate the data with the pie
        .enter()
        .append("g")
        .attr("class", "slice");

  arcs.append("path")
      .attr("fill", function (d, i) {  return d.data.color; })
      .style("opacity", "0.5")
      .attr("d", arc); //this creates the actual SVG path using the associated data (pie) with the arc drawing function



  arcs.append("text")
      .attr("transform", function(d,i) { //set the label's origin to the center of the arc
          var centered = arc.centroid(d,i); 
          return "translate(" + centered[0]*1.6 +","+ centered[1]*1.6 + ")rotate(" + setAngle(d) + ")"; 
          })
      .attr("text-anchor", "middle")
       .attr("dy", ".35em")
       .style("fill", "White")
       .style("font", "bold 12px Arial")
      .text(function(d) { return d.data.label; });


  // Computes the angle of an arc, converting from radians to degrees.
    function setAngle(d) {
      var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90;

      return a > 90 ? a - 180 : a;
    }

/** Pie Chart - Dash **/



/** Inline - Dash **/
var inlines = d3.svg.arc()
        .innerRadius(0)
        .outerRadius(r)
        .startAngle(function (d,i) { return (i*angle); })
    .endAngle(function (d,i) { return (i*angle)+(1*angle); });

var outerPath = group.selectAll(".inlines")
      .data(pie(myData))
      .enter().append("path")
      .attr("stroke", "black")
      .attr("stroke-width", "2")
      .style("stroke-dasharray", ("6"))
      .attr("fill", "none")
      .attr("class", "inlines")
      .attr("d", inlines);  
/** Inline - Dash **/



/** Outline - Arc **/

var outlineArc = d3.svg.arc()
        .innerRadius(0)
        .outerRadius(r)
        .startAngle(function (d,i) { return (i*angleColor); })
    .endAngle(function (d,i) { return (i*angleColor)+(1*angleColor); });

var outerPath = group.selectAll(".outlineArc")
      .data(pie(outlineColor))
    .enter().append("path")
      .attr("stroke", function(d,i){ return d.data.color;})
      .attr("stroke-width", "3")
      .attr("fill", "none")
      .attr("class", "outlineArc")
      .attr("d", outlineArc);  



/** Outline - Arc **/

Upvotes: 1

Views: 624

Answers (1)

Eric Lease
Eric Lease

Reputation: 4194

The issue is with the pie.sort comparator that you specify...

  var pie = d3.layout.pie() //this will create arc data for us given a list of values
    .value(function (d) {/*console.log(d);*/ return d.importance; })  // Binding each value to the pie
    .sort( function(d) { return null; } );

You want a null value for the sort comparator, not a function that returns null. Using a function that returns null ends up garbling the sorted list, giving you {9, 0, 2, 3, 5, 6, 7, 8, 1, 10, 11, 12, 13, 14, 15, 16, 17}, thus the angles are correct, but the order is wrong.

So use... .sort(null)


To see what's going on...

  1. Download the D3 source, and modify your <script> src value to point to the non-minified version (so you can step through it if you want).

  2. Modify the declaration of pie.sort as follows:

                .sort( function(a, b) { 
                   return null; 
                } );
    
  3. Open a modern web browser, open the JS console, locate the source and insert a breakpoint at the return null line (above).

  4. Press F5 to reload the page and hit the breakpoint - observe this is just a standard sort function that takes two parameters for comparison and should work like this (but apparently returns the inverse of the traditional sort return values...)

    function pie(data) {
            var n = data.length, values = data.map(function(d, i) {
                    return +value.call(pie, d, i);
            }), a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle), da = (typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - a, p = Math.min(Math.abs(da) / n, +(typeof padAngle === "function" ? padAngle.apply(this, arguments) : padAngle)), pa = p * (da < 0 ? -1 : 1), k = (da - n * pa) / d3.sum(values), index = d3.range(n), arcs = [], v;
            if (sort != null) 
                    index.sort(sort === d3_layout_pieSortByValue 
                            ? function(i, j) {
                                    return values[j] - values[i];
                            } : function(i, j) {
                                    return sort(data[i], data[j]);
                            });
    

Upvotes: 3

Related Questions