在多系列折线图上有条件地显示标签

时间:2014-06-26 08:39:52

标签: d3.js charts

multi series line chart的d3示例中,所有系列标签都显示在图表的最右侧。我感兴趣的是改变它,以便它们有条件地显示在图表的某些部分,或者根本不显示。

因此,使用该示例,如果我希望标签在每个系列的最大值上显示并且与每个系列的颜色相同(可能类似于this),那么我该如何实现呢?我可以认为它与块

有关
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; });

但我无法解决这个问题。我已尝试使用IndexOf或MaxOf功能,但无法在该上下文中工作。意识到这可能会导致图形上出现一些重叠,但我可以稍后处理。奖励指向任何能够展示如何添加带有指向最大值的条件标签的人。

谢谢:)

1 个答案:

答案 0 :(得分:0)

没有注意图表的可读性,一个解决方案可能是首先添加一个函数:

function find_max_date(max,values)
{
for (i=0;i<values.length;i++)
{
if (values[i].temperature==max)
{
return i;
break;
}
}
}

并使用此代码:

city.append("text")
      .datum(function(d) {return {name: d.name, max: d3.max(d.values,function(d){return d.temperature}), max_date: d.values[find_max_date(d3.max(d.values,function(d){return d.temperature}),d.values)].date };})
      .attr("transform", function(d) {return "translate(" + x(d.max_date) + "," + y(d.max) + ")"; })
      .attr("x", 3)
      .attr("dy", ".35em")
      .text(function(d) { return d.name; }).attr("fill",function(d) {return color(d.name);});

所以基本上,这会将max和max_data添加到data-object - max,只需使用d3.max-method查找最高温度,并通过循环遍历每个城市的原始数据并返回最大位置来max_data数组中的温度,用于访问相应的日期。颜色的添加方式与添加到线条的方式相同。

编辑:不确定“奖金”问题,结果应该是什么样的?指向每个城市的最大值的线?

edit2:奖金问题的可能解决方案,因为我很好奇,如果它是可行的,但可能有更好的方法来做到这一点。我更改了代码的不同部分,因此您可以删除函数find_max_date。

在脚本开头我添加了一个全局变量

var offset=20;

用作控制彼此之间和边界之间标签距离的变量。然后,在cities-color-domain-part和scale-domains之间,我添加了以下代码:

for (i=0;i<cities.length;i++)
{
  cities[i].max=0;
  cities[i].label_off=0;
  for (j=0;j<cities[i].values.length;j++)
  {
    if (cities[i].values[j].temperature>cities[i].max) 
    {
      cities[i].max=cities[i].values[j].temperature;
      cities[i].max_date=cities[i].values[j].date;
    }
  }
}

这与我们之前使用d3.max以及查找最高温度日期的自定义函数基本相同,只是这次我建议将值直接添加到cities-variable中。 cities.label_off将是一个在标签之间放置一些空间的值,否则它们会重叠。

接下来,我添加了另一个循环来比较x-scale上的值以检查它们是否重叠。简单地说,如果两个x值之间的距离小于100个像素,则label_off-value设置为offset-value(在这种情况下为20像素),否则label_off-value保持为0.您可能需要调整这取决于标签的短/长。

for (i=0;i<cities.length;i++)
{
  for (j=i+1;j<cities.length;j++)
  {
    if (Math.abs(x(cities[i].max_date)-x(cities[j].max_date))>0 && Math.abs(x(cities[i].max_date)-x(cities[j].max_date))<100) cities[j].label_off=offset;
  }
}

接下来,我将偏移变量添加到y域,只是为了有更多的空间来放置标签:

y.domain([
    d3.min(cities, function(c) { return d3.min(c.values, function(v) { return v.temperature; }); }),
    d3.max(cities, function(c) { return d3.max(c.values, function(v) { return v.temperature; })+offset; })  
  ]);

最后添加标签和线条的部分。由于cities-variable已经包含了您不需要使用其他datum所需的所有必要信息,因此只需使用属性名称对其进行处理:

city.append("text")
  .attr("transform", function(d) {return "translate(" + (x(d.max_date)+30+d.label_off) + "," + (offset+d.label_off) + ")"; })
  .attr("x", 3)
  .attr("dy", ".35em")
  .text(function(d) { return d.name; })
  .attr("fill",function(d) {return color(d.name);});

city.append("line")
  .attr("class","line_con")
  .attr("x1",function(d){return x(d.max_date);})
  .attr("y1",function(d){return y(d.max);})
  .attr("x2",function(d){return (x(d.max_date)+30+d.label_off);})
  .attr("y2",function(d){return (offset+d.label_off);})
  .attr("stroke",function(d) {return color(d.name);});

如您所见,变换只是将标注放在x刻度上最高温度日期的位置。然后它增加30,这是一个任意值,只是将它们稍微向右移动,因此它们不会直接位于值的上方。 d.label_off如果没有重叠则加0,在我们的情况下如果有则为20。

在y尺度上的

我将它们定位在图表的顶部,它们之间有一些空间和边界使用偏移量。再次,如果有重叠,它们会向下移动一点。

对于线条,它只是连接标签的最大值和起始点。他们使用不同的css类来允许相应的颜色:

.line_con {
  fill: none;
  stroke: none;
  stroke-width: 1.5px;

再一次,可能有更好的方法来实现它,我不确定这是否适用于所有类型的数据,但它可能会让你知道如何处理它。