1

I'm a D3 newbie and trying to build word cloud page using JasonDavies d3-cloud.

But sometimes all words are showing in circle layout, but sometimes in rectangular.

How can I make them always locate in circle layout?

1 Answer 1

1

d3-cloud's layout algorithm places words within an ellipse starting from the center of the rectangle you've provided (d3.layout.cloud().size([width, height])).

If you have a container (size([width, height])) big enough compared to your number of words and the size you've given to these words, then you'll get a nice circle (or an ellipse if your container isn't a square):

Math.seedrandom('hello words');

var data = [{ text: "Hello", count: 38 }, { text: "World", count: 27 }, { text: "Whatever", count: 21 }, { text: "Massive", count: 21 }, { text: "Thing", count: 16 }, { text: "Something", count: 14 }, { text: "What", count: 12 }, { text: "Else", count: 9 }, { text: "Blabla", count: 6 }, { text: "Small", count: 6 }, { text: "VeryLong", count: 6 }, { text: "Word", count: 3 }, { text: "abcdef", count: 4 }, { text: "Elsewhere", count: 9 }, { text: "Anything", count: 3 }, { text: "Placeholder", count: 14 }, { text: "WhateverSmall", count: 3 }, { text: "Work", count: 12 }, { text: "Wording", count: 7 }, { text: "Verb", count: 4 }];

  var svg = d3.select("svg").append("g");

  let fill = d3.scaleOrdinal(d3.schemeCategory20);
  let size = d3.scaleLinear().range([0, 20]).domain([0, d3.max(data, d => d.count)]);

  let word_cloud_data = data
    .map( function(d) {
      return { text: d.text, size: 9 + size(d.count) * 3.5 };
    });

  let layout = d3.layout.cloud()
    .size([600, 600])
    .words(word_cloud_data)
    .padding(2.5)
    .rotate(d => ~~(Math.random() * 2) * -90)
    .fontSize(d => d.size)
    .on("end", draw);

  layout.start();

  function draw(words) {

    svg.append("g")
      .attr("transform", "translate(250,250)")
      .selectAll("text")
      .data(words)
      .enter().append("text")
      .style("fill", (d, i) => { d.color = fill(i); return d.color; })
      .style("text-anchor", "middle")
      .attr("transform", d => "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")")
      .text(d => d.text)
      .style("font-size", d => d.size + "px");
  }
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://cdn.rawgit.com/jasondavies/d3-cloud/master/build/d3.layout.cloud.js"></script>
<script src="https://d3js.org/d3-scale.v1.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/seedrandom/2.4.4/seedrandom.min.js">
</script>
<svg width="700" height="700"></svg>

If your container can't hold your words as an ellipse/circle, d3-cloud's layout algorithm will start finding space in the corners of the container you've chosen to contain your cloud. And it will start looking like a rectangle/square. (Up to the point there's not even enough space to add the remaining words):

Math.seedrandom('hello words');

var data = [{ text: "Hello", count: 38 }, { text: "World", count: 27 }, { text: "Whatever", count: 21 }, { text: "Massive", count: 21 }, { text: "Thing", count: 16 }, { text: "Something", count: 14 }, { text: "What", count: 12 }, { text: "Else", count: 9 }, { text: "Blabla", count: 6 }, { text: "Small", count: 6 }, { text: "VeryLong", count: 6 }, { text: "Word", count: 3 }];

  var svg = d3.select("svg").append("g");

  let fill = d3.scaleOrdinal(d3.schemeCategory20);
  let size = d3.scaleLinear().range([0, 20]).domain([0, d3.max(data, d => d.count)]);

  let word_cloud_data = data
    .map( function(d) {
      return { text: d.text, size: 9 + size(d.count) * 3.5 };
    });

  let layout = d3.layout.cloud()
    .size([275, 275])
    .words(word_cloud_data)
    .padding(2.5)
    .rotate(d => ~~(Math.random() * 2) * -90)
    .fontSize(d => d.size)
    .on("end", draw);

  layout.start();

  function draw(words) {

    svg.append("g")
      .attr("transform", "translate(150,150)")
      .selectAll("text")
      .data(words)
      .enter().append("text")
      .style("fill", (d, i) => { d.color = fill(i); return d.color; })
      .style("text-anchor", "middle")
      .attr("transform", d => "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")")
      .text(d => d.text)
      .style("font-size", d => d.size + "px");
  }
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://cdn.rawgit.com/jasondavies/d3-cloud/master/build/d3.layout.cloud.js"></script>
<script src="https://d3js.org/d3-scale.v1.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/seedrandom/2.4.4/seedrandom.min.js">
</script>
<svg width="700" height="700"></svg>

The solutions consist in either scaling down the size of your words in order to give d3-layout enough space or increase the width and height of the cloud container.

For instance by scaling down the size of words:

// domain reduced from [0, 20] to [0, 13]:
var sizeScale = d3.scaleLinear().range([0, 13]).domain([0, d3.max(data, d => d.count)]);

Math.seedrandom('hello word');

var data = [{ text: "Hello", count: 38 }, { text: "World", count: 27 }, { text: "Whatever", count: 21 }, { text: "Massive", count: 21 }, { text: "Thing", count: 16 }, { text: "Something", count: 14 }, { text: "What", count: 12 }, { text: "Else", count: 9 }, { text: "Blabla", count: 6 }, { text: "Small", count: 6 }, { text: "VeryLong", count: 6 }, { text: "Word", count: 3 }];

  var svg = d3.select("svg").append("g");

  let fill = d3.scaleOrdinal(d3.schemeCategory20);
  let size = d3.scaleLinear().range([0, 13]).domain([0, d3.max(data, d => d.count)]);

  let word_cloud_data = data
    .map( function(d) {
      return { text: d.text, size: 9 + size(d.count) * 3.5 };
    });

  let layout = d3.layout.cloud()
    .size([275, 275])
    .words(word_cloud_data)
    .padding(2.5)
    .rotate(d => ~~(Math.random() * 2) * -90)
    .fontSize(d => d.size)
    .on("end", draw);

  layout.start();

  function draw(words) {

    svg.append("g")
      .attr("transform", "translate(150,150)")
      .selectAll("text")
      .data(words)
      .enter().append("text")
      .style("fill", (d, i) => { d.color = fill(i); return d.color; })
      .style("text-anchor", "middle")
      .attr("transform", d => "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")")
      .text(d => d.text)
      .style("font-size", d => d.size + "px");
  }
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://cdn.rawgit.com/jasondavies/d3-cloud/master/build/d3.layout.cloud.js"></script>
<script src="https://d3js.org/d3-scale.v1.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/seedrandom/2.4.4/seedrandom.min.js">
</script>
<svg width="700" height="700"></svg>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.