百度地图大量点聚合js源码分析

百度地图中的点聚合主要有两个文件一个控制聚合即MarkerClusterer.js,一个控制显示的样子TextIconOverlay.js,这里吐槽一下,官方给的文档并没有说明!!!

http://api.map.baidu.com/library/TextIconOverlay/1.2/src/TextIconOverlay_min.js(无格式压缩版)

http://api.map.baidu.com/library/TextIconOverlay/1.2/src/TextIconOverlay.js

http://api.map.baidu.com/library/MarkerClusterer/1.2/src/MarkerClusterer_min.js(无格式压缩版)

http://api.map.baidu.com/library/MarkerClusterer/1.2/src/MarkerClusterer.js

先看MarkerClusterer,一般创建聚合时的代码如下.这里的for循环实际上在随机的创建一些经纬度坐标,

var markers = [];
for(i=0; i < 10000; i++) {
	 pt = new BMap.Point(Math.random() * 5 + 75, Math.random() * 10 + 21);
	 markers.push(new BMap.Marker(pt));
}
var st = [ { url: "m4.png", size:new BMap.Size(92, 92) } ];
var markerClusterer = new BMapLib.MarkerClusterer(map, {markers:markers,styles:st});

对应到源码部分

/**
     *@exports MarkerClusterer as BMapLib.MarkerClusterer
     */
    var MarkerClusterer =
        /**
         * MarkerClusterer
         * @class 用来解决加载大量点要素到地图上产生覆盖现象的问题,并提高性能
         * @constructor
         * @param {Map} map 地图的一个实例。
         * @param {Json Object} options 可选参数,可选项包括:<br />
         *    markers {Array<Marker>} 要聚合的标记数组<br />
         *    girdSize {Number} 聚合计算时网格的像素大小,默认60<br />
         *    maxZoom {Number} 最大的聚合级别,大于该级别就不进行相应的聚合<br />
         *    minClusterSize {Number} 最小的聚合数量,小于该数量的不能成为一个聚合,默认为2<br />
         *    isAverangeCenter {Boolean} 聚合点的落脚位置是否是所有聚合在内点的平均值,默认为否,落脚在聚合内的第一个点<br />
         *    styles {Array<IconStyle>} 自定义聚合后的图标风格,请参考TextIconOverlay类<br />
         */
        BMapLib.MarkerClusterer = function(map, options){
            if (!map){
                return;
            }
            this._map = map;
            this._markers = [];
            this._clusters = [];

            var opts = options || {};
            this._gridSize = opts["gridSize"] || 60;
            this._maxZoom = opts["maxZoom"] || 18;
            this._minClusterSize = opts["minClusterSize"] || 2;
            this._isAverageCenter = false;
            if (opts['isAverageCenter'] != undefined) {
                this._isAverageCenter = opts['isAverageCenter'];
            }
            this._styles = opts["styles"] || [];

            var that = this;
            this._map.addEventListener("zoomend",function(){
                that._redraw();
            });

            this._map.addEventListener("moveend",function(){
                that._redraw();
            });

            var mkrs = opts["markers"];
            isArray(mkrs) && this.addMarkers(mkrs);
        };

可以看到源码中BMapLib.MarkerClusterer = function(map, options)的参数,第一个为map,第二个为options提供了很多选项.每个选项上面说的很清楚了.代码里也用opts["xxx"]来取出其中的数据.||后面意思为,不写的属性默认值.可以看到源码最后两行

var mkrs = opts["markers"];
isArray(mkrs) && this.addMarkers(mkrs);

实际上是把创建MarkerClusterer时候传入的marker数组拿出来,先判断是否是一个Array即数组,再调用addMarkers方法进行具体操作.我们看到addMarkers方法的源码里,可以看到,其对数组进行了一个遍历添加的过程.最后进行this._createClusters()的具体操作.

/**
 * 添加要聚合的标记数组。
 * @param {Array<Marker>} markers 要聚合的标记数组
 *
 * @return 无返回值。
 */
MarkerClusterer.prototype.addMarkers = function(markers){
    for(var i = 0, len = markers.length; i <len ; i++){
        this._pushMarkerTo(markers[i]);
    }
    this._createClusters();
};

插播一个问题:聚合物难以清除的问题

网上给的解决方法大多是

如果要清除必须用markerClusterer.clearMarkers()清除,map.clearOverlays()拖动的时候会再出现

但实际操作来看的话如果想删除再生产是不行的.这里给出一个解决方案.

先初始化一个MarkerClusterer

//初始化打点数据
var markers=[];
//初始化点聚合
var markerClusterer=new BMapLib.MarkerClusterer(map, {markers:markers});

可以用markerClusterer.clearMarkers()清除,后面如果需要再添加,不用再new BMapLib.MarkerClusterer 可以直接使用markerClusterer.addMarkers.其实源码里也可以看出来,它也是这么做的.源码在创建BMapLib.MarkerClusterer最后也是调用了this.addMarkers方法.

另外:这里要注意最后一句话 styles {Array<IconStyle>} 自定义聚合后的图标风格,请参考TextIconOverlay类.有些人疑惑我们并没有创建TextIconOverlay,那么更改样式图片等行为是怎么实现的,实际上这个便是两个js文件联系的地方.那么我们就在当前MarkerClusterer.js文件找TextIconOverlay有没有出现.发现如下

 /**
     * @ignore
     * Cluster
     * @class 表示一个聚合对象,该聚合,包含有N个标记,这N个标记组成的范围,并有予以显示在Map上的TextIconOverlay等。
     * @constructor
     * @param {MarkerClusterer} markerClusterer 一个标记聚合器示例。
     */
    function Cluster(markerClusterer){
        this._markerClusterer = markerClusterer;
        this._map = markerClusterer.getMap();
        this._minClusterSize = markerClusterer.getMinClusterSize();
        this._isAverageCenter = markerClusterer.isAverageCenter();
        this._center = null;//落脚位置
        this._markers = [];//这个Cluster中所包含的markers
        this._gridBounds = null;//以中心点为准,向四边扩大gridSize个像素的范围,也即网格范围
        this._isReal = false; //真的是个聚合

        this._clusterMarker = new BMapLib.TextIconOverlay(this._center, this._markers.length, {"styles":this._markerClusterer.getStyles()});
        //this._map.addOverlay(this._clusterMarker);
    }

可以看到最后一行,创建了TextIconOverlay.并传入了获取到的参数,看到没有我们找到了styles!!!,也就是说,自动为我们创建了TextIconOverlay,那么我们就去看看,TextIconOverlay文件干了什么.

/**
     *@exports TextIconOverlay as BMapLib.TextIconOverlay
     */
    var TextIconOverlay =
        /**
         * TextIconOverlay
         * @class 此类表示地图上的一个覆盖物,该覆盖物由文字和图标组成,从Overlay继承。文字通常是数字(0-9)或字母(A-Z ),而文字与图标之间有一定的映射关系。
         *该覆盖物适用于以下类似的场景:需要在地图上添加一系列覆盖物,这些覆盖物之间用不同的图标和文字来区分,文字可能表示了该覆盖物的某一属性值,根据该文字和一定的映射关系,自动匹配相应颜色和大小的图标。
         *
         *@constructor
         *@param {Point} position 表示一个经纬度坐标位置。
         *@param {String} text 表示该覆盖物显示的文字信息。
         *@param {Json Object} options 可选参数,可选项包括:<br />
         *"<b>styles</b>":{Array<IconStyle>} 一组图标风格。单个图表风格包括以下几个属性:<br />
         *   url	{String}	 图片的url地址。(必选)<br />
         *   size {Size}	图片的大小。(必选)<br />
         *   anchor {Size} 图标定位在地图上的位置相对于图标左上角的偏移值,默认偏移值为图标的中心位置。(可选)<br />
         *   offset {Size} 图片相对于可视区域的偏移值,此功能的作用等同于CSS中的background-position属性。(可选)<br />
         *   textSize {Number} 文字的大小。(可选,默认10)<br />
         *   textColor {String} 文字的颜色。(可选,默认black)<br />
         */
        BMapLib.TextIconOverlay = function(position, text, options){
            this._position = position;
            this._text = text;
            this._options = options || {};
            this._styles = this._options['styles'] || [];
            (!this._styles.length) && this._setupDefaultStyles();
        };

这里就很清楚,刚才传进来三个参数的意义,第一个是位置,第二个是显示的文本,第三个包含了一些可选项上面注释已经很清楚了.

可以看出options中最重要的是styles.若创建时候自己定义了则按照定义的样式来,若未定义,则取默认风格即:this._setupDefaultStyles()

TextIconOverlay.prototype._setupDefaultStyles = function(){
        var sizes = [53, 56, 66, 78, 90];
        for(var i = 0, size; size = sizes[i]; i++){
            this._styles.push({
                url:_IMAGE_PATH + i + '.' + _IMAGE_EXTENSION,
                size: new BMap.Size(size, size)
            });
        }//for循环的简洁写法
    };

这里根据聚合里包含的点的个数,显示不同的样式.可以看到共有五种样子.

url:_IMAGE_PATH + i + '.' + _IMAGE_EXTENSION,
size: new BMap.Size(size, size)

也可以找到_IMAGE_PATH和_IMAGE_EXTENSION 实际上就是进行了拼接而已.

/**

     * 图片的路径

     * @private
     * @type {String}

     */
    var _IMAGE_PATH = 'http://api.map.baidu.com/library/TextIconOverlay/1.2/src/images/m';
/**

     * 图片的后缀名

     * @private
     * @type {String}

     */
    var _IMAGE_EXTENSION  = 'png';

事实上MarkerClusterer.js里的

Cluster.prototype.updateClusterMarker = function ()函数在更新时也会修改样式.
/**
     * 更新该聚合的显示样式,也即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);
        });

    };

具体在TextIconOverlay.js文件中还可以修改很多.读者可以自己分析.比如更改显示的样式_image_path就可以看到点聚合图片url,修改即可改变聚合点的样式。另外还可以在MarkerClusterer.js中改写源代码.提升效率,这里可以参考https://blog.csdn.net/educast/article/details/69523705.

猜你喜欢

转载自blog.csdn.net/weixin_38450840/article/details/82767810