优化百度地图点聚合

百度的点聚合算法 是基于方格和距离的聚合算法,即开始的时候地图上没有任何已知的聚合点,而后遍历全部的点,去计算点的外包正方形(由gridSize指定),若此点的外包正方形与现有的聚合点的外包正方形不相交,则新建聚合点,若相交就把该点加到该聚合点,效果以下图,为了便于查看,笔者特意把外包正方形画了出来。算法

 

 

好的,笔者开始了做死之旅。上面笔者只是生成了50个随机点。dom

接下来要测试下1000个点,嗯有点小卡,可是还能操做,测试

2000个点,个人天,这shit同样的卡顿是什么鬼!!this

5000个点,好的,完美,动也动不了 简直漂亮google

10000个点,页面无响应。。。。。。。spa

 

-----------我只是一条漂亮的分割线----------prototype

由于,曾经按着baidu的点聚合,在google地图里 依样画葫芦过,当时老是隐隐的以为不妥,因而笔者准备查查源码。code

    /**
     * 根据所给定的标记,建立聚合点
     * @return 无返回值
     */
    MarkerClusterer.prototype._createClusters = function(){
        var mapBounds = this._map.getBounds();
        var extendedBounds = getExtendedBounds(this._map, mapBounds, this._gridSize);
        for(var i = 0, marker; marker = this._markers[i]; i++){
            if(!marker.isInCluster && extendedBounds.containsPoint(marker.getPosition()) ){ 
                this._addToClosestCluster(marker);
            }
        }   
    };

    /**
     * 根据标记的位置,把它添加到最近的聚合中
     * @param {BMap.Marker} marker 要进行聚合的单个标记
     *
     * @return 无返回值。
     */
    MarkerClusterer.prototype._addToClosestCluster = function (marker){
        var distance = 4000000;
        var clusterToAddTo = null;
        var position = marker.getPosition();
        for(var i = 0, cluster; cluster = this._clusters[i]; i++){
            var center = cluster.getCenter();
            if(center){
                var d = this._map.getDistance(center, marker.getPosition());
                if(d < distance){
                    distance = d;
                    clusterToAddTo = cluster;
                }
            }
        }
    
        if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)){
            clusterToAddTo.addMarker(marker);
        } else {
            var cluster = new Cluster(this);
            cluster.addMarker(marker);            
            this._clusters.push(cluster);
        }    
    };

以上两个方法就是前文所述的算法的具体实现,blog

先排除全部不在可视范围的点,而后经过比较marker点和聚合点的距离,拿到距离最近的聚合点,判断marker点是否在聚合点的外包正方形内;rem

这一段是正常算法须要没啥问题,看起来问题只能出在  cluster.addMarker(marker);  了

 

 if(this.isMarkerInCluster(marker)){
            return false;
        }//也可用marker.isInCluster判断,外面判断OK,这里基本不会命中
    
        if (!this._center){
            this._center = marker.getPosition();
            this.updateGridBounds();//
        } else {
            if(this._isAverageCenter){
                var l = this._markers.length + 1;
                var lat = (this._center.lat * (l - 1) + marker.getPosition().lat) / l;
                var lng = (this._center.lng * (l - 1) + marker.getPosition().lng) / l;
                this._center = new BMap.Point(lng, lat);
                this.updateGridBounds();
            }//计算新的Center
        }
    
        marker.isInCluster = true;
        this._markers.push(marker);
    
        var len = this._markers.length; if(len < this._minClusterSize ){ this._map.addOverlay(marker); //this.updateClusterMarker();
            return true; } else if (len === this._minClusterSize) { for (var i = 0; i < len; i++) { this._markers[i].getMap() && this._map.removeOverlay(this._markers[i]); } } this._map.addOverlay(this._clusterMarker); this._isReal = true; this.updateClusterMarker(); return true;
    };

 

果真不出所料,在addMarker() 方法内不停的去进行dom操做,不卡才怪。为何度娘就不等计算结束后,在去一次操做完呢,

因而笔者把标黄的代码抽离了出来给cluster类加了一个render方法, 而后在MarkerClusterer.createClusters方法最后加了一个遍历全部聚合点的操做,代码以下

 

render: function() {
	
	var len = this._markers.length;
	
	if (len < this._minClusterSize) {
		this._map.addOverlays(this._markers);
	} else {
		this._map.addOverlay(this._clusterMarker);
		this._isReal = true;
		this.updateClusterMarker();
	}
},

  

 

最后测试了一下,妥妥的1W无压力,2W也hold住。(总感受度娘故意的)