使用 renderlet 在 dc.js 图表的背景中呈现

Rendering in the background of a dc.js chart with renderlet

我使用dc.js来显示多种分类算法的结果。更具体地说,我想展示一个精确召回图表(每个点对应一个分类系统的结果)。 我已经为此使用了 dc.js 散点图,效果很好。 另外我想要一个d3 contour in the background of the chart which shows the F-measure。 这已经实施。唯一的问题是等高线部分在前景而不是图表的背景中。 请查看 jsfiddle for a full example.

有两个问题 仍然对我开放,因为我不是 dc.jsd3 专家:

  1. 有没有办法把等高线放在背景或散点图的符号(周期)放在前景(我已经在这个Whosebug question的帮助下尝试过但没有成功)
  2. 我使用 'g.brush' 选择器来获取内部图表的面积。只要刷亮打开,这就可以正常工作。选择器是一个好方法还是有更好的选择(如果关闭刷牙也可能有效)。

在我的示例中,我将轮廓部分放在左上角以查看它是否有效,但我还提供了代码(当前未注释)以将轮廓的宽度和高度增加到正确的大小。

chart
  .on('renderlet', function (chart) {
    var innerChart = chart.select('g.brush');

    var width = 300, height=300;
    //getting the correct width, height
    //var innerChartBoundingRect = innerChart.node().getBoundingClientRect();
    //var width = innerChartBoundingRect.width, height=innerChartBoundingRect.height;

    [contours, color] = generateFmeasureContours(width,height, 1);

    innerChart
      .selectAll("path")
      .data(contours)
      .enter()
      .append("path")
      .attr("d", d3.geoPath())
      .attr("fill",  d => color(d.value));

    var symbols = chart.chartBodyG().selectAll('path.symbol');
        symbols.moveToFront();
  });

jsfiddle

将某些东西放在背景中是一种通用的 SVG 技能。

SVG 按照声明的顺序从后到前呈现所有内容,因此关键是在语法上将您的内容放在图表中其他所有内容之前。

我建议将它封装在一个 svg <g> 元素中,为了获得正确的顺序,您可以使用 d3-selection 的 insert 方法和 :first-child CSS 选择器而不是 append:

  .on('pretransition', function (chart) {
    // add contour layer to back (beginning of svg) only if it doesn't exist
    var contourLayer = chart.g().selectAll('g.contour-layer').data([0]);
    contourLayer = contourLayer
        .enter().insert('g', ':first-child')
        .attr('class', 'contour-layer')
        .attr('transform', 'translate(' + [chart.margins().left,chart.margins().top].join(',') + ')')
        .merge(contourLayer);

关于此实现的更多要点:

  • 使用 dc 的 pretransition 事件,因为它在渲染和重绘后立即发生(而 renderlet 等待转换完成)​​
  • 模式 .data([0]).enter() 仅在元素不存在时才添加该元素。 (它绑定一个 1 元素数组;该元素是什么并不重要。)这很重要,因为事件处理程序将在每次重绘时被调用,我们不想继续添加层。
  • 我们为我们的图层指定了不同的 class 名称 contour-layer 以便我们可以识别它,因此一次添加模式有效
  • contourLayer = contourLayer.enter().insert(...)...merge(contourLayer) 是另一种常见的 D3 模式,用于插入内容并将其合并回选择中,以便我们稍后将插入和修改视为相同。使用较新的 selection.join 方法可能会更简单,但我还没有尝试过。
  • (我认为在排序方面可能也有一些改进,可能比 insert 更容易,但同样,我将使用我知道有效的方法。)
  • 最后,我们从 margin mixin
  • 中获取左上偏移量

接下来,我们可以使用检索实际图表主体的宽度和高度 (叹息,undocumented)来自 dc.marginMixin 的方法:

var width = chart.effectiveWidth(), height = chart.effectiveHeight();

而且我们不需要将点移到前面或任何其他地方;你的代码的其余部分和以前一样,除了我们使用这个新层而不是绘制到刷层:

contourLayer
  .selectAll("path")
  .data(contours)
  .enter()
  .append("path")
  .attr("d", d3.geoPath())
  .attr("fill",  d => color(d.value));

Fork of your fiddle.

同样,如果您想合作将轮廓示例放入 dc.js,那就太棒了!