专栏首页前端加油站⭐Mapbox GL JS学习探索系列(4) - Marker重叠解决方案

⭐Mapbox GL JS学习探索系列(4) - Marker重叠解决方案

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/j_bleach/article/details/103145507

简介

相比于layer,marker 有着更为灵活的呈现方式,适用于地图上更加复杂的标注显示,而与此同时marker是通过dom渲染,然后叠加在地图图层上的,因此在性能上不及layer。在实际应用场景中,当地图需要大量渲染复杂的结构标注时,layer通常不能完全满足需求,而此时marker就成了替代方案之一,但marker没有layer那么多的配置项去满足marker之间或者marker与地图之间的位置关系。本文利用source的cluster属性,着重解决marker在地图中显示重叠的问题。

基础用法

var popup = new mapboxgl.Popup({ offset: 25 })
.setText('popUpText');
var marker = new mapboxgl.Marker({
element: element,
draggable: true,
offset: [10, 0],
})
.setLngLat([0, 0])
.setPopup(popup)
.addTo(map);

marker 接收一个dom元素作为显示单位,默认是一个svg 定位图标。options 还支持配置偏移量及可拖拽配置,也可以对marker增加一个弹出窗口。

marker重叠显示解决方案

在mapbox中,想要直接达到marker具有边界检测的效果是比较困难的,目前的思路是通过两两计算marker间的距离,来控制marker的显示隐藏,避免重叠。但这种方法,计算量太大,可行度差。因此需要一种借助于类似于layer那种自适配地图显示的方案,来解决marker的重叠显示问题。

this.map.addSource("build-marker-source", {
                                "type": "geojson",
                                "data": {
                                    "type": "FeatureCollection",
                                    "features": []
                                },
                                "cluster": true,
                                "clusterRadius": 35
                            })

通过给layer设置聚合属性的source来间接控制marker的显示隐藏。在source中设置cluster为true时,可以使当前图层的marker之间获取边缘检测的效果,使得marker两两之间碰撞覆盖时,自动聚合成其中的一个(聚合目标的经纬度坐标与原始数据有一定偏差),clusterRadius来设置聚合目标的半径大小。

 this.map.addSource("build-marker-source", {
                                "type": "geojson",
                                "data": {
                                    "type": "FeatureCollection",
                                    "features": []
                                },
                                "cluster": true,
                                "clusterRadius": 35 // 聚合半径
                            })

通过监听地图的数据更新,来实时的绘制与layer显示状态相同的marker。

this.map.on("data", (e) => {
                            if (e.sourceId !== "build-marker-source" || !e.isSourceLoaded) return;

                            this.map.on("move", updateMarkers);
                            this.map.on("moveend", updateMarkers);
                            updateMarkers();
                        });

在监听地图数据更新过程中,过滤掉非操作marker的数据变动,及数据未加载完成的状态,有且只在满足更新条件时,更新地图标注显示。

					var markers = {};
                    var markersOnScreen = {};
                    const updateMarkers = () => {
                        let sourceObj = this.map.getSource("build-marker-source")
                        var newMarkers = {};
                        var features = this.map.querySourceFeatures("build-marker-source");
                        for (var i = 0; i < features.length; i++) {
                            let coords = features[i].geometry.coordinates;
                            let props = features[i].properties;
                            let name = ""                           
                             if (!props.cluster) continue;
                            var id = props.cluster_id;
                            if (id) {
                                sourceObj.getClusterLeaves(id, 10, 0, (e, f) => {
                                    name = f[0].properties.name
                                })
                            } else {
                                id = props.id
                                name = props.name
                            }
                            var marker = markers[id];
                            if (!marker && name) {
                                let el = document.createElement("div");
                                el.className = "popUpBox";
                                el.innerText = name
                                marker = markers[id] = new creeper.Marker({element: el}).setLngLat(coords);
                            }

                            newMarkers[id] = marker;

                            if (!markersOnScreen[id])
                                marker && marker.addTo(this.map);
                        }
                        // for every marker we've added previously, remove those that are no longer visible
                        for (id in markersOnScreen) {
                            if (!newMarkers[id])
                                markersOnScreen[id] && markersOnScreen[id].remove();
                        }
                        markersOnScreen = newMarkers;
                    }

流程图:

变量

描述

markers

当前地图标注总集合,通过聚合id或资源自定义uid为主键

markersOnScreen

上轮地图数据变更标注集合,即本轮数据变更前,地图显示标注集合

newMarkers

本轮地图数据变更标注集合,当前地图需显示的标注集合

利用this.map.querySourceFeatures("build-marker-source") 获取当前地图可视的标注信息数据集合,通过遍历集合来查看当前可视marker是否为聚合类,如果为非聚合类的话,当前marker数据就是原始数据可以直接标记在地图当中,如果遍历目标为聚合类,则需要利用资源对象中的getClusterLeaves方法,通过cluster_id来查找原始数据源,因为聚合之后的marker坐标,失去了原有的properties,取而代之的是聚合相关的内容属性,因此想要获取marker的name及原始经纬度,则需要二次查询。

通过自定义属性中的uid,或者cluster_id来循环查找markers里面是否已经实例化当前marker。每一轮次的可视feature遍历,都去重置newMarkers,将符合可视条件的marker以key-value的方式赋值到newMarkers,并在markersOnScreen中遍历旧的marker是否存在于newMarkers,如果不存在则在当前地图中移除。逻辑末尾,再将newMarkers赋值到markersOnScreen上,等待下一轮次的数据更新,来判断相关marker的显示隐藏。

总结

至此,通过source上的cluster配置,解决了关于地图marker的重叠显示问题,实现了通过地图缩放,来自适应的显示相关标注点,如果有更好的方法欢迎交流讨论。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • angular $http

    j_bleach
  • 前端开发知识汇总--HTML、CSS

    版权声明:本文为博主原创文章,未经博主允许不得转载。 ...

    j_bleach
  • angular4实战(2) router

    版权声明:本文为博主原创文章,未经博主允许不得转载。 ...

    j_bleach
  • python画图常用颜色

    装了seaborn扩展的话,在字典seaborn.xkcd_rgb中包含所有的xkcd crowdsourced color names。如下:

    py3study
  • SAP UI5 webIDE good build and bad build

    版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。

    Jerry Wang
  • Linux常用操作

    修改/etc/sysconfig/network-scripts/ifcfg-ens192文件

    剑行者
  • 微信小程序云开发数据导入(json格式)

    上篇写道:小程序云开发的开通流程及添加集合数据:https://www.jianshu.com/p/17097e5c9ae4

    祈澈菇凉
  • 滴滴快的叶耘:从移动出行看共享经济的未来和边界

     7月14日晚,腾讯研究院与TechWeb联合主办的第13期“互联网前沿沙龙”如期举行,本期主题为“共享经济:下一个大趋势”。连尚网络(wifi万能钥匙)联合...

    腾讯研究院
  • MQL5从入门到精通【第四章】(一)条件判断语句

    程序的基础功能之一就是做判断。判断语句通过评估条件的真伪作出决定。MQL5中有三个条件判断语句:if - else,三目操作符,和switch - case语句...

    程序员小助手
  • 快速排序算法

    爱撒谎的男孩

扫码关注云+社区

领取腾讯云代金券