美文网首页让前端飞
echart:地图上标签重叠问题解决

echart:地图上标签重叠问题解决

作者: 小飞牛牛 | 来源:发表于2021-10-28 16:28 被阅读0次

    最近做大屏用echart里面的矢量地图,上面需要显示一些标签。 标签使用常规的做法,即用散点图,坐标则设为地图坐标:

       var seriesData= {
                coordinateSystem: 'geo',
                symbolSize: 12,
                type: 'scatter',
                labelLine: {
                    show: true,
                    length2: 15,
                    smooth: 0.2,
                    minTurnAngle: 90,
                    lineStyle: {
                        color: '#fff',
                        width: 2
                    }
                },
                label: {
                    color: '#fff',
                    backgroundColor: 'rgba(219, 218, 217, 0.8)',
                    borderColor: '#8C8D8E',
                    borderWidth: 1,
                    lineHeight: 25,
                    padding: 0,
                    show: true,
                    position: 'right',
                    rich: {
                    // 此处省略富文本定义
                    }
                },
                labelLayout: {
                    dy: -40,
                    dx: 20,
                    align: 'left'
                },
               data: []
    }
    

    出来的效果如图:


    image.png

    这里有个比较大的问题,标签显示会重叠在一起。我给标签设置了事件,鼠标放上去之后,对应标签会亮起,其它会变暗。然而,客户仍然希望不要出现遮挡的情况。

    查了一下echart文档的scatter部分,labelLayout有两个跟标签重叠相关的参数。


    image.png

    hideOverlap参数会在标签重叠时隐藏一部分。这是一种解决办法,不过客户希望显示所有标签,不要隐藏。


    image.png
    第二个参数是moveOverlap, 在重叠时它能将图标移动放置重叠。这时labelLayout需要以函数的方式传参。
        labelLayout: function(params) {
                    return {
                        x: params.rect.x,
                        moveOverlap: 'shiftY'
                    }
                },
    

    实现的效果如图:


    image.png

    虽然不重叠了,但看起来有点凌乱。而且当图标超过一定数量,其实还是会重叠。所以,它只是优化,尽可能不重叠,但不能确保不重叠。


    image.png
    我想到了echart里面还有个关系图,关系图有力导向布局,图标可以进行排斥,然而试了半天,关系图似乎不能使用地图坐标。而且设置坐标点也挺麻烦的。于是我打算自己设计一个排布算法。

    仍然使用labelLayout参数,然而返回的dx, dy 我需要自己计算。
    我需要有一个方法得到标签的偏移值。

       labelLayout: function(params) {
                    var dPos = posHandler.getDeltaPos(params);
                    return {
                        dx: dPos.dx,
                        dy: dPos.dy
                    }
                },
    

    现在试想一下, 我把地图划分成固定的格子。


    image.png

    如此每一个散点都会在一个格子里面。当labelLayout方法执行时,从params里面可以得到这些参数


    image.png
    其中params.rect是散点映射到画布的矩形对象。通过x,y 除以 格子的长宽取整,可以知道散点在哪个格子。
    如果这个格子是空的,就把它分配给该标签,如果非空,则按照一个九宫格的顺序,在它的附近查找空的格子。如果找一圈找不到,以最后查找的格子作为当前格子继续查找。
    image.png

    函数的主体部分是这样的:

    var posHandler = {
        posMap: {},
        reset: function() {
            this.posMap = {};
        },
        getDeltaPos: function(params){
                var rect = params.rect;
                var labelWidth = 160, labelHeight = 30;
                var gridx = Math.floor(rect.x / labelWidth);
                var gridy = Math.floor(rect.y / labelHeight);
                var currCell = [gridx, gridy], currPos = [];
                var increaseArr = [[0, -1], [1, -1], [1, 0], [1, 1], [0, 1], [-1, -1], [-1, 0], [-1, -1]];
                // 将显示区域划分成一个个定宽和定高的区域
                // 将标签放在离它最近的区域
                // 如果最近的那格没有,向周围九宫格上面找
                // 找到一个没有放过的,分配给它,然后得出一个偏移量
                var found = false;
                // 如果格子已被占,循环查找
                if(this.posMap[currCell[0] + '-' + currCell[1]]) {
                    while(!found) {
                        for(var i = 0;i<increaseArr.length;i++) {
                            currCell[0] = currCell[0] + increaseArr[i][0];
                            currCell[1] = currCell[1] + increaseArr[i][1];
                            if (!this.posMap[currCell[0] + '-' + currCell[1]]) {
                                found = true;
                                this.posMap[currCell[0]+'-'+currCell[1]] = params.text;
                                currPos = [currCell[0]* labelWidth, currCell[1] * labelHeight];
                                break;
                            }
                        }
                        if(found) {
                            break;
                        }
                    }
                } else {
                    // 如果格子没有被占,就它了
                    this.posMap[gridx + '-' + gridy] = params.text
                }
                currPos = [currCell[0]* labelWidth, currCell[1] * labelHeight];
                var deltaPos = {
                    dx: currPos[0] - rect.x,
                    dy: currPos[1] - rect.y
                }
                return deltaPos;
            }
          };
    

    算法的关键在于通过posMap对象记录已经分配的格子,由于在地图缩放时,param.rect坐标会发生变化,所以,要在缩放之后将posMap对象清空再通过调用实例的resize方法重新计算。

      myMap.on('georoam', function() {
                    console.log('resize');
                    posHandler.reset();
                    myMap.resize();
                })
    

    最后实现的效果,不管有多少标签,都会整齐排布,而且缩放之后会自动调整:

    image.png

    相关文章

      网友评论

        本文标题:echart:地图上标签重叠问题解决

        本文链接:https://www.haomeiwen.com/subject/bdsvaltx.html