美文网首页
百度地图聚合优化之路(二)

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

作者: 古川耕夫 | 来源:发表于2019-04-30 16:10 被阅读0次

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

    /** 
     * @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

    相关文章

      网友评论

          本文标题:百度地图聚合优化之路(二)

          本文链接:https://www.haomeiwen.com/subject/xnsvnqtx.html