Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I add labels to d3.js force bubble chart

I'm trying to modify this code bl.ocks.org/mbostock/7881887 so that I can use the technique to show a word cloud where the circle size would be relative to the no of words in a file but my main problem at the moment finding out how to add text to the circles in the first place. Tantalizingly I can see what looks like a function that does this in the code ... node.append("text")? So I assumed I would be able to add a "name" value to "d" in the nodes function and away we'd go. As you can see I've just added name: text where text = "Test" to d. Any help getting any text at all to appear within or near these circles would be very greatly appreciated! I'm new D3 as you can probably tell and have spent many evenings googling this to find nothing at all. I can see examples on a different kind of chart... the force layout eg http://bl.ocks.org/mbostock/1093130 but they appear so different I can't apply one to the other :-(

here is my jsfiddle https://jsfiddle.net/TimBrighton/vn7reroe/1/

var width = 960,
    height = 500,
    padding = 1.5, // separation between same-color nodes
    clusterPadding = 6, // separation between different-color nodes
    maxRadius = 12;

var n = 100, // total number of nodes
    m = 5; // number of distinct clusters

var color = d3.scale.category10()
    .domain(d3.range(m));

// The largest node for each cluster.
var clusters = new Array(m);

var nodes = d3.range(n).map(function() {
  test="Test";
  var i = Math.floor(Math.random() * m),
      r = Math.sqrt((i + 1) / m * -Math.log(Math.random())) * maxRadius,      
      d = {
        name: test,
        cluster: i,
        radius: r,
        name: "test",
        x: Math.cos(i / m * 2 * Math.PI) * 200 + width / 2 + Math.random(),
        y: Math.sin(i / m * 2 * Math.PI) * 200 + height / 2 + Math.random()
      };
  if (!clusters[i] || (r > clusters[i].radius)) clusters[i] = d;
  console.log(d.name);
  return d;

});

var force = d3.layout.force()
    .nodes(nodes)
    .size([width, height])
    .gravity(.02)
    .charge(0)
    .on("tick", tick)
    .start();

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

var node = svg.selectAll("circle")
    .data(nodes)
  .enter().append("circle")
    .style("fill", function(d) { return color(d.cluster); })
    .call(force.drag);


node.append("text")
.text(function(d) { return d.name; })
     // .style("font-size", function(d) { return Math.min(2 * d.r, (2 * d.r - 8) / this.getComputedTextLength() * 24) + "px"; })
     //.attr("dy", ".35em");

    .attr("dx", 10)
    .attr("dy", ".35em")
    .text(function(d) { return d.name })
    .style("stroke", "gray");

node.transition()
    .duration(750)
    .delay(function(d, i) { return i * 5; })
    .attrTween("r", function(d) {
      var i = d3.interpolate(0, d.radius);
      return function(t) { return d.radius = i(t); };
    });




function tick(e) {
  node
      .each(cluster(10 * e.alpha * e.alpha))
      .each(collide(.5))
      .attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; });
}

// Move d to be adjacent to the cluster node.
function cluster(alpha) {
  return function(d) {
    var cluster = clusters[d.cluster];
    if (cluster === d) return;
    var x = d.x - cluster.x,
        y = d.y - cluster.y,
        l = Math.sqrt(x * x + y * y),
        r = d.radius + cluster.radius;
    if (l != r) {
      l = (l - r) / l * alpha;
      d.x -= x *= l;
      d.y -= y *= l;
      cluster.x += x;
      cluster.y += y;
    }
  };
}

// Resolves collisions between d and all other circles.
function collide(alpha) {
  var quadtree = d3.geom.quadtree(nodes);
  return function(d) {
    var r = d.radius + maxRadius + Math.max(padding, clusterPadding),
        nx1 = d.x - r,
        nx2 = d.x + r,
        ny1 = d.y - r,
        ny2 = d.y + r;
    quadtree.visit(function(quad, x1, y1, x2, y2) {
      if (quad.point && (quad.point !== d)) {
        var x = d.x - quad.point.x,
            y = d.y - quad.point.y,
            l = Math.sqrt(x * x + y * y),
            r = d.radius + quad.point.radius + (d.cluster === quad.point.cluster ? padding : clusterPadding);
        if (l < r) {
          l = (l - r) / l * alpha;
          d.x -= x *= l;
          d.y -= y *= l;
          quad.point.x += x;
          quad.point.y += y;
        }
      }
      return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
    });
  };
}
like image 783
TimBrighton Avatar asked Nov 25 '15 23:11

TimBrighton


1 Answers

You are adding the TEXT DOM with in the circle DOM.

That is the reason why you are not seeing the text.

<circle cx="331.0297405069362" cy="238.4899367858363" style="fill: rgb(255, 127, 14);" r="24.842603758681765">
    <text dx="10" dy=".35em" style="stroke: gray;">test</text>
</circle>

This is incorrect.

The correct way would be to make a group add circle and text with in it. Like this:

<g transform="translate(402.3818560847106,240.01473655622326)">
   <circle r="31.769893912794977" style="fill: rgb(255, 127, 14);">   </circle>
   <text dx="-10" dy=".35em" style="stroke: gray;">test</text>
</g>

So to do this first you make groups like this:

var node = svg.selectAll("circle")
    .data(nodes)
    .enter().append("g").call(force.drag);

Add circle to the group:

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

Add text to the group

//add text to the group    
node.append("text")
    .text(function (d) {
    return d.name;
})
.attr("dx", -10)
    .attr("dy", ".35em")
    .text(function (d) {
    return d.name
})
    .style("stroke", "gray");

Inside the tick function instead of updating the cx cy of the circle we need to move the full group so use transforms like thsi:

function tick(e) {
    node.each(cluster(10 * e.alpha * e.alpha))
        .each(collide(.5))
    //.attr("transform", functon(d) {});
    .attr("transform", function (d) {
        var k = "translate(" + d.x + "," + d.y + ")";
        return k;
    })

}

Working code here.

like image 110
Cyril Cherian Avatar answered Sep 17 '22 03:09

Cyril Cherian