0

In the code below, I need to make the bubbles with the highest values ​​float to the left of the screen, but I have no deep knowledge of D3.js and I can't find a way to do this.

My code

<script type="text/javascript">

     dataset = {
        "children": [{"Name":"Olives","Count":10},
            {"Name":"Tea","Count":8},
            {"Name":"Mashed Potatoes","Count":6},
            {"Name":"Boiled Potatoes","Count":5},
            {"Name":"Milk","Count":4},
            {"Name":"Chicken Salad","Count":4},
            {"Name":"Vanilla Ice Cream","Count":2},
            {"Name":"Cocoa","Count":7}];

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

    var bubble = d3.pack(dataset)
        .size([diameter, diameter])
        .padding(1.5);

    var svg = d3.select("body")
        .append("svg")
        .attr("width", diameter)
        .attr("height", diameter)
        .attr("class", "bubble");

    var nodes = d3.hierarchy(dataset)
        .sum(function(d) { return d.Count; });

    var node = svg.selectAll(".node")
        .data(bubble(nodes).descendants())
        .enter()
        .filter(function(d){
            return  !d.children
        })
        .append("g")
        .attr("class", "node")
        .attr("transform", function(d) {
            return "translate(" + d.x + "," + d.y + ")";
        });

    node.append("title")
        .text(function(d) {
            return d.Name + ": " + d.Count;
        });

    node.append("circle")
        .attr("r", function(d) {
            return d.r;
        })
        .style("fill", function(d,i) {
            return color(i);
        });

    node.append("text")
        .attr("dy", ".2em")
        .style("text-anchor", "middle")
        .text(function(d) {
            return d.data.Name.substring(0, d.r / 3);
        })
        .attr("font-family", "sans-serif")
        .attr("font-size", function(d){
            return d.r/5;
        })
        .attr("fill", "white");

    node.append("text")
        .attr("dy", "1.3em")
        .style("text-anchor", "middle")
        .text(function(d) {
            return d.data.Count;
        })
        .attr("font-family",  "Gill Sans", "Gill Sans MT")
        .attr("font-size", function(d){
            return d.r/5;
        })
        .attr("fill", "white");

    d3.select(self.frameElement)
        .style("height", diameter + "px");

</script>

Edit in JS Fiddle

The code produces this result

I need the bubbles with the highest values ​​to be on the left, as in the image below.

What property or function of D3.js can I use to control the severity of the bubbles as I need them? Thanks!

Renan Barbosa
  • 1,046
  • 3
  • 11
  • 31

1 Answers1

1

There's not a function specifically in d3.pack for this. d3.force allows you specify x and y positions based on the data's value, and could achieve the result you are looking for.

The force simulation has an .x and .y function that is based on the data's count, and then to avoid overlaps, the .collide function adjusts the circles positions based on their radius (plus a small paddding of 3px).

 var simulation = d3.forceSimulation(nodes)
        .force("forceX", d3.forceX().strength(.051).x(d => xScale(d.Count)))
        .force("forceY", d3.forceY().strength(.051).y(d => yScale(d.Count)))
        .force('collision', d3.forceCollide().radius(d => rScale(d.Count) + 3))

dataset = {
        "children": [{"Name":"Olives","Count":10},
            {"Name":"Tea","Count":8},
            {"Name":"Mashed Potatoes","Count":6},
            {"Name":"Boiled Potatoes","Count":5},
            {"Name":"Milk","Count":4},
            {"Name":"Chicken Salad","Count":4},
            {"Name":"Vanilla Ice Cream","Count":2},
            {"Name":"Cocoa","Count":7}]
    }
    
    let nodes = dataset.children

    var width = 600;
    var height = 600;
    var margin = 50
    var color = d3.scaleOrdinal(d3.schemeCategory20);
    
    let extentCount = d3.extent(nodes, d => d.Count)
    let maxRadius = 100 
    
    let yScale = d3.scaleLinear()
     .domain(extentCount)
     .range([height - maxRadius, maxRadius])
    
    let xScale = d3.scaleLinear()
     .domain(extentCount)
     .range([(width - maxRadius), maxRadius])
    
    let rScale = d3.scaleSqrt()
     .domain(extentCount)
     .range([5, maxRadius])
    
    var svg = d3.select("body")
        .append("svg")
        .attr("width", width + margin + margin)
        .attr("height", height + margin + margin)
        .attr("class", "bubble");
    
    var g = svg.append("g")
     .attr("transform", "translate(" + margin + "," + margin + ")")
    
    var simulation = d3.forceSimulation(nodes)
        .force("forceX", d3.forceX().strength(.051).x(d => xScale(d.Count)))
        .force("forceY", d3.forceY().strength(.051).y(d => yScale(d.Count)))
        .force('collision', d3.forceCollide().radius(d => rScale(d.Count) + 3))
      .on("tick", function(d){
            node
                .attr("cx", function(d){ return d.x; })
                .attr("cy", function(d){ return d.y; })
          })
      .stop()
    
    for (var i = 0; i < 120; i++) {
      simulation.tick()
    }

    var node = g.selectAll(".node")
        .data(nodes)
        .enter()
        .append("g")
        .attr("class", "node")
        .attr("transform", function(d) {
            return "translate(" + d.x + "," + d.y + ")";
        });

    node.append("title")
        .text(function(d) {
            return d.Name + ": " + d.Count;
        });

    node.append("circle")
        .attr("r", d => rScale(d.Count))
        .style("fill", function(d,i) {
            return color(i);
        });

    node.append("text")
        .attr("dy", ".2em")
        .style("text-anchor", "middle")
        .text(function(d) {
            return d.Name.substring(0, rScale(d.Count) / 3);
        })
        .attr("font-family", "sans-serif")
        .attr("font-size", function(d){
            return rScale(d.Count)/5;
        })
        .attr("fill", "white");

    node.append("text")
        .attr("dy", "1.3em")
        .style("text-anchor", "middle")
        .text(function(d) {
            return d.Count;
        })
        .attr("font-family",  "Gill Sans", "Gill Sans MT")
        .attr("font-size", function(d){
            return d.r/5;
        })
        .attr("fill", "white");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
Tom Shanley
  • 1,757
  • 1
  • 7
  • 9
  • Your solution helps me a lot, but right now, I need to get bubbles as close to each other as possible. Is that possible? – Renan Barbosa Apr 02 '20 at 23:44