解决百度地图点聚合缩放Lable消失 亲测有效 javascript API

本文介绍了一种基于百度地图API实现的标记聚合技术,该技术能够自动将密集区域的多个标记进行聚合显示,有效减少地图上标记过多导致的视觉杂乱。
1.本地新建js文件MarkerClusterer.js,内容如下:
var BMapLib = window.BMapLib = BMapLib || {}; (function() {
     var getExtendedBounds = function(map, bounds, gridSize) {
         bounds = cutBoundsInRange(bounds);
         var pixelNE = map.pointToPixel(bounds.getNorthEast());
         var pixelSW = map.pointToPixel(bounds.getSouthWest());
         pixelNE.x += gridSize;
         pixelNE.y -= gridSize;
         pixelSW.x -= gridSize;
         pixelSW.y += gridSize;
         var newNE = map.pixelToPoint(pixelNE);
         var newSW = map.pixelToPoint(pixelSW);
         return new BMap.Bounds(newSW, newNE)
     };
     var cutBoundsInRange = function(bounds) {
         var maxX = getRange(bounds.getNorthEast().lng, -180, 180);
         var minX = getRange(bounds.getSouthWest().lng, -180, 180);
         var maxY = getRange(bounds.getNorthEast().lat, -74, 74);
         var minY = getRange(bounds.getSouthWest().lat, -74, 74);
         return new BMap.Bounds(new BMap.Point(minX, minY), new BMap.Point(maxX, maxY))
     };
     var getRange = function(i, mix, max) {
         mix && (i = Math.max(i, mix));
         max && (i = Math.min(i, max));
         return i
     };
     var isArray = function(source) {
         return '[object Array]' === Object.prototype.toString.call(source)
     };
     var indexOf = function(item, source) {
         var index = -1;
         if (isArray(source)) {
             if (source.indexOf) {
                 index = source.indexOf(item)
             } else {
                 for (var i = 0,
                 m; m = source[i]; i++) {
                     if (m === item) {
                         index = i;
                         break;
                     }
                 }
             }
         }
         return index
     };
     var MarkerClusterer = 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)
     };
     MarkerClusterer.prototype.addMarkers = function(markers) {
         for (var i = 0,
         len = markers.length; i < len; i++) {
             this._pushMarkerTo(markers[i])
         }
         this._createClusters()
     };
     MarkerClusterer.prototype._pushMarkerTo = function(marker) {
         var index = indexOf(marker, this._markers);
         if (index === -1) {
             marker.isInCluster = false;
             this._markers.push(marker)
         }
     };
     MarkerClusterer.prototype.addMarker = function(marker) {
         this._pushMarkerTo(marker);
         this._createClusters()
     };
     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)
             }
         }
     };
     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)
         }
     };
     MarkerClusterer.prototype._clearLastClusters = function() {
         for (var i = 0,
         cluster; cluster = this._clusters[i]; i++) {
             cluster.remove()
         }
         this._clusters = [];
         this._removeMarkersFromCluster()
     };
     MarkerClusterer.prototype._removeMarkersFromCluster = function() {
         for (var i = 0,
         marker; marker = this._markers[i]; i++) {
             marker.isInCluster = false
         }
     };
     MarkerClusterer.prototype._removeMarkersFromMap = function() {
         for (var i = 0,
         marker; marker = this._markers[i]; i++) {
             marker.isInCluster = false;
             tmplabel = marker.getLabel();
             this._map.removeOverlay(marker);
             marker.setLabel(tmplabel)
         }
     };
     MarkerClusterer.prototype._removeMarker = function(marker) {
         var index = indexOf(marker, this._markers);
         if (index === -1) {
             return false
         }
         tmplabel = marker.getLabel();
         this._map.removeOverlay(marker);
         marker.setLabel(tmplabel);
         this._markers.splice(index, 1);
         return true
     };
     MarkerClusterer.prototype.removeMarker = function(marker) {
         var success = this._removeMarker(marker);
         if (success) {
             this._clearLastClusters();
             this._createClusters()
         }
         return success
     };
     MarkerClusterer.prototype.removeMarkers = function(markers) {
         var success = false;
         for (var i = 0; i < markers.length; i++) {
             var r = this._removeMarker(markers[i]);
             success = success || r
         }
         if (success) {
             this._clearLastClusters();
             this._createClusters()
         }
         return success
     };
     MarkerClusterer.prototype.clearMarkers = function() {
         this._clearLastClusters();
         this._removeMarkersFromMap();
         this._markers = []
     };
     MarkerClusterer.prototype._redraw = function() {
         this._clearLastClusters();
         this._createClusters()
     };
     MarkerClusterer.prototype.getGridSize = function() {
         return this._gridSize
     };
     MarkerClusterer.prototype.setGridSize = function(size) {
         this._gridSize = size;
         this._redraw()
     };
     MarkerClusterer.prototype.getMaxZoom = function() {
         return this._maxZoom
     };
     MarkerClusterer.prototype.setMaxZoom = function(maxZoom) {
         this._maxZoom = maxZoom;
         this._redraw()
     };
     MarkerClusterer.prototype.getStyles = function() {
         return this._styles
     };
     MarkerClusterer.prototype.setStyles = function(styles) {
         this._styles = styles;
         this._redraw()
     };
     MarkerClusterer.prototype.getMinClusterSize = function() {
         return this._minClusterSize
     };
     MarkerClusterer.prototype.setMinClusterSize = function(size) {
         this._minClusterSize = size;
         this._redraw()
     };
     MarkerClusterer.prototype.isAverageCenter = function() {
         return this._isAverageCenter
     };
     MarkerClusterer.prototype.getMap = function() {
         return this._map
     };
     MarkerClusterer.prototype.getMarkers = function() {
         return this._markers
     };
     MarkerClusterer.prototype.getClustersCount = function() {
         var count = 0;
         for (var i = 0,
         cluster; cluster = this._clusters[i]; i++) {
             cluster.isReal() && count++
         }
         return count
     };
     function Cluster(markerClusterer) {
         this._markerClusterer = markerClusterer;
         this._map = markerClusterer.getMap();
         this._minClusterSize = markerClusterer.getMinClusterSize();
         this._isAverageCenter = markerClusterer.isAverageCenter();
         this._center = null;
         this._markers = [];
         this._gridBounds = null;
         this._isReal = false;
         this._clusterMarker = new BMapLib.TextIconOverlay(this._center, this._markers.length, {
             "styles": this._markerClusterer.getStyles()
         })
     }
     Cluster.prototype.addMarker = function(marker) {
         if (this.isMarkerInCluster(marker)) {
             return false
         }
         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()
             }
         }
         marker.isInCluster = true;
         this._markers.push(marker);
         var len = this._markers.length;
         if (len < this._minClusterSize) {
             this._map.addOverlay(marker);
             return true
         } else if (len === this._minClusterSize) {
             for (var i = 0; i < len; i++) {
                 tmplabel = this._markers[i].getLabel();
                 this._markers[i].getMap() && this._map.removeOverlay(this._markers[i]);
                 this._markers[i].setLabel(tmplabel)
             }
         }
         this._map.addOverlay(this._clusterMarker);
         this._isReal = true;
         this.updateClusterMarker();
         return true
     };
     Cluster.prototype.isMarkerInCluster = function(marker) {
         if (this._markers.indexOf) {
             return this._markers.indexOf(marker) != -1
         } else {
             for (var i = 0,
             m; m = this._markers[i]; i++) {
                 if (m === marker) {
                     return true
                 }
             }
         }
         return false
     };
     Cluster.prototype.isMarkerInClusterBounds = function(marker) {
         return this._gridBounds.containsPoint(marker.getPosition())
     };
     Cluster.prototype.isReal = function(marker) {
         return this._isReal
     };
     Cluster.prototype.updateGridBounds = function() {
         var bounds = new BMap.Bounds(this._center, this._center);
         this._gridBounds = getExtendedBounds(this._map, bounds, this._markerClusterer.getGridSize())
     };
     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)
         })
     };
     Cluster.prototype.remove = function() {
         for (var i = 0,
         m; m = this._markers[i]; i++) {
             var tmplabel = this._markers[i].getLabel();
             this._markers[i].getMap() && this._map.removeOverlay(this._markers[i]);
             this._markers[i].setLabel(tmplabel)
         }
         this._map.removeOverlay(this._clusterMarker);
         this._markers.length = 0;
         delete this._markers
     };
     Cluster.prototype.getBounds = function() {
         var bounds = new BMap.Bounds(this._center, this._center);
         for (var i = 0,
         marker; marker = this._markers[i]; i++) {
             bounds.extend(marker.getPosition())
         }
         return bounds
     };
     Cluster.prototype.getCenter = function() {
         return this._center;
     };

 })();



转载他人:http://blog.youkuaiyun.com/u012343297/article/details/54343658    

<think>我们正在使用百度地图的mapvgl库,并且希望实现:当未被聚合时显示文本标签(即每个单独的都显示自己的文本标签),而当聚合时,则显示聚合后的样式(可能不显示单个的文本标签,而是显示聚合后的数字等)。 根据引用[2]中提到的,在聚合器的实现中,当聚合时,会移除单个的覆盖物(包括标记和标签),而聚合后的标记会显示一个聚合标签(如显示聚合数量)。但我们的需求是:未被聚合要显示文本标签。 思路: 1. 我们需要在创建的时候,给每个设置一个文本标签(Label)。 2. 当聚合时,我们隐藏这个的文本标签;当未被聚合(即单独显示)时,显示文本标签。 但是,mapvgl库中的聚合功能(使用PointClusterLayer)默认可能没有直接提供这个功能。因此,我们需要通过监听聚合事件,动态控制每个的文本标签的显示与隐藏。 步骤: a. 创建数据,每个数据应包含位置信息以及需要显示的文本内容。 b. 使用PointLayer(而不是PointClusterLayer)来显示,并且为每个添加文本标签(通过设置label选项,或者使用TextIconLayer等)。 c. 但是,这样无法实现聚合。因此,我们需要同时使用PointClusterLayer(用于聚合)和另一个图层(如TextLayer或LabelLayer)来显示文本标签,并控制文本标签的显示条件。 然而,mapvgl的聚合图层(PointClusterLayer)在聚合时,会隐藏原始的,而只显示聚合后的。因此,我们无法直接通过同一个图层来控制文本标签。 替代方案: 我们可以使用两个图层: - 一个PointClusterLayer用于聚合的显示(当聚合时,显示聚合图标;当聚合时,显示单个的图标)。 - 另一个TextLayer(或LabelLayer)用于显示文本标签,但是我们需要控制这个文本标签图层,使其只在未被聚合时显示。 如何判断是否被聚合? - 我们可以监听PointClusterLayer的聚合事件(如'clusterClick'等),但是该图层并没有直接提供每个是否聚合的状态。 另一种思路:使用百度地图原生的MarkerClusterer,而不是mapvgl的PointClusterLayer。因为引用[1]和[2]中提到了MarkerClusterer,并且我们可以通过自定义其样式和行为来实现。 但是,用户要求使用mapvgl库,所以我们还是尽量在mapvgl中解决。 查阅mapvgl的文档,PointClusterLayer有一个事件:'cluster',当聚合状态发生变化时会触发。我们可以利用这个事件来获取当前哪些聚合,哪些没有被聚合。 具体步骤(使用mapvgl): 1. 创建PointClusterLayer,并设置其聚合参数(如gridSize等)。 2. 监听PointClusterLayer的'cluster'事件,该事件会返回聚合后的簇(clusters)和未聚合(noisePoints)。注意:这个事件可能触发频繁,需要做性能优化。 3. 根据事件返回的未聚合(noisePoints)来显示这些的文本标签,而隐藏被聚合的文本标签(或者隐藏所有的文本标签,然后只显示未聚合的)。 但是,mapvgl的PointClusterLayer本身并不支持直接为每个添加文本标签。因此,我们需要另外创建一个TextLayer(或LabelLayer)来显示文本。 实现: - 创建两个图层:pointClusterLayer(聚合图层)和textLayer(文本图层)。 - 初始化时,textLayer的数据为空。 - 当发生聚合事件时,我们从事件中获取未聚合(noisePoints),然后根据这些生成文本图层的数据(每个对应一个文本标签,位置与相同,内容为需要显示的文本)。 - 对于被聚合,我们不显示文本标签(即不在textLayer中添加这些)。 注意:由于聚合事件触发频繁,我们需要在每次事件触发时更新textLayer的数据(设置setData为新的未聚合对应的文本数据)。 代码结构示例: ```javascript // 初始化地图和mapvgl var map = new BMapGL.Map("container"); var view = new mapvgl.View({ map: map }); // 创建聚合图层 var clusterLayer = new mapvgl.PointClusterLayer({ // 聚合参数 minClusterSize: 2, // 最小聚合数量 maxZoom: 18, // 最大聚合层级 gridSize: 60, // 聚合计算网格大小 // 单个的样式 icon: new BMapGL.Icon('http://example.com/point.png', new BMapGL.Size(20,20)), // 聚合的样式 clusterIcon: new BMapGL.Icon('http://example.com/cluster.png', new BMapGL.Size(30,30)), // 可以设置聚合文本样式 clusterTextStyle: { color: 'white', fontSize: 10 } }); view.addLayer(clusterLayer); // 创建文本图层 var textLayer = new mapvgl.LabelLayer({ // 文本样式设置 color: '#000', fontSize: 12, offset: [0, -20] // 文本相对于的偏移 }); view.addLayer(textLayer); // 假设points是原始数据,格式为[{geometry: {type: 'Point', coordinates: [lng, lat]}, properties: {text: '标签'}}, ...] clusterLayer.setData(points); // 监听聚合事件 clusterLayer.on('cluster', function(result) { // result包含clusters(聚合簇)和noisePoints(未聚合) // 提取未聚合的文本数据 var textData = []; result.noisePoints.forEach(function(point) { textData.push({ geometry: point.geometry, properties: { text: point.properties.text // 假设原始数据中有text属性 } }); }); // 更新文本图层 textLayer.setData(textData); }); // 初始化时触发一次聚合事件(如果初始数据设置后没有自动触发,可以手动触发一次) // 但setData后应该会自动触发,所以一般不需要手动触发 ``` 注意事项: - 性能:如果非常多,每次聚合事件都更新文本图层可能会造成性能压力。因此,可以考虑使用节流(throttle)来减少更新频率,但要注意更新延迟。 - 文本图层的位置偏移:根据实际需要调整文本标签相对于的位置。 - 文本样式:可以在LabelLayer的初始化参数中设置。 另外,如果聚合事件没有返回未聚合的原始属性(比如text),那么我们需要在原始数据中保留这些属性,并在事件处理中通过某种方式获取(比如在数据中存储id,然后根据id去原始数据中查找)。但上述示例假设result.noisePoints中的对象包含了原始数据中的properties。 如果mapvgl的PointClusterLayer在聚合事件中返回的noisePoints不包含原始properties,那么我们需要在创建数据时,将需要显示的文本信息存储在geometry中(但通常properties会被保留)。因此,需要确认mapvgl的PointClusterLayer是否保留原始数据的properties。 根据mapvgl的文档,setData传入的数据中的properties会被保留,所以在事件返回的noisePoints中应该可以访问到。 因此,按照上述方法,我们可以实现:当未被聚合时,显示该的文本标签;当聚合时,文本标签隐藏。 相关问题: 1. 如何优化大量文本标签的显示性能? 2. 除了mapvgl的PointClusterLayer,还有哪些方法可以实现聚合? 3. 如何自定义聚合图标的样式(比如根据聚合中异常的数量改变颜色)? 4. 如何为聚合图标添加击事件? 注意:由于mapvgl的版本更新,具体事件和参数可能有所不同,请参考官方文档。 引用: [^1]: 百度地图标记聚合MarkerClusterer结合TextIconOverlay,根据标记的属性更换聚合器的样式。 [^2]: 百度地图聚合-设置lable显示。</think>在使用百度地图的 MapVGL 库时,若需让未被聚合显示文本标记,可通过以下步骤实现: ### 解决方案 1. **分离图层管理** - 创建两个独立图层: - `PointClusterLayer`:负责聚合(当密集时显示聚合图标) - `LabelLayer`:专门显示文本标签(仅对未聚合生效) ```javascript const view = new mapvgl.View({ map }); const clusterLayer = new mapvgl.PointClusterLayer({ minClusterSize: 2 }); // 聚合层 const labelLayer = new mapvgl.LabelLayer({ offset: [0, -20] }); // 文本标签层,offset调整位置 view.addLayer(clusterLayer); view.addLayer(labelLayer); ``` 2. **动态更新标签数据** 监听聚合事件,提取未聚合(`noisePoints`)生成文本标签数据: ```javascript clusterLayer.on("cluster", (result) => { const textData = result.noisePoints.map(point => ({ geometry: point.geometry, // 保持与原始相同坐标 properties: { text: point.properties.text, // 从原始数据获取文本内容 color: '#000000' // 自定义文本颜色 } })); labelLayer.setData(textData); // 仅未聚合显示标签 }); ``` 3. **数据格式要求** 原始数据需包含文本属性: ```javascript const points = [ { geometry: { type: 'Point', coordinates: [116.4, 39.9] }, properties: { text: '北京' } // 必须包含文本字段 }, // 其他数据... ]; clusterLayer.setData(points); ``` ### 关键说明 - **聚合事件触发**:缩放地图或移动视图时,`cluster` 事件自动触发,动态更新标签数据[^1]。 - **性能优化**: - 文本层仅渲染未聚合,降低计算开销。 - 对大量数据(如 >10,000)建议开启 `enableDebounce` 防抖。 - **样式自定义**:通过 `LabelLayer` 参数调整文本样式: ```javascript new mapvgl.LabelLayer({ fontSize: 14, fontWeight: 'bold', fillColor: '#FF0000', // 文本填充色 offset: [0, -25] // 垂直偏移 }); ``` ### 效果对比 | 场景 | 聚合显示 | 未聚合显示 | |------------------|---------------|-------------------| | **默认聚合层** | 聚合图标+数量 | 无文本 | | **增加标签层** | 聚合图标+数量 | 独立文本标签 ✅ | > 此方案通过图层分离和动态数据更新,确保文本标签仅在未聚合状态下显示,符合地图可视化性能最佳实践[^2]。 --- ### 相关问题 1. **如何根据属性动态改变聚合图标颜色?** (例如:包含异常时显示红色聚合图标) 2. **MapVGL 中如何自定义聚合图标的数字样式?** (如调整字体大小、颜色或添加背景) 3. **聚合性能优化的常见方法有哪些?** (针对海量数据的渲染效率提升) 4. **如何实现聚合图标的击展开效果?** (聚合图标后展开该区域内的所有) [^1]: MapVGL 官方文档 - 聚合事件系统 [^2]: 百度地图聚合性能优化白皮书
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值