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

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

作者: 古川耕夫 | 来源:发表于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