在d3词云重叠

时间:2014-05-09 16:24:18

标签: javascript d3.js overlap word-cloud

我使用Jason Davies' d3(https://github.com/jasondavies/d3-cloud)的wordcloud库,我的问题是云中的单词重叠。

我知道在堆栈溢出(和其他站点)上已经存在关于此问题的问题,但在我的情况下这些都没有帮助。

在下面的示例中,我使用了来自Jason Davies的示例云'网站只改变了一些事情:

  • 我从外部文件中读取了我的文字及其大小。
  • 我将旋转设置为0.旋转角度似乎没有什么区别。
  • 我评论了"影响"字体,以排除加载字体的任何问题。 (尽管如此,它没有任何区别。)

这是我的代码:

<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="d3.js"></script>
<script src="d3.layout.cloud.js"></script>
<script>
   d3.tsv("testdata.txt", 
  function(error, data) {

  var fill = d3.scale.category20();



  d3.layout.cloud().size([300, 300])
      .words(data)
      .padding(1)
      .rotate(function(d) { return 0; })
  //    .font("Impact")
      .fontSize(function(d) { return d.size; })
      .on("end", draw)
      .start();

  function draw(words) {
    d3.select("body").append("svg")
        .attr("width", 300)
        .attr("height", 300)
      .append("g")
        .attr("transform", "translate(150,150)")
      .selectAll("text")
        .data(words)
      .enter().append("text")
        .style("font-size", function(d) { return d.size + "px"; })
    //    .style("font-family", "Impact")
        .style("fill", function(d, i) { return fill(i); })
        .attr("text-anchor", "middle")
        .attr("transform", function(d) {
          return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
        })
        .text(function(d) { return d.word; });
  }
  }
  )

</script>

testdata看起来像这样(示例中没有使用颜色信息):

word    size    color
der 39  #a9a9a9
die 37  #a9a9a9
und 30  #a9a9a9
athenischen 29  #a9a9a9
Die 29  #a9a9a9
eine    28  #a9a9a9
,   27  #a9a9a9
einer   26  #a9a9a9
attischen   26  #a9a9a9
liberalen   26  #1e90ff
zur 25  #a9a9a9
athenische  24  #a9a9a9
christliche 23  #a9a9a9
attische    23  #a9a9a9
_START_ 22  #a9a9a9 
reinen  22  #a9a9a9
englischen  21  #a9a9a9 
oder    21  #a9a9a9
--  21  #a9a9a9
radikalen   21  #a9a9a9
Q*M 21  #a9a9a9
Q*M 21  #a9a9a9
christlichen    20  #a9a9a9
schöne  20  #1e90ff
repräsentativen 20  #a9a9a9
sozialen    20  #a9a9a9
hellenische 19  #1e90ff
modernen    19  #a9a9a9
radikale    19  #a9a9a9
griechische 19  #a9a9a9
-   18  #a9a9a9
schönen 18  #1e90ff
alle    18  #a9a9a9
radicalen   18  #a9a9a9
als 17  #a9a9a9
neuen   17  #a9a9a9
perikleischen   16  #a9a9a9
bürgerlichen    16  #a9a9a9
Namen   16  #1e90ff

如果我用测试数据运行js脚本,我的文字云会出现重叠。有时它只会在重新加载后发生,但它很频繁。

其他人报告了同样的问题,发现它与使用网络字体或跳过rotate参数有关。这不适用于我的示例。

我怀疑它可能与画布大小有很多单词这一事实有关,但是,我也做了测试,我显着增加了画布尺寸并且它仍然发生(虽然不常见,随机放置这些话使它不太可能)。除此之外,由于画布尺寸较小,您可以看到几个单词都没有显示。为什么要留下一些并为其他人创造重叠?所以我认为问题出在其他地方。

有什么想法吗?

谢谢!

3 个答案:

答案 0 :(得分:16)

我最后问了Jason Davies本人,这实际上是一个非常简单的错误:你必须在第一个语句中指定文本访问器函数(不仅仅是在&#34; draw&#34;函数中)。如果您添加如下所示的一行,它可以工作:

d3.layout.cloud().size([300, 300])
  .words(data)
  .padding(1)
  .rotate(function(d) { return 0; })
//    .font("Impact")
  .text(function(d) { return d.word; }) // THE SOLUTION
  .fontSize(function(d) { return d.size; })
  .on("end", draw)
  .start();

答案 1 :(得分:0)

我试过一个样本供你摆弄, 请看一看。 wordcloud without overlap

基本上:

<div id="cloud"></div> 

// First define your cloud data, using `text` and `size` properties:


var fill = d3.scale.category20();
var words = {
"Battery Related": "52382",
"Billing": "52412",
"Break Related": "52490",
"Chain Related": "52471",
"Clutch Related": "52468",
"Dealer attitude": "52488",
"Electrical Related": "52352",
"Engine Related": "52446",
"Handle Bar Related": "52486",
"Happy": "52472",
"Jerking": "52325",
"Jerking Problem": "52325",
"Low Mileage": "52489",
"Noise": "52462",
"Poor Pickup": "52406",
"Running Off": "52242",
"Service Quality": "52488",
"Silencer Problem": "52468",
"Starting Trouble": "52490",
"Suspension Related": "52365",
"Vehicle Noise": "52467",
"Vibration": "52463",
"Washing": "52488"
};
var max_freq = 52490;
var cloudwords = ["Battery Related", "Billing", "Break Related", "Chain Related", "Clutch Related", "Dealer attitude", "Electrical Related", "Engine Related", "Handle Bar Related", "Happy", "Jerking", "Jerking Problem", "Low Mileage", "Noise", "Poor Pickup", "Running Off", "Service Quality", "Silencer Problem", "Starting Trouble", "Suspension Related", "Vehicle Noise", "Vibration", "Washing"];
var url = 'http://xxx.yyyy.zz.ww/?q=abc/';
var width = 800,
height = 800;

var leaders = cloudwords
.map(function(d) {

return {
  text: d,
  size: 5 + (words[d] / max_freq) * 0.9 * 30 // *the size of the "box" occupied by each word. has no relation to text size.
};
})
 .sort(function(a, b) {
 return d3.descending(a.size, b.size)
});

var leaderScale = d3.scale.linear().range([1, 20]); // *scale range to plot the relative sizes of the words.

leaderScale.domain([d3.min(leaders, function(d) {
return d.size;
}),
d3.max(leaders, function(d) {
return d.size;
})
]);

 // Next you need to use the layout script to calculate the placement, rotation and size of each word:

 d3.layout.cloud().size([width, height])
.words(leaders)
.padding(0) //fiddle with padding here, does not really have any effect    on overlap.
.rotate(function() {
 return ~~0; //to keep the words horizontal 
 })
 .font("Impact")
 .fontSize(function(d) {
 return d.size;
 })
 .on("end", drawCloud)
 .start();

 function drawCloud(words) {
 d3.select("#cloud").append("svg")
 .attr("width", width)
 .attr("height", height)
 .attr("text-align", "center")
 .append("g")
 .attr("transform", "translate(" + [width >> 1, height >> 1] + ")")    //for transalting words to their different postions.
 .selectAll("text")
 .data(words)
 .enter().append("text")
 .style("font-size", function(d) {
  return leaderScale(d.size) + "px"; //used scale to resize words to a linear scale.
 })
 .style("font-family", "Impact")
 .style("fill", function(d, i) {
  return fill(i);
 })
 .attr("text-anchor", "middle")
 .attr("transform", function(d) {
  return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
 })
 .text(function(d) {
  return d.text;
 })
 .on("click", function(d, i) {
  window.open(url + d.text);
 });
 }

 // set the viewbox to content bounding box (zooming in on the content, effectively trimming whitespace)

 var svg = document.getElementsByTagName("svg")[0];
 var bbox = svg.getBBox();
 var viewBox = [bbox.x, bbox.y, bbox.width, bbox.height].join(" ");
 svg.setAttribute("viewBox", viewBox);

答案 2 :(得分:0)

作者的修复对我不起作用。 d 的工作是在云布局设置中指定.font() ,并在绘图代码中声明相同的字体系列-讽刺的是,以上作者已将其注释掉。这为代码提供了用于计算尺寸的字体。没有它,就只能做出最佳猜测。

d3.layout.cloud().size([800, 400])
  .words(words)
  .font('Impact') // <-- what mattered
  .fontSize(d => d.size)
  .on('end', draw)
  .start()

您还需要 draw()函数中指定字体系列,以便云以正确的字体呈现与您上面声明的字体匹配的单词:

.attr('font-family', 'Impact')