百度地图聚合功能自定义聚合文字

缘起

近日, 产品提了个新的功能,需要用到地图点聚合功能。 了解了 leaflet 框架,是基于地图瓦片实现的,但是我司要求使用百度地图实现该功能, 发现 leaflet 对天地图、高德比较友好。 对百度地图支持度并不好,遂弃用。

后来,研究百度聚合demo发现,百度聚合只支持自定义样式,并不支持自定义文字。

最终,我发现百度地图聚合功能是第三方库实现的,可以自己下载下来, 重写聚合设置聚合文字相关代码及功能。 简单记录如下,希望对以后有需要的童鞋有所帮助吧。

分析

研究百度地图聚合源码发现,如果打算自定义文字需要修改两处:
一、修改 BMapLib.MarkerClusterer.js 插件中 Cluster.prototype.updateClusterMarker 方法,实现过程如下:

/**
 * 更新该聚合的显示样式,也即TextIconOverlay。
 * @return 无返回值。
 */
Cluster.prototype.updateClusterMarker = function () {
    if (this._map.getZoom() > this._markerClusterer.getMaxZoom()) {
        this._clusterMarker && this._map.removeOverlay(this._clusterMarker);
        for (var i = 0, marker; marker = this._markers[i]; i++) {
            this._map.addOverlay(marker);
        }
        return;
    }

    if (this._markers.length < this._minClusterSize) {
        this._clusterMarker.hide();
        return;
    }

    this._clusterMarker.setPosition(this._center);
    
    this._clusterMarker.setText(this._markers.length);

    var thatMap = this._map;
    var thatBounds = this.getBounds();
    this._clusterMarker.addEventListener("click", function(event){
        thatMap.setViewport(thatBounds);
    });
};

方法中: this._clusterMarker.setText(this._markers.length); 表示, 聚合点显示的文字内容是 当前 Cluster 类 _markers 属性 的 长度;

二、修改 TextIconOverlay.js 中的 构造方法 和 样式方法

查看 TextIconOverlay.js 的源码发现, text 只支持 数字类型的值,而且和 styles 属性绑定,需要修改constructor 方法, 和 设置属性的方法。

性空

如上,知道了聚合的文字是 Cluster.prototype.updateClusterMarker 方法 , 那么我们需要进行以下步骤:

1、把百度地图的聚合库存到自己的项目;
2、不要使用百度地图默认的maker,改用自己定义的复杂覆盖物;
3、重写 updateClusterMarker 方法;
4、重写 TextIconOverlay.js 的 setText() 方法;
5、引用类聚合文件,由百度的官方网站,改为自己的项目路径;
6、大功告成。

  • 第一步,自己操作即可;
  • 第二步,自定义自己的复杂覆盖物,传入自定义 text
// 引入复杂覆盖物监听插件
import EventWrapper from './EventWrapper.js';

//合并参数
function extend(o, n, override) {
    for(let key in n){
        if(n.hasOwnProperty(key) && (!o.hasOwnProperty(key) || override)){
            o[key]=n[key];
        }
    }
    return o;
}

//复杂的自定义覆盖物
function ComplexCustomOverlay(opts){
    let def = {
        _point: '',
        _text: '',
        _initalizeCallBack: function () {
            let div = document.createElement("div");
            div.className  = 'map-ha-popbox';
            let span = document.createElement("span");
            span.className = 'name';
            let i = document.createElement("i");
            span.appendChild(document.createTextNode(this.getOption('_text')));
            span.appendChild(i);
            div.appendChild(span);
            return div;
        }, //initalize的回调方法。
        _eventCallBack: function () {
            //覆盖物事件监听的回调方法(渲染到地图上时才可调用):点击等事件用 EventWrapper插件
            let me = this;
            EventWrapper.addDomListener(this._dom, "touchend", function() {
                //这里 me 是 复杂覆盖物本身
                console.log(me);
                //注意: 这里this 指的是 dom 元素,不是复杂覆盖物
                console.log(this);
            });
        } // 相关事件的回调方法
    };
    this._setting = extend(def, opts, true);
};
//复杂覆盖物实例,继承自百度 overlay
ComplexCustomOverlay.prototype = new BMap.Overlay();
//重写 initialize 方法
ComplexCustomOverlay.prototype.initialize = function(map){
    this._map = map;
    this._dom = '';
    if (this._setting['_initalizeCallBack'] && typeof(this._setting['_initalizeCallBack']) == 'function') {
        this._dom = this._setting['_initalizeCallBack'].call(this);
        this._map.getPanes().labelPane.appendChild(this._dom);
    }
    this._cwidth = this._dom.clientWidth;
    this._cheight = this._dom.clientHeight;
    //获取到div元素之后,才可以添加事件回调方法
    if (this._setting['_eventCallBack'] && typeof(this._setting['_eventCallBack']) == 'function') {
        this._setting['_eventCallBack'].call(this);
    }
    //这里必须返回DOM元素,clearOverlays方法会清除这里的dom元素
    return this._dom;
}
//重写 draw 方法
ComplexCustomOverlay.prototype.draw = function(){
    this._map && this._updatePosition();
}
ComplexCustomOverlay.prototype._updatePosition = function() {
    if (this._dom && this.getOption('_point')) {
        let pixel = this._map.pointToOverlayPixel(this.getOption('_point'));
        let iObj = this._div.getElementsByTagName('i')[0];
        this._div.style.left = pixel.x - this._cwidth/2 + "px";
        // 多个overlay 循环 渲染的时候,非第一个overlay会有个 offsetTop 值
        this._div.style.top  = pixel.y - this._div.offsetTop - this._cheight - iObj.offsetHeight + "px";
    }
};
ComplexCustomOverlay.prototype.getNum = function(){
    return this.getOption('_num');
}
ComplexCustomOverlay.prototype.getOption = function(key) {
    return this._setting[key];
}

export default ComplexCustomOverlay;

通过查看百度地图聚合源码,发现 Cluster 类调用了 markergetPositiongetMap 方法,我们需要自定义这俩方法如下:

//在调用聚合方法时会将会调用标注的getPosition和getMap方法
ComplexCustomOverlay.prototype.getMap = function(){
    return this._map;
};
ComplexCustomOverlay.prototype.getPosition = function(){
    return this.getOption('_point');
};
// 获取自定义text,不建议通过 marker._setting['_text'] 获取
ComplexCustomOverlay.prototype.getText = function(){
    return this.getOption('_text');
};
  • 第三步,重写 Cluster.prototype.updateClusterMarker 方法:
/**
 * 更新该聚合的显示样式,也即TextIconOverlay。
 * @return 无返回值。
 */
Cluster.prototype.updateClusterMarker = function () {
    if (this._map.getZoom() > this._markerClusterer.getMaxZoom()) {
        this._clusterMarker && this._map.removeOverlay(this._clusterMarker);
        for (var i = 0, marker; marker = this._markers[i]; i++) {
            this._map.addOverlay(marker);
        }
        return;
    }

    if (this._markers.length < this._minClusterSize) {
        this._clusterMarker.hide();
        return;
    }

    this._clusterMarker.setPosition(this._center);
    
    // 这里重写 
    // this._clusterMarker.setText(this._markers.length);
    // 自定义文字
    var _text = "";
    for (var i = 0, marker; marker = this._markers[i]; i++) {
        //自定义文字,这里简单连接
        _text += marker.getText() + "<br>"; // setText 方法是 TextOverlay 的 innerHtml 赋值, 这里支持 html 标记
    }
    this._clusterMarker.setText(_text);

    var thatMap = this._map;
    var thatBounds = this.getBounds();
    this._clusterMarker.addEventListener("click", function(event){
        thatMap.setViewport(thatBounds);
    });
};
  • 第四步,文字覆盖物 TextOverly ,重写 setText 方法:
TextOverly.prototype.setText = function(text) {
    if(text && (!this._text || (this._text.toString() != text.toString()))){
        this._text = text;
        this._updateText(); // 更新内容
        this._updatePosition();  // 渲染样式
    }
};

TextOverly.prototype._updateText = function() {
        if (this._div) {
            let spanDom = this._div.getElementsByTagName('span')[0];
            spanDom.innerHTML = this._text;
            this._cwidth = this._div.clientWidth;
            this._cheight = this._div.clientHeight;
        }
 };

TextOverly.prototype._updatePosition = function() {
    if (this._div && this._point) {
        let pixel = this._map.pointToOverlayPixel(this._point);
        this._div.style.position = "absolute";
        this._div.style.left = pixel.x - Math.ceil(parseInt(this._cwidth) / 2) + "px";
        this._div.style.top  = pixel.y - Math.ceil(parseInt(this._cheight)/ 2) + "px";
    }
};
  • 第五步、第六步略。

最终,我们实现了自定义 百度地图点聚合文字 信息。

附Github: https://github.com/sphenginx/vue-slider

注: 代码库并不是修改的 TextIconOverlay覆盖物,而是自定义了 SVGIconOverlay 覆盖物, 基于SVG的,可以自定义文字 和 填充颜色 等。
聚合类 Cluster_clusterMarkerTextIconOverlay 改为 SVGIconOverlay。 具体可以查看源码
欢迎使用及PR

附:

类 BMapLib.MarkerClusterer

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