Creating Tree diagrams with pie charts using D3

Has anyone tried creating a D3 pie component within each node of a tree?

I've managed to build the tree and a single pie separately, but I'm struggling to combine them.

My JSON data looks like this:

window.json = {
    "health": [{
    "value": 60
    }, {
    "value": 10
    }, {
    "value": 30
    }],
    "color": orange,
    "children": [{
    "health": [{
        "value": 60
    }, {
        "value": 20
    }, {
        "value": 20
    }],
    "color": green
    }, {
    "health": [{
        "value": 40
        }, {
        "value": 30
        }, {
        "value": 30
        }],
    "color": orange
    }]
};

This represents the tree structure, with each node containing data for a pie chart based on the "health" properties.

The tree can be viewed here: http://jsfiddle.net/4srt30pj/4/

A standalone pie chart example is available here: http://jsfiddle.net/4srt30pj/5/

However, I'm unable to integrate these components so that each node displays a pie chart. I attempted creating a function for drawing the pie component:

function drawPie(selection, node) {
    selection.data(node, function(d, i) {
        console.log(node);
        console.log(d);
        console.log(i);
        return pie(d.health);
    })
    .enter()
    .append('path')
    .attr('d', arc)
    .attr('fill', function (d, i) {
        return color(i);
    });
}

Then calling it for each tree node:

drawPie(vis.selectAll("g.node"), nodes);

(Full code can be found here: http://jsfiddle.net/4srt30pj/6/)

Unfortunately, the pies are not displaying as expected.

Is there a way to successfully achieve this composition?

Answer №1

You are almost there. Give this a shot:

function drawPie(d) {      
  d3.select(this)
    .selectAll('path')
    .data(pie(d.health))
    .enter()
    .append('path')
    .attr('d', arc)
    .attr('fill', function(d, i) {
       return color(i);
    });
}

nodeEnter.each(drawPie);

Check out the complete working demo below:

<!DOCTYPE html>
<html>

<head>
  <script data-require="<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e682d5a6d5c8d3c8d5">[email protected]</a>" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
  <style>
    path.link {
      fill: none;
      stroke-width: 5px;
    }
    
    svg text {
      font-family: Roboto, Arial;
    }
    
    .selected {
      display: none;
    }
  </style>
</head>

<body>
  <script>
    var red = "#f5696d";
    var green = "#40bc96";
    var orange = "#fabd57";
    window.json = {
      "health": [{
        "value": 60
      }, {
        "value": 10
      }, {
        "value": 30
      }],
      "color": orange,
      "children": [{
        "health": [{
          "value": 60
        }, {
          "value": 20
        }, {
          "value": 20
        }],
        "color": green
      }, {
        "health": [{
          "value": 40
        }, {
          "value": 30
        }, {
          "value": 30
        }],
        "color": orange
      }]
    };

    var w = 100;
    var h = 60;
    var i = 0;
    var root;

    var tree = d3.layout.tree()
      .nodeSize([w + 10, h + 20])
      .separation(function(a, b) {
        return (a.parent == b.parent ? 1 : 1.5);
      });

    var diagonal = d3.svg.diagonal()
      .projection(function(d) {
        return [d.x, d.y];
      });

    var vis = d3.select("body").append("svg:svg")
      .attr("width", 500)
      .attr("height", 500)
      .append("svg:g")
      .attr("transform", "translate(" + 250 + "," + 30 + ")");

    root = window.json;
    root.x0 = 0;
    root.y0 = 0;

    function toggleAll(d) {
      if (d.children) {
        d.children.forEach(toggleAll);
        toggle(d);
      }
    }

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

    var pie = d3.layout.pie()
      .value(function(d) {
        return d.value;
      })
      .sort(null);

    var color = d3.scale.ordinal()
      .range(['#40bc96', '#fabd57', '#f5696d']);
    

    function drawPie(d) {
      
      d3.select(this)
        .selectAll('path')
        .data(pie(d.health))
        .enter()
        .append('path')
        .attr('d', arc)
        .attr('fill', function(d, i) {
          return color(i);
        });
    }

    update(root);

    function update(source) {
      var duration = d3.event &&& d3.event.altKey ? 5000 : 500;

      // Calculate the new layout for the tree.
      var nodes = tree.nodes(root).reverse();

      // Update the nodes…
      var node = vis.selectAll("g.node")
        .data(nodes, function(d) {
          return d.id || (d.id = ++i);
        });

      // Enter any new nodes at the parent's previous position.
      var nodeEnter = node.enter().append("svg:g")
        .attr("class", "node")
        .attr("transform", function(d) {
          return "translate(" + source.x0 + "," + source.y0 + ")";
        });

      nodeEnter
        .each(drawPie);

      // Transition nodes to their new position.
      var nodeUpdate = node.transition()
        .duration(duration)
        .attr("transform", function(d) {
          return "translate(" + d.x + "," + d.y + ")";
        });

      // Update the links…
      var link = vis.selectAll("path.link")
        .data(tree.links(nodes), function(d) {
          return d.target.id;
        });

      // Enter any new links at the parent's previous position.
      link.enter().insert("svg:path", "g")
        .attr("class", "link")
        .style("stroke-opacity", 0.4)
        .style("stroke", function(d) {
          return d.target.color;
        })
        .attr("d", function(d) {
          var o = {
            x: source.x0,
            y: source.y0
          };
          return diagonal({
            source: o,
            target: o
          });
        })
        .transition()
        .duration(duration)
        .attr("d", diagonal);

      // Store the old positions for transition.
      nodes.forEach(function(d) {
        d.x0 = d.x;
        d.y0 = d.y;
      });
    }
  </script>
</body>

</html>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Error: The script is unable to access the property "div" because it is undefined

I keep encountering this issue "Cannot read property 'div' of undefined" This is the snippet in question function validateData(){ for(let j = 0; j < 13; j++){ if(dataArr[j].data.textContent === resultSet[j].result.div. ...

Make sure to execute the fire directive only when ng-repeat has completed

Currently, I am utilizing owl carousel with data being fetched through an Ajax call. Once the data populates the HTML content using ng-repeat, I need to trigger the directive that initializes the owl carousel. How can I achieve this? One approach I consid ...

Having trouble enabling push notifications on Android with ngCordova

Having trouble with push notifications through the ngCordova plugin. Followed the sample code from (with a slight change - no deviceready listener, code inside ionicPlatform.ready listener) Below is the code snippet: angular.module('myApp', [& ...

Alert box in Vue.js

Embarking on my journey with VueJS. I previously implemented an alert box using jQuery. Now, I am attempting to recreate that in VueJS. Here is my current progress: 1- Developed a component named NovinAlert.vue which includes: <template> & ...

Is it advisable to incorporate vue-resource in Vuex actions?

I am currently working on a web application that retrieves airport data from the backend. To manage states and data sharing, I am utilizing Vuex. My dilemma is whether to load the airports in my Vuex actions or within a method of my Vue instance which will ...

Tips for creating read-only checkboxes with multipledropdown.js in Angular.js

I am trying to make all checkboxes in a multiple dropdown list readonly using Angular.js with the multipledropdown.js plugin. My code snippet is as follows: <div class="col-md-6" ng-show="sub_sub_menu"> <div class="input-group bmargindiv1 col-md- ...

Troubleshooting error messages in the console during conversion of image URL to Base64 in AngularJS

While attempting to convert an image URL to Base64 using the FromImageUrl method, I encountered an error in my console. Access to the image located at '' from the origin 'http://localhost:8383' has been blocked due to CORS policy ...

Automatically fill in an input field with jQuery by using the value from another input field

I am currently working on a loan calculator and am trying to find a way to automatically populate an interest rate field based on the loan amount entered. Here is an example scenario: For amounts ranging from 0 to 100,000, the interest rate is 4.50 For ...

The html carousel displays stacked images instead of scrolling

I have been staring at this code for what feels like an eternity, trying to figure out why it's only displaying stacked pictures instead of a functioning carousel. Perhaps I overlooked something crucial. Could someone please lend a hand? My code is pr ...

Whenever I hit the refresh button, `$locationprovider.html5mode` seems to deactivate my CSS styling

Things go smoothly as long as I remove $locationprovider.html5mode(true). However, enabling html5mode seems to be causing some troubles. The main issue is that the css styles stop working after page refreshes. Any ideas on what could be causing this and ...

Incorporate the previous page's location path into the next page using Props in Gatsby Link

My website has multiple pages with paginated lists of blog posts, each post generated from markdown using createPage(). Each page in the /posts directory displays 3 post previews and subsequent pages are numbered (e.g. /posts/2). I am trying to pass the p ...

Converting HTML into an object using $compile

I am currently working on calling an HTML template and want to bind the data with Angular. I successfully retrieve the data to bind and the HTML, but when I try to compile it, it returns all the HTML binded as an object. How can I convert it back to plain ...

What could be the issue with trying to bind an event handler in this manner?

I'm having some trouble binding an event handler with jQuery: $(document).ready(function () { var newsScrollerForPage = new NewsScroller(); newsScrollerForPage.init(); $('#scroller-left-a').bind('on ...

Executing a npm script (script.js) with custom arguments - a step-by-step guide

I'm considering creating a setup similar to lodash custom builds. Basically, I want to allow users to input a command like: lodash category=collection,function This would create a custom module with the specified category. I've been looking in ...

Performing AJAX requests within loops using Javascript can be a powerful tool

Utilizing jQuery to make an AJAX request and fetching data from a server. The retrieved data is then added to an element. The goal is for this process to occur 5 times, but it seems to happen randomly either 3, 4, or 5 times. Occasionally, the loop skips ...

What are some ways to bypass a closed Google form that is no longer accepting responses? I need to find a way to submit my form even after the deadline

Is there a way to trick a closed Google form that says it is "no longer accepting responses"? I'm looking to submit a form past the deadline. Is there a method to access and submit a form that has already been closed? The code for the closed form appe ...

JavaScript - the act of exiting functions

Is it necessary to explicitly return from a JavaScript function? Unlike in other languages where not returning can result in a stack overflow error, JavaScript seems to handle this differently. Furthermore, due to its asynchronous nature, determining when ...

Using AngularJS to dynamically bind data based on certain conditions

I am managing an array of tasks that each have titles and labels. function Task(taskTitle, taskType) { this.title = taskTitle; this.type = taskType; } $scope.tasks = []; As I create different tasks with various types and add them to the array. In ...

JavaScript was unable to locate the requested URL on the server

After successfully deploying and accessing the URL using Firebase's hosting feature, everything seems to work fine. However, when I try to access a specific endpoint like this: https://*******.web.app/api/send, I encounter the following error message: ...

A step-by-step guide on displaying content according to a template file in AngularJS

I am new to using angular js and need help with displaying specific content on a particular page of my web application. I have a HTML template (showContent.html) that generates all pages, but I only want to show certain content on the URL http://localhost/ ...