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

1

7

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;
    });
  };
}
Response answered 25/11, 2015 at 23:49 Comment(0)
P
7

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.

Particiaparticipant answered 26/11, 2015 at 6:13 Comment(1)
Wow thank you very very much!! Thank you Cyril and Stackoverflow. I'm sold! Hope to help you back one day but for now thank you so much for your time.Response

© 2022 - 2024 — McMap. All rights reserved.