Search code examples
d3.jschartsdonut-chart

Multiple ring donut chart using D3js v4


I am trying to get this donut charts with multiple ring working . This is using d3js v4. This is a continuation from another post where this was working in v3(multi-ring pie chart using d3js). The path created always shows up M0,0Z . Could anyone let me know where is this going wrong. I also get Expected lenght NaN in the console.

var dataset = [{
  "name": "Population Quater",
  "code": "POP_QUATER",
  "parent": "POP_BY_QUAT",
  "children": [{
    "name": "POP_CYQ1",
    "code": "POP_CYQ1",
    "parent": "POP_QUATER",
    "value": "6772",
    "label": "CYQ1",
    "children": []
  }, {
    "name": "POP_CYQ2",
    "code": "POP_CYQ2",
    "parent": "POP_QUATER",
    "value": "6716",
    "label": "CYQ2",
    "children": []
  }, {
    "name": "POP_CYQ3",
    "code": "POP_CYQ3",
    "parent": "POP_QUATER",
    "value": "6714",
    "label": "CYQ3",
    "children": []
  }, {
    "name": "POP_CYQ4",
    "code": "POP_CYQ4",
    "parent": "POP_QUATER",
    "value": "6703",
    "label": "CYQ4",
    "children": []
  }, {
    "name": "POP_LYQ1",
    "code": "POP_LYQ1",
    "parent": "POP_QUATER",
    "value": "6721",
    "label": "LYQ1",
    "children": []
  }, {
    "name": "POP_LYQ2",
    "code": "POP_LYQ2",
    "parent": "POP_QUATER",
    "value": "6671",
    "label": "LYQ2",
    "children": []
  }, {
    "name": "POP_LYQ3",
    "code": "POP_LYQ3",
    "parent": "POP_QUATER",
    "value": "6708",
    "label": "LYQ3",
    "children": []
  }, {
    "name": "POP_LYQ4",
    "code": "POP_LYQ4",
    "parent": "POP_QUATER",
    "value": "6734",
    "label": "LYQ4",
    "children": []
  }]
}, {
  "name": "Transient Pop",
  "code": "TRANSIENT_POP",
  "parent": "POP_BY_QUAT",
  "label": "Transient Pop",
  "children": [{
    "name": "TRANSIENT_LYQ1",
    "code": "TRANSIENT_LYQ1",
    "parent": "TRANSIENT_POP",
    "value": "54",
    "label": "LYQ1",
    "children": []
  }, {
    "name": "TRANSIENT_LYQ2",
    "code": "TRANSIENT_LYQ2",
    "parent": "TRANSIENT_POP",
    "value": "86",
    "label": "LYQ2",
    "children": []
  }, {
    "name": "TRANSIENT_LYQ3",
    "code": "TRANSIENT_LYQ3",
    "parent": "TRANSIENT_POP",
    "value": "219",
    "label": "LYQ3",
    "children": []
  }, {
    "name": "TRANSIENT_LYQ4",
    "code": "TRANSIENT_LYQ4",
    "parent": "TRANSIENT_POP",
    "value": "191",
    "label": "LYQ4",
    "children": []
  }, {
    "name": "TRANSIENT_CYQ1",
    "code": "TRANSIENT_CYQ1",
    "parent": "TRANSIENT_POP",
    "value": "52",
    "label": "CYQ1",
    "children": []
  }, {
    "name": "TRANSIENT_CYQ2",
    "code": "TRANSIENT_CYQ2",
    "parent": "TRANSIENT_POP",
    "value": "91",
    "label": "CYQ2",
    "children": []
  }, {
    "name": "TRANSIENT_CYQ3",
    "code": "TRANSIENT_CYQ3",
    "parent": "TRANSIENT_POP",
    "value": "222",
    "label": "CYQ3",
    "children": []
  }, {
    "name": "TRANSIENT_CYQ4",
    "code": "TRANSIENT_CYQ4",
    "parent": "TRANSIENT_POP",
    "value": "186",
    "label": "CYQ4",
    "children": []
  }]
}, {
  "name": "Seasonal Pop",
  "code": "SEASONAL_POP",
  "parent": "POP_BY_QUAT",
  "label": "Seasonal Pop",
  "children": [{
    "name": "SEASONAL_LYQ1",
    "code": "SEASONAL_LYQ1",
    "parent": "SEASONAL_POP",
    "value": "2",
    "label": "LYQ1",
    "children": []
  }, {
    "name": "SEASONAL_LYQ2",
    "code": "SEASONAL_LYQ2",
    "parent": "SEASONAL_POP",
    "value": "24",
    "label": "LYQ2",
    "children": []
  }, {
    "name": "SEASONAL_LYQ3",
    "code": "SEASONAL_LYQ3",
    "parent": "SEASONAL_POP",
    "value": "152",
    "label": "LYQ3",
    "children": []
  }, {
    "name": "SEASONAL_LYQ4",
    "code": "SEASONAL_LYQ4",
    "parent": "SEASONAL_POP",
    "value": "55",
    "label": "LYQ4",
    "children": []
  }, {
    "name": "SEASONAL_CYQ1",
    "code": "SEASONAL_CYQ1",
    "parent": "SEASONAL_POP",
    "value": "2",
    "label": "CYQ1",
    "children": []
  }, {
    "name": "SEASONAL_CYQ2",
    "code": "SEASONAL_CYQ2",
    "parent": "SEASONAL_POP",
    "value": "22",
    "label": "CYQ2",
    "children": []
  }, {
    "name": "SEASONAL_CYQ3",
    "code": "SEASONAL_CYQ3",
    "parent": "SEASONAL_POP",
    "value": "161",
    "label": "CYQ3",
    "children": []
  }, {
    "name": "SEASONAL_CYQ4",
    "code": "SEASONAL_CYQ4",
    "parent": "SEASONAL_POP",
    "value": "55",
    "label": "CYQ4",
    "children": []
  }]
}];

var width = 460,
  height = 300,
  cwidth = 25;


var color = d3.scaleOrdinal(d3.schemeCategory20);

var pie = d3.pie()
  .sort(null).value(function(d) {
    return d.value; //since score is the parameter for the pie
  });

var arc = d3.arc();

var svg = d3.select("body").append("svg")
  .attr("width", width)
  .attr("height", height)
  .append("g")
  .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

var gs = svg.selectAll("g")
  .data(dataset)
  .enter()
  .append("g");
var path = gs.selectAll("path")
  .data(function(d) {
    return pie(d.children);
  })
  .enter().append("path")
  .attr("fill", function(d, i) {
    return color(i);
  })
  .attr("d", function(d, i, j) {
    return arc.innerRadius(10 + cwidth * j).outerRadius(cwidth * (j + 1))(d);
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js"></script>


Solution

  • I rewrote a bit your code. Check, now it works. Pay attention that I extend data object with property parentIndex when I store the index of the current data item. In d3 version 3 you can get the index of parent data-item as third argument function(d,i,j) (j in this example), but in the d3 version 4, the third argument it's not the index.

    var path = gs.selectAll("path")
      .data(function(d, i) {
        return pie(d.children).map(function(payload) { // <-- !!!
          return {
            payload: payload,
            parentIndex: i
          }
        })
      })
      .enter().append("path")
      .attr("fill", function(d, i) {
        return color(i);
      })
      .attr("d", function(d, i) {
        return arc
                .innerRadius(10 + cwidth * d.parentIndex) //<-- !!!
                .outerRadius(cwidth * (d.parentIndex + 1))(d.payload); //<-- !!!
      });
    

    var dataset = [{
      "name": "Population Quater",
      "code": "POP_QUATER",
      "parent": "POP_BY_QUAT",
      "children": [{
        "name": "POP_CYQ1",
        "code": "POP_CYQ1",
        "parent": "POP_QUATER",
        "value": "6772",
        "label": "CYQ1",
        "children": []
      }, {
        "name": "POP_CYQ2",
        "code": "POP_CYQ2",
        "parent": "POP_QUATER",
        "value": "6716",
        "label": "CYQ2",
        "children": []
      }, {
        "name": "POP_CYQ3",
        "code": "POP_CYQ3",
        "parent": "POP_QUATER",
        "value": "6714",
        "label": "CYQ3",
        "children": []
      }, {
        "name": "POP_CYQ4",
        "code": "POP_CYQ4",
        "parent": "POP_QUATER",
        "value": "6703",
        "label": "CYQ4",
        "children": []
      }, {
        "name": "POP_LYQ1",
        "code": "POP_LYQ1",
        "parent": "POP_QUATER",
        "value": "6721",
        "label": "LYQ1",
        "children": []
      }, {
        "name": "POP_LYQ2",
        "code": "POP_LYQ2",
        "parent": "POP_QUATER",
        "value": "6671",
        "label": "LYQ2",
        "children": []
      }, {
        "name": "POP_LYQ3",
        "code": "POP_LYQ3",
        "parent": "POP_QUATER",
        "value": "6708",
        "label": "LYQ3",
        "children": []
      }, {
        "name": "POP_LYQ4",
        "code": "POP_LYQ4",
        "parent": "POP_QUATER",
        "value": "6734",
        "label": "LYQ4",
        "children": []
      }]
    }, {
      "name": "Transient Pop",
      "code": "TRANSIENT_POP",
      "parent": "POP_BY_QUAT",
      "label": "Transient Pop",
      "children": [{
        "name": "TRANSIENT_LYQ1",
        "code": "TRANSIENT_LYQ1",
        "parent": "TRANSIENT_POP",
        "value": "54",
        "label": "LYQ1",
        "children": []
      }, {
        "name": "TRANSIENT_LYQ2",
        "code": "TRANSIENT_LYQ2",
        "parent": "TRANSIENT_POP",
        "value": "86",
        "label": "LYQ2",
        "children": []
      }, {
        "name": "TRANSIENT_LYQ3",
        "code": "TRANSIENT_LYQ3",
        "parent": "TRANSIENT_POP",
        "value": "219",
        "label": "LYQ3",
        "children": []
      }, {
        "name": "TRANSIENT_LYQ4",
        "code": "TRANSIENT_LYQ4",
        "parent": "TRANSIENT_POP",
        "value": "191",
        "label": "LYQ4",
        "children": []
      }, {
        "name": "TRANSIENT_CYQ1",
        "code": "TRANSIENT_CYQ1",
        "parent": "TRANSIENT_POP",
        "value": "52",
        "label": "CYQ1",
        "children": []
      }, {
        "name": "TRANSIENT_CYQ2",
        "code": "TRANSIENT_CYQ2",
        "parent": "TRANSIENT_POP",
        "value": "91",
        "label": "CYQ2",
        "children": []
      }, {
        "name": "TRANSIENT_CYQ3",
        "code": "TRANSIENT_CYQ3",
        "parent": "TRANSIENT_POP",
        "value": "222",
        "label": "CYQ3",
        "children": []
      }, {
        "name": "TRANSIENT_CYQ4",
        "code": "TRANSIENT_CYQ4",
        "parent": "TRANSIENT_POP",
        "value": "186",
        "label": "CYQ4",
        "children": []
      }]
    }, {
      "name": "Seasonal Pop",
      "code": "SEASONAL_POP",
      "parent": "POP_BY_QUAT",
      "label": "Seasonal Pop",
      "children": [{
        "name": "SEASONAL_LYQ1",
        "code": "SEASONAL_LYQ1",
        "parent": "SEASONAL_POP",
        "value": "2",
        "label": "LYQ1",
        "children": []
      }, {
        "name": "SEASONAL_LYQ2",
        "code": "SEASONAL_LYQ2",
        "parent": "SEASONAL_POP",
        "value": "24",
        "label": "LYQ2",
        "children": []
      }, {
        "name": "SEASONAL_LYQ3",
        "code": "SEASONAL_LYQ3",
        "parent": "SEASONAL_POP",
        "value": "152",
        "label": "LYQ3",
        "children": []
      }, {
        "name": "SEASONAL_LYQ4",
        "code": "SEASONAL_LYQ4",
        "parent": "SEASONAL_POP",
        "value": "55",
        "label": "LYQ4",
        "children": []
      }, {
        "name": "SEASONAL_CYQ1",
        "code": "SEASONAL_CYQ1",
        "parent": "SEASONAL_POP",
        "value": "2",
        "label": "CYQ1",
        "children": []
      }, {
        "name": "SEASONAL_CYQ2",
        "code": "SEASONAL_CYQ2",
        "parent": "SEASONAL_POP",
        "value": "22",
        "label": "CYQ2",
        "children": []
      }, {
        "name": "SEASONAL_CYQ3",
        "code": "SEASONAL_CYQ3",
        "parent": "SEASONAL_POP",
        "value": "161",
        "label": "CYQ3",
        "children": []
      }, {
        "name": "SEASONAL_CYQ4",
        "code": "SEASONAL_CYQ4",
        "parent": "SEASONAL_POP",
        "value": "55",
        "label": "CYQ4",
        "children": []
      }]
    }];
    
    var width = 460,
      height = 300,
      cwidth = 25;
    
    
    var color = d3.scaleOrdinal(d3.schemeCategory20);
    
    var pie = d3.pie()
      .sort(null).value(function(d) {
        return d.value; //since score is the parameter for the pie
      });
    
    var arc = d3.arc();
    
    var svg = d3.select("body").append("svg")
      .attr("width", width)
      .attr("height", height)
      .append("g")
      .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
    
    var gs = svg.selectAll("g")
      .data(dataset)
      .enter()
      .append("g");
    var path = gs.selectAll("path")
      .data(function(d, i) {
      	return pie(d.children).map(function(payload) {
    			return {
          	payload: payload,
            parentIndex: i
          }
        })
      })
      .enter().append("path")
      .attr("fill", function(d, i) {
        return color(i);
      })
      .attr("d", function(d, i, j) {
        return arc.innerRadius(10 + cwidth * d.parentIndex).outerRadius(cwidth * (d.parentIndex + 1))(d.payload);
      });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js"></script>