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

最近做大屏用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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容