百度地图聚合优化之路(二)

·················································二更······························································
始终觉得百度的聚合插件很卡,后来发现点多了之后相当吃内存。所以最终还是放弃百度地图插件,将里面核心的东西提出来,重新写了个简版的。

/** 
 * @namespace BMap的所有library类均放在BMapLib命名空间下
 */
var BMapLib = window.BMapLib = BMapLib || {};
(function() {
    var rootMap;
    var rootGridSize;
    var rootMinClusterSize;
    var rootMapOverlays = [];
    var rootIsAverageCenter;
    /**
     * 获取一个扩展的视图范围,把上下左右都扩大一样的像素值。
     * @param {Map} map BMap.Map的实例化对象
     * @param {BMap.Bounds} bounds BMap.Bounds的实例化对象
     * @param {Number} gridSize 要扩大的像素值
     *
     * @return {BMap.Bounds} 返回扩大后的视图范围。
     */
    var getExtendedBounds = function(bounds) {
        bounds = cutBoundsInRange(bounds);
        var pixelNE = rootMap.pointToPixel(bounds.getNorthEast());
        var pixelSW = rootMap.pointToPixel(bounds.getSouthWest());
        pixelNE.x += rootGridSize;
        pixelNE.y -= rootGridSize;
        pixelSW.x -= rootGridSize;
        pixelSW.y += rootGridSize;
        var newNE = rootMap.pixelToPoint(pixelNE);
        var newSW = rootMap.pixelToPoint(pixelSW);
        return new BMap.Bounds(newSW, newNE);
    };

    /**
     * 按照百度地图支持的世界范围对bounds进行边界处理
     * @param {BMap.Bounds} bounds BMap.Bounds的实例化对象
     *
     * @return {BMap.Bounds} 返回不越界的视图范围
     */
    var cutBoundsInRange = function(bounds) {
        var minX = getRange(bounds[0], -180, 180);
        var maxX = getRange(bounds[1], -180, 180);
        var minY = getRange(bounds[2], -74, 74);
        var maxY = getRange(bounds[3], -74, 74);

        return new BMap.Bounds(new BMap.Point(minX, minY), new BMap.Point(maxX, maxY));
    };
    /**
     * 紫 红 橙 黄 蓝 绿  单个
     */
    var getStylesBylength = function(len) {
        var style = {
            'cursor': 'pointer',
            'padding': 0,
            'max-width': 'inherit',
            'margin-bottom': 0,
            'border': 0,
            'color': '#fff',
            'font-size': '0.8em',
            'border-radius': '50%',
            'text-align': 'center',
            'box-shadow': '0 0 8px 2px RGBa(135, 185, 255, 0.5), 0 0 8px 2px RGBa(135, 185, 255, 0.5) inset',
        };
        var color = '';
        var size = '';
        if (len == 1) {} else if (len < 100) {
            color = 'rgba(24,150,3, 0.6)';
            size = '28px';
        } else if (len < 500) {
            color = 'rgba(13, 141, 181, 0.6)';
            size = '28px';
        } else if (len < 1000) {
            color = 'rgba(255,255,0, 0.6)';
            size = '28px';
        } else if (len < 5000) {
            color = 'rgba(255, 165,0, 0.6)';
            size = '35px';
        } else if (len < 50000) {
            color = 'rgba(247, 9, 9, 0.6)';
            size = '40px';
        } else {
            color = 'rgba(183,0,255, 0.6)';
            size = '40px';
        }
        style['background-color'] = color;
        style['width'] = size;
        style['height'] = size;
        style['line-height'] = size;
        return style;
    };

    /**
     * 对单个值进行边界处理。
     * @param {Number} i 要处理的数值
     * @param {Number} min 下边界值
     * @param {Number} max 上边界值
     * 
     * @return {Number} 返回不越界的数值
     */
    var getRange = function(i, mix, max) {
        mix && (i = Math.max(i, mix));
        max && (i = Math.min(i, max));
        return i;
    };
    /**
     *@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;
            }
            rootMap = map;
            this._markers = [];
            this._clusters = [];

            var opts = options || {};
            rootGridSize = opts["gridSize"] || 2000 / map.getZoom();
            rootMinClusterSize = opts["minClusterSize"] || 2;
            rootIsAverageCenter = true;
            if (opts['isAverageCenter'] != undefined) {
                rootIsAverageCenter = opts['isAverageCenter'];
            }
            this._styles = opts["styles"] || [];
            var that = this;
            var mkrs = opts["markers"];
            rootMap.addEventListener("zoomend", function(lv) {
                let lvl = lv.target.getZoom();
                rootGridSize = 2000 / lvl;
                that._redraw2();
            });
            rootMap.addEventListener("moveend", function() {
                that._redraw2();
            });
            this._markers = mkrs;
            this.addMarkers2();
        };


    MarkerClusterer.prototype.addMarkers2 = function() {
        var mapBounds = rootMap.getBounds();
        var x_min = mapBounds.Le,
            x_max = mapBounds.Ge,
            y_min = mapBounds.Wd,
            y_max = mapBounds.Ud;
        //遍历排除不在屏幕范围的点
        let tMks = [];
        for (let index = 0, mk; mk = this._markers[index]; index++) {
            if (mk) {
                if (mk[0] <= x_max && mk[0] >= x_min && mk[1] >= y_min && mk[1] <= y_max)
                    tMks.push(mk);
            }
        }
        for (var j = 0, mk; mk = tMks[j]; j++) {
            this._addToClosestCluster2(mk)
        }
        //开始渲染  
        for (var i = 0, cls; cls = this._clusters[i]; i++) {
            if (cls) {
                cls.render();
            }
        }
    }
    MarkerClusterer.prototype._addToClosestCluster2 = function(mk) {
        var dis2 = 32400,
            d1 = 0,
            d2 = 0,
            d = 0;
        var cluster = null;
        for (let i = 0, clus; clus = this._clusters[i]; i++) {
            var c = clus.center; //[lng,lat]
            d1 = c[0] - mk[0];
            d2 = c[1] - mk[1];
            d = d1 * d1 + d2 * d2;
            if (d < dis2) {
                dis2 = d;
                cluster = clus;
            }
        }
        if (cluster && cluster.isMarkerInClusterBounds(mk)) {
            cluster.addMk(mk)
        } else {
            var clustera = new Cluster2(this);
            clustera.addMk(mk);
            this._clusters.push(clustera);
        }
    };
    MarkerClusterer.prototype._redraw2 = function() {
        this.clear();
        this.addMarkers2();
    };
    MarkerClusterer.prototype.clear = function() {
        rootMapOverlays.forEach(ol => {
            rootMap.removeOverlay(ol);
        })
        for (var i = 0, cluster; cluster = this._clusters[i]; i++) {
            cluster.mks = [];
            cluster.center = null;
            cluster._gridBounds = [];
        }
        this._clusters = []; //置空Cluster数组
    };

    function Cluster2() {
        this.center = null; //聚合图标的落脚点 ,目前是假的center
        this.mks = [];
        this._gridBounds = [];
    }
    Cluster2.prototype.render = function() {
        var len = this.mks.length;
        if (len < rootMinClusterSize) {
            for (var j = 0; j < len; j++) {
                let pt = new BMap.Point(this.mks[j][0], this.mks[j][1]);
                let marker = new BMap.Marker(pt);
                rootMap.addOverlay(marker);
                rootMapOverlays.push(marker);
            }
        } else {
            if (rootIsAverageCenter) {
                var lngLat = this.mks.reduce((s, i) => {
                    s[0] = s[0] + i[0];
                    s[1] = s[1] + i[1];
                    return s;
                }, [0, 0]);
                var lng = lngLat[0] / len;
                var lat = lngLat[1] / len;
                this.center = [lng, lat];
                this.updateGridBounds(this.center);
            }
            var clusterMarker = new BMap.Label(len, {
                position: new BMap.Point(this.center[0], this.center[1]), // 指定文本标注所在的地理位置
                offset: new BMap.Size(0, -3) //设置文本偏移量
            }); // 创建文本标注对象
            clusterMarker.setStyle(getStylesBylength(len));
            rootMap.addOverlay(clusterMarker);
            rootMapOverlays.push(clusterMarker);
            var that = this;
            clusterMarker.addEventListener('click', (a) => {
                var b = that._gridBounds;
                rootMap.setViewport(b);
            });
        }
    };
    Cluster2.prototype.addMk = function(mk) {
        if (!this.center) {
            //处理第一个点的情况
            this.center = mk;
            this.updateGridBounds(mk); //
        } else {
            // if (rootIsAverageCenter) {
            //     var l = this.mks.length + 1;
            //     var lat = (this.center[1] * (l - 1) + mk[1]) / l;
            //     var lng = (this.center[0] * (l - 1) + mk[0]) / l;
            //     this.center = [lng, lat];
            //     this.updateGridBounds(this.center);
            // }
        }
        this.mks.push(mk);
    }
    Cluster2.prototype.updateGridBounds = function(mk) {
        this._gridBounds = getExtendedBounds([mk[0], mk[0], mk[1], mk[1]]);
    }
    Cluster2.prototype.isMarkerInClusterBounds = function(mk) {
        return mk[0] <= this._gridBounds.Ge && mk[0] >= this._gridBounds.Le &&
            mk[1] >= this._gridBounds.Wd && mk[1] <= this._gridBounds.Ud;
    };
})();

效果还是可以比较快。


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

推荐阅读更多精彩内容