Search code examples
javascriptd3.jscircle-pack

Drawing multiple circles with text in them using d3.js


I am a new javascript programmer. I am trying to make a visualization that draws circles based on categories ("basically a single category on every circle"). My issue is that my code only prints one circle, with all categories on top of each other.

 <script> 
        var width = 600;
        var height = 600;

        var data = d3.json("/data", function(error, data){
                    console.log(data)
        // Make SVG container 
        var svgContainer = d3.select("body")
                             .append("svg")
                             .attr("width", width)
                             .attr("height", height);

        var elem = svgContainer.selectAll("div")
                               .data(data);

        var elemEnter = elem.enter()
                            .append("g")

        var circles = elemEnter.append("circle")
                              .attr("cx", 100)
                              .attr("cy", 100)
                              .attr("r",80)
                              .style("fill", "blue")

        elemEnter.append("text")
                 .attr("dy", function(d){
            return d.SkillProficiencyId +80;
            }).attr("dx",function(d){ return d.SkillProficiencyId-1;})
                    .text(function(d){return d.CategoryName});
        });


    </script>

enter image description here

> Sample of my Data
(18) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
0:
EmployeeId: 11111
AdUserName: null
FirstName: "ABC"
LastName: "DEF"
SkillId: 2346
CategoryName: "Adaptive Security Architecture"
SkillProficiencyId: 1
SkillProficiencyLevel: "Beginner"
__proto__: Object
1: {EmployeeId: 11111, AdUserName: null, FirstNam...
2: {EmployeeId: 11111, AdUserName: null, FirstName....

Solution

  • I created a example with circles dynamically created, with text centered inside.

    https://codepen.io/mayconmesquita/pen/OJyRZxX

    Demo:

    enter image description here

    The JS code: [Edited *]

    var width = 600;
    var height = 600;
    
    // Place your JSON here.
    var data = [
      { CategoryName: 'Adaptive Security', SkillProficiencyId: 1 },
      { CategoryName: 'Programmer', SkillProficiencyId: 2 },
      { CategoryName: 'Coffee Drinker', SkillProficiencyId: 3 }
    ];
    
    /*
      This 'cxBase' will be multiplied by element's index, and sum with offset.
      So for 3 elements, cx = 0, 200, 400 ...
      All these values changeable by this vars.
    */
    const cxBase = 200;
    const cxOffset = 100;
    
    console.log(data);
    
    // Make SVG container  
    var svgContainer = d3.select("body")
    .append("svg")
    .attr("width", width)
    .attr("height", height);
    
    // This function will iterate your data
    data.map(function(props, index) {
      var cx = cxBase * (index) + cxOffset; // Here CX is calculated
    
      var elem = svgContainer.selectAll("div").data(data);
    
      var elemEnter = elem.enter()
      .append("g")
    
      var circles = elemEnter.append("circle")
      .attr("cx", cx)
      .attr("cy", 100)
      .attr("r", 80)
      .style("fill", "blue");
    
      elemEnter
      .append("text")
      .style("fill", "white")
      .attr("dy", function(d){
        return 105;
      })
      .attr("dx",function(d){
        return cx - (props.CategoryName.length * 3.5);
      })
      .text(function (d) {
        return props.CategoryName
      });
    });
    

    Edit:

    1. As author requested, i did improve the code. Now the circle's cx coordinate is calculated dynamically based on array element's index.
    /*
      This 'cxBase' will be multiplied by element's index, and sum with offset
      So for 3 elements, cx = 0, 200, 400 ...
      All these values changeable by this vars.
    */
    const cxBase = 200;
    const cxOffset = 100;