Перекрытие в d3 wordcloud

Я использую библиотеку wordcloud Джейсона Дэвиса для d3 (https://github.com/jasondavies/d3-cloud) и моя проблема в том, что слова в облаке перекрываются.

Я знаю, что уже есть вопросы по этой проблеме с переполнением стека (и на других сайтах), но в моем случае ни один из них не помог.

В следующем примере я использовал пример облака с сайта Джейсона Дэвиса и изменил только несколько вещей:

  • Я читаю свои слова и их размеры из внешнего файла.
  • Я установил вращение на 0. Угол поворота, похоже, не имеет значения.
  • Я закомментировал шрифт «Impact», чтобы исключить любые проблемы с загрузкой шрифта. (Хотя это тоже не имеет значения.)

Вот мой код:

<!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>

Тестовые данные выглядят следующим образом (информация о цвете в примере не используется):

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-скрипт с тестовыми данными, мое облако слов выходит с перекрытиями. Иногда это происходит только после нескольких перезагрузок, но довольно часто.

Другие люди сообщили о той же проблеме и обнаружили, что она связана с использованием веб-шрифтов или пропуском параметра поворота. В моем примере это не применяется.

Я подозреваю, что это может быть связано с тем фактом, что для размера холста слишком много слов, однако я также провел тесты, в которых я значительно увеличил размер холста, и это все равно происходило (хотя и реже, поскольку случайное размещение слов сделал это менее вероятным). Кроме того, вы можете видеть, что некоторые слова вообще не отображаются из-за небольшого размера холста. Зачем оставлять одни и создавать дублирование других? Так что я думаю, проблема в другом.

Любые идеи?

Спасибо!


person spaebrun    schedule 09.05.2014    source источник
comment
Согласились, что есть трудности. Однако, немного поиграв с размерами (макет и svg) и семейством шрифтов, можно достичь лишь случайных и относительно умеренных совпадений (но насколько приемлемо это совпадение, может быть немного субъективным). В любом случае, вот кусок, который я создал, чтобы поиграть с ним. По крайней мере, я считаю, что эти размеры улучшились за счет исключения слов ... похоже, что большинство из них есть (хотя они должны быть все там).   -  person FernOfTheAndes    schedule 10.05.2014
comment
Да, спасибо. Вы меняли размеры и шрифт, насколько я понимаю? Для примера это работает вполне нормально. Однако мне очень хотелось бы решить проблему в целом. На самом деле я очень удивлен, что это происходит, поскольку весь смысл макета облака слов должен состоять в том, чтобы избежать дублирования, и я получил много похвал за это.   -  person spaebrun    schedule 12.05.2014
comment
Ты прав. Это все, что я сделал. И, согласитесь с вами, нужно общее решение. Возможно, мы что-то упускаем ... У меня не было времени вникать в это слишком глубоко. Вы также можете написать письмо Джейсону.   -  person FernOfTheAndes    schedule 12.05.2014


Ответы (3)


В итоге я спросил самого Джейсона Дэвиса, и на самом деле это была довольно простая ошибка: вы должны указать функцию доступа к тексту в первом операторе (а не только в функции "рисования"). Это сработает, если вы добавите одну строку вроде этой:

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();
person spaebrun    schedule 12.05.2014
comment
Нет, он просто изменил размер распределения слов на экране и сделал поле еще меньше, что привело к большему наложению - person dresh; 06.02.2017
comment
ОБЩАЯ ПРОБЛЕМА: текст SVG не привязан к центру, .attr("text-anchor", "middle") - person Alok Pepakayala; 30.08.2017
comment
проблема остается в моем случае - person bumbeishvili; 23.09.2017

Исправление автора у меня не сработало. Что сделал, так это указание .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')
person papercowboy    schedule 19.12.2018

Я попробовал образец, с которым вы можете поиграть, пожалуйста, посмотрите. wordcloud без перекрытия

по сути:

<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);
person dresh    schedule 08.02.2017