高图表散点图 - 如何修复重叠的数据标签



我看过很多关于这个主题的帖子,但这个问题似乎从未得到妥善解决。

我们有一个很大的散点,上面有大约 30 个点(没有什么压倒性的)。 但在某些情况下,这些点会非常接近或重叠(我猜我们对此无能为力)。

主要问题是我们希望数据标签始终可见,并且当点彼此靠近时,这些数据标签是重叠的。

我们已经尝试过allowOverlap:false,但这不是我们真正需要/想要的。 我们理想的结果是允许所有数据标签显示在散点内的屏幕上,同时仍然能够随时读取每个数据标签。

我们是通过调整点的分隔还是通过调整数据标签的分隔/填充来解决此问题? 有什么建议吗? 谢谢。

我还没有从Highcharts找到这个问题的工作配置解决方案(尽管我不能保证最新版本中没有)。但是,有一些算法可以接受拆分数据标签的标签坐标随机化。

以下是一些有用的链接,可以帮助您使用算法:

R中的词云包(云。R 是包含算法的文件)

R 中的直接标签包

在 JavaScript 中,R 代码的一些虚拟伪代码翻译将是:

splitLabels: function() {
    // Create an array of x-es and y-es that indicate where your data lie
    var xArr = getAllDataX();
    var yArr = getAllDataY();
    var labelsInfo = {};
    this.chartSeries.forEach(function(el) {
        var text = el.data.name;
        labelsInfo[el.data.id] = {
            height: getHeight(text),
            width: getWidth(text),
            text: text
        };
    }, this);
    var sdx = getStandardDeviation(xArr);
    var sdy = getStandardDeviation(yArr);
    if(sdx === 0) sdx = 1;
    if(sdy === 0) sdy = 1;
    var boxes = [];
    var xlim = [], ylim = [];
    xlim[0] = this.chart.xAxis[0].getExtremes().min;
    xlim[1] = this.chart.xAxis[0].getExtremes().max;
    ylim[0] = this.chart.yAxis[0].getExtremes().min;
    ylim[1] = this.chart.yAxis[0].getExtremes().max;
    for (var i = 0; i < data.length; i++) {
        var pointX = data[i].x;
        var pointY = data[i].y;
        if (pointX<xlim[0] || pointY<ylim[0] || pointX>xlim[1] || pointY>ylim[1]) continue;
        var theta = Math.random() * 2 * Math.PI,
            x1 = data[i].x,
            x0 = data[i].x,
            y1 = data[i].y,
            y0 = data[i].y,
            width = labelsInfo[data[i].id].width,
            height = labelsInfo[data[i].id].height ,
            tstep = Math.abs(xlim[1] - xlim[0]) > Math.abs(ylim[1] - ylim[0]) ? Math.abs(ylim[1] - ylim[0]) / 100 : Math.abs(xlim[1] - xlim[0]) / 100,
            rstep = Math.abs(xlim[1] - xlim[0]) > Math.abs(ylim[1] - ylim[0]) ? Math.abs(ylim[1] - ylim[0]) / 100 : Math.abs(xlim[1] - xlim[0]) / 100,
            r = 0;
        var isOverlapped = true;
        while(isOverlapped) {
            if((!hasOverlapped(x1-0.5*width, y1-0.5*height, width, height, boxes)
                && x1-0.5*width>xlim[0] && y1-0.5*height>ylim[0] && x1+0.5*width<xlim[1] && y1+0.5*height<ylim[1]) )
            {
                boxes.push({
                    leftX: x1-0.5*width,
                    bottomY: y1-0.5*height,
                    width: width,
                    height: height,
                    icon: false,
                    id: data[i].id,
                    name: labelsInfo[data[i].id].text
                });
                data[i].update({
                    name: labelsInfo[data[i].id].text,
                    dataLabels: {
                        x: (x1 - data[i].x),
                        y: (data[i].y - y1)
                    }
                }, false);
                isOverlapped = false;
            } else {
                theta = theta+tstep;
                r = r + rstep*tstep/(2*Math.PI);
                x1 = x0+sdx*r*Math.cos(theta);
                y1 = y0+sdy*r*Math.sin(theta);
            }
        }
    }
    // You may have to redraw the chart here
},

可以在重绘时调用此函数,也可以优化以降低调用频率。

请注意,如果您有一些大点或形状或图标指示您的数据项所在的位置,则必须检查任何建议的解决方案是否也不会干扰(重叠)图标。

您可以尝试调整此算法:

    function StaggerDataLabels(series) {
    sc = series.length;
    if (sc < 2) return;
    for (s = 1; s < sc; s++) {
        var s1 = series[s - 1].points,
            s2 = series[s].points,
            l = s1.length,
            diff, h;
        for (i = 0; i < l; i++) {
            if (s1[i].dataLabel && s2[i].dataLabel) {
                diff = s1[i].dataLabel.y - s2[i].dataLabel.y;
                h = s1[i].dataLabel.height + 2;
                if (isLabelOnLabel(s1[i].dataLabel, s2[i].dataLabel)) {
                    if (diff < 0) s1[i].dataLabel.translate(s1[i].dataLabel.translateX, s1[i].dataLabel.translateY - (h + diff));
                    else s2[i].dataLabel.translate(s2[i].dataLabel.translateX, s2[i].dataLabel.translateY - (h - diff));
                }
            }
        }
    }
}
//compares two datalabels and returns true if they overlap

function isLabelOnLabel(a, b) {
    var al = a.x - (a.width / 2);
    var ar = a.x + (a.width / 2);
    var bl = b.x - (b.width / 2);
    var br = b.x + (b.width / 2);
    var at = a.y;
    var ab = a.y + a.height;
    var bt = b.y;
    var bb = b.y + b.height;
    if (bl > ar || br < al) {
        return false;
    } //overlap not possible
    if (bt > ab || bb < at) {
        return false;
    } //overlap not possible
    if (bl > al && bl < ar) {
        return true;
    }
    if (br > al && br < ar) {
        return true;
    }
    if (bt > at && bt < ab) {
        return true;
    }
    if (bb > at && bb < ab) {
        return true;
    }
    return false;
}

http://jsfiddle.net/menXU/6/

最新更新