Highcharts Scatter Plot - 如何修复重叠数据标签?

时间:2015-04-14 10:07:24

标签: highcharts overlap scatter-plot

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

我们有大约30分的大分散(没有压倒性的)。但在某些情况下,这些点会非常接近或重叠(我猜我们真的不能做到这一点)。

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

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

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

2 个答案:

答案 0 :(得分:2)

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

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

wordcloud package in R(cloud.R是包含算法的文件)

direct labels package in R

R代码的JavaScript中的一些伪伪代码转换将是:

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
},

您可以在重绘时调用此功能,也可以优化调用此功能。

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

答案 1 :(得分:0)

您可以尝试调整此算法:

    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/