D3 折线图标签重叠问题

4
我根据这里的示例创建了一条折线图:

http://bl.ocks.org/mbostock/3884955

然而,使用我的数据时,由于不同线的y轴最终值经常非常接近,因此线标签(城市)会重叠。我知道我需要比较每条线的最后一个值,并在值相差12个单位或更少时将标签向上或向下移动。我的想法是查看由此代码块编写的文本标签:
city.append("text")
.datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) { return "translate(" + x(d.value.date) + "," +  y(d.value.temperature) + ")"; })
.attr("x", 3)
.attr("dy", ".35em")
.text(function(d) { return d.name; });

如果y(d.value.temperature)的值相差不超过12,请将这些值分开,直到它们之间至少有12个单位。对于如何完成此操作,您有何想法?这是我的第一个d3项目,语法仍然让我头痛!
2个回答

2

最好一次性传入所有标签--这也更符合d3的概念。你可以使用类似下面的代码:

svg.selectAll("text.label").data(data)
   .enter()
   .append("text")
   .attr("transform", function(d, i) {
       var currenty = y(d.value.temperature);
       if(i > 0) {
           var previousy = y(data[i-1].value.temperature),
           if(currenty - previousy < 12) { currenty = previousy + 12; }
       }
       return "translate(" + x(d.value.date) + "," +  currenty + ")";
   })
   .attr("x", 3)
   .attr("dy", ".35em")
   .text(function(d) { return d.name; });

这并没有考虑到之前的标签可能已经被移动了。您可以显式获取先前标签的位置,并根据该位置移动当前标签。代码几乎相同,只是您需要保存对当前元素(this)的引用,以便稍后访问。

所有这些都不能防止标签与它们最终标记的行之间可能相距甚远。如果您需要移动每个标签,则最后一个标签将非常远。更好的做法可能是单独创建图例,在其中可以根据需要调整标签和线条的间距。


除非我漏了什么,否则此代码只检查相邻行的标签是否重叠。例如,如果第1行的标签和第3行的标签重叠,这将不会被此程序检测并修复。如果我对此代码的作用有误解,请告诉我。谢谢! - DG_Montana
你是正确的 - 如果考虑前一个标签的位置而不是前一个数据点的位置,那么这将被考虑在内。我的观点是,如果您在标签放置方面遇到问题,最好单独使用图例。 - Lars Kotthoff
听取您的意见了解到了...谢谢您让我放心,我没有错过什么简单的东西。 - DG_Montana

2
考虑使用D3力导向布局来放置标签。可以在这里看到一个示例:https://bl.ocks.org/wdickerson/bd654e61f536dcef3736f41e0ad87786 假设您有一个包含具有“value”属性的对象的数据数组和一个比例尺“y”:
// Create some nodes
const labels = data.map(d => {
  return {
    fx: 0,
    targetY: y(d.value)
  };
});

// Set up the force simulation
const force = d3.forceSimulation()
  .nodes(labels)
  .force('collide', d3.forceCollide(10))
  .force('y', d3.forceY(d => d.targetY).strength(1))    
  .stop();

// Execute thte simulation
for (let i = 0; i < 300; i++) force.tick();

// Assign values to the appropriate marker
labels.sort((a, b) => a.y - b.y);
data.sort((a, b) => b.value - a.value);
data.forEach((d, i) => d.y = labels[i].y);

现在你的data数组将有一个表示其最佳位置的y属性。
示例使用D3 4.0,更多信息请阅读此处:https://github.com/d3/d3-force

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接