Search code examples
d3.jssunburst-diagram

Root element is not showing its children in sunburst


I am trying to make a sunburst by following the 3-part tutorial on https://bl.ocks.org/denjn5/3b74baf5edc4ac93d5e487136481c601 My json contains sell information based on country and product division. I am trying to show in the first layer sell based on country and in the 2nd layer sell based on product division. My Json file looks like this:

    {
       "country": "All", 
        "shares":[

        {
            "country": "at",
            "shares":[
                {
                "productdivision": "accessorie",
                "label": 53222
                },
                {
                "productdivision": "apparel",
                "label": 365712
                },
                {
                "productdivision": "footwear",
                "label": 523684
                }
            ]
        },

        {
            "country": "be",
            "shares":[
                {
                "productdivision": "accessorie",
                "label": 57522
                },
                {
                 "productdivision": "apparel",
                 "label": 598712
                },
                {
                "productdivision": "footwear",
                "label": 52284
                }
            ]
        },

        {
            "country": "DE",
            "shares":[
                {
                "productdivision": "accessorie",
                "label": 56982

                },
                {
                "productdivision": "apparel",
                "label": 55312
                },
                {
                "productdivision": "footwear",
                "label": 67284
                }
            ]
         },

        {
            "country": "Fr",
            "shares":[
                {
                "productdivision": "accessorie",
                "label": 5862
                },
                {
                "productdivision": "apparel",
                "label": 45312
                },
                {
                "productdivision": "footwear",
                "label": 26284
                }
            ]
        }

    ]
    }

This json file's name is kpiDrillDown2.json and I call it in my code with d3.json(). I have made slight changes to the code to work for my data. The code is as follows:

<html>
    <head>
        <style>
            @import url('https://fonts.googleapis.com/css?family=Raleway');
            body {
                    font-family: "Raleway", "Helvetica Neue", Helvetica, Arial, sans-serif;
                }
        </style>

        <script src="https://d3js.org/d3.v4.min.js"></script>
    </head>
    <body>
        <svg></svg>
        <script>
            //initialize variables
            var width = 500; 
            var height = 500;
            var radius = Math.min(width, height) / 2;  
            var color = d3.scaleOrdinal(d3.schemeCategory20b);  

            //setting up svg workspace
            var g = d3.select('svg')  
                    .attr('width', width)  
                    .attr('height', height)
                    .append('g')  
                    .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')'); 

            //formatting the data
            var partition = d3.partition()      
                            .size([2 * Math.PI, radius]);

            function draw(nodeData){

                debugger;
                //finding the root node
                var root = d3.hierarchy(nodeData)     
                            .sum(function (d) { return d.label});           

                //calculating each arc
                partition(root);    

                var arc = d3.arc()  
                            .startAngle(function (d) { return d.x0; })     
                            .endAngle(function (d) { return d.x1; })       
                            .innerRadius(function (d) { return d.y0; })    
                            .outerRadius(function (d) { return d.y1; });   

                g.selectAll('g')   
                    .data(root.descendants())
                    .enter()
                    .append('g')
                    .attr("class", "node")  
                    .append('path') 
                    .attr("display", function (d) { return d.depth ? null : "none"; })
                    .attr("d", arc)
                    .style('stroke', '#fff')
                    .style("fill", function (d) { return color((d.parent ? d : d.parent).data.productdivision); })


                g.selectAll(".node")  
                    .append("text")  
                    .attr("transform", function(d) {
                        return "translate(" + arc.centroid(d) + ")rotate(" + computeTextRotation(d) + ")"; })  
                    .attr("dx", "-20")  
                    .attr("dy", ".5em") 
                    .text(function(d) { return d.parent ? d.data.productdivision : "" }); 


                    function computeTextRotation(d) {
                        var angle = (d.x0 + d.x1) / Math.PI * 90;  

                        // Avoid upside-down labels
                        return (angle < 90 || angle > 270) ? angle : angle + 180; 
                    }

            }

            d3.json('kpiDrillDown3.json', draw);

        </script>
    </body>
</html>

I put a debbuger in the draw functin to inspect root element. Root doesn't have any children. This is what I see in the console:Inspect the root element

When I continue it gives me the error:"Cannot read property 'data' of null". As shown in console, root doesn't have children. My question is, do I need to change my json data format to make root recogninze the chilren, or am I doing something wrong. I am new to d3js and basically by getting the source code and modifying it, I am making my way through. This is the error in console:

Console error in redeading productDivision attribute

I appreciate your help and thank you very much.


Solution

  • According to the API:

    The specified children accessor function is invoked for each datum, starting with the root data, and must return an array of data representing the children, or null if the current datum has no children. If children is not specified, it defaults to:

    function children(d) {
        return d.children;
    }
    

    However, in your data structure, you don't have children, but shares instead.

    So, the hierarchy should be:

    var root = d3.hierarchy(data, function(d) {
        return d.shares;
    })
    

    Pay attention to the fact that in the JSON of that tutorial you linked (just like in the API's example) the children's array is named children.

    Here is a demo, look at the console (your browser's console, not the snippet one):

    var data = {
      "country": "All",
      "shares": [{
          "country": "at",
          "shares": [{
              "productdivision": "accessorie",
              "label": 53222
            },
            {
              "productdivision": "apparel",
              "label": 365712
            },
            {
              "productdivision": "footwear",
              "label": 523684
            }
          ]
        },
        {
          "country": "be",
          "shares": [{
              "productdivision": "accessorie",
              "label": 57522
            },
            {
              "productdivision": "apparel",
              "label": 598712
            },
            {
              "productdivision": "footwear",
              "label": 52284
            }
          ]
        },
        {
          "country": "DE",
          "shares": [{
              "productdivision": "accessorie",
              "label": 56982
    
            },
            {
              "productdivision": "apparel",
              "label": 55312
            },
            {
              "productdivision": "footwear",
              "label": 67284
            }
          ]
        },
        {
          "country": "Fr",
          "shares": [{
              "productdivision": "accessorie",
              "label": 5862
            },
            {
              "productdivision": "apparel",
              "label": 45312
            },
            {
              "productdivision": "footwear",
              "label": 26284
            }
          ]
        }
      ]
    };
    
    var root = d3.hierarchy(data, function(d) {
        return d.shares;
      })
      .sum(function(d) {
        return d.label
      });
    
    console.log(root)
    <script src="https://d3js.org/d3.v4.min.js"></script>