实现 Leaflet 多类型点位标记与聚合功能的实战经验分享
在现代的地理信息系统(GIS)应用中,地图功能是不可或缺的一部分。无论是展示商业网点、旅游景点还是公共服务设施,地图都能以直观的方式呈现数据。然而,当数据量较大时,地图上可能会出现大量的标记点,这不仅会影响用户体验,还会导致性能问题。因此,我们需要一种方法来智能地聚合这些标记点,同时允许用户在需要时查看详细信息。
Leaflet 是一个轻量级的开源 JavaScript 地图库,它提供了丰富的 API 和插件支持,非常适合实现这种功能。结合 Leaflet.markercluster 插件,我们可以轻松实现标记点的聚合和动态显示。

项目背景
在最近开发中,我遇到了一个有趣的需求:实现基于 Leaflet 的地图功能,支持多类型点位标记和聚合。这个功能需要展示不同类型的数据点,并在地图缩放时智能聚合,以避免地图过于拥挤。经过一番努力,我成功实现了这一功能,并在此分享我的经验和代码实现。
实现思路
1. 数据结构设计
在实现功能之前,我们需要定义清晰的数据结构,以便更好地管理和展示不同类型的数据点。以下是点位数据的格式示例:
markerData: [{latitude: "23.131828",longitude: "113.326821",name: "麦当劳(正佳广场五楼店)",address: "广州市天河区天河南街道天河路228号正佳广场五楼561铺",type: "fast_food"},{latitude: "23.116523",longitude: "113.390699",name: "肯德基(车陂南店)",address: "广州市天河区车陂路6号一层",type: "fast_food"}...
]
此外,我们还需要定义支持的标记点类型和对应的样式配置:
markerType: ["fast_food","coffee","landmark","tourist","shopping","entertainment","transport"
],typeList: [{name: "全部",type: "all",color: "#000000",iconUrl: require("../../assets/img/all.png")},{name: "快餐店",type: "fast_food",color: "blue",iconUrl: require("../../assets/img/fast_food.png")},{name: "咖啡店",type: "coffee",color: "green",iconUrl: require("../../assets/img/coffee.png")},{name: "城市地标",type: "landmark",color: "orange",iconUrl: require("../../assets/img/landmark.png")},{name: "旅游景点",type: "tourist",color: "red",iconUrl: require("../../assets/img/tourist.png")},{name: "购物中心",type: "shopping",color: "purple",iconUrl: require("../../assets/img/shopping.png")},{name: "娱乐场所",type: "entertainment",color: "yellow",iconUrl: require("../../assets/img/entertainment.png")},{name: "交通设施",type: "transport",color: "teal",iconUrl: require("../../assets/img/transport.png")}
]
2. 地图初始化
首先,我们需要初始化地图。Leaflet 提供了简单易用的 API 来创建地图实例,并设置中心点、缩放级别等基本参数。在我们的项目中,我们使用了天地图(Tianditu)作为地图源,这需要通过 WMTS 服务加载矢量地图和影像地图图层。
this.map = L.map("mapRef", {center: [23.111532, 113.324357], // 地图中心zoom: 13, // 缩放比例zoomControl: false, // 是否显示缩放按钮doubleClickZoom: false, // 是否双击放大attributionControl: false, // 是否显示右下角 Leaflet 标识minZoom: 3, // 最小缩放级别
});
3. 添加地图图层
我们使用了天地图的矢量地图和影像地图图层。需要注意的是,天地图的访问需要一个有效的 API 密钥(tk),并且图层的 URL 需要按照 WMTS 服务的规范进行拼接。
const tiandiKey = "YOUR_TIANDITU_API_KEY"; // 替换为你的天地图 API 密钥
const mapUrl = `http://t0.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${tiandiKey}`;
const cvaLayer = L.tileLayer(`http://t0.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${tiandiKey}`
);this.map.addLayer(L.tileLayer(mapUrl)); // 添加矢量地图图层
this.map.addLayer(cvaLayer); // 添加影像地图图层
注意:如果你在加载天地图时遇到问题,请检查 API 密钥是否有效,以及 URL 拼接是否正确。如果问题仍然存在,可能是网络问题或链接本身的问题,建议适当重试或联系天地图官方支持。
4. 创建聚合图层
为了实现标记点的聚合,我们使用了 Leaflet.markercluster 插件。该插件可以将大量的标记点智能地聚合在一起,形成可点击的群组。当用户放大地图时,这些群组会自动拆分成更小的子集或单个标记点。

我们为每种类型的标记点创建了一个独立的聚合图层,并通过 iconCreateFunction 动态设置聚合图标的颜色和样式。
this.markerType.forEach((type) => {const iconCreateFunction = (cluster) => {const dynamicClassName = `custom-marker cluster-icon-${type}`;return L.divIcon({html: `<div class="${dynamicClassName}"><span>${cluster.getChildCount()}</span></div>`,className: `custom-marker cluster-icon-${type}`,iconSize: L.point(40, 40),});};this.markerClusters[type] = L.markerClusterGroup({iconCreateFunction: iconCreateFunction,});
});
5. 添加标记点
标记点的数据通常以数组的形式存储,每个数据项包含类型、名称、地址和坐标等信息,可以点击展示点位信息。我们根据数据项的类型,将其添加到对应的聚合图层中。

this.markerData.forEach((item) => {const [longitude, latitude] = gcoord.transform([item.longitude, item.latitude],gcoord.GCJ02,gcoord.WGS84);const marker = L.marker([latitude, longitude], {icon: icons[item.type],});marker.bindPopup(`<b>${item.name}</b><br>${item.address}`);this.markerClusters[item.type].addLayer(marker);
});Object.values(this.markerClusters).forEach((cluster) => {this.map.addLayer(cluster);
});
6. 动态过滤标记点
为了提升用户体验,我们允许用户通过点击按钮来筛选特定类型的标记点。当用户点击按钮时,我们移除所有聚合图层,并根据用户的选择重新添加对应的图层。

filterType(item) {Object.values(this.markerClusters).forEach((cluster) => {this.map.removeLayer(cluster);});if (item.type === "all") {Object.values(this.markerClusters).forEach((cluster) => {this.map.addLayer(cluster);});} else {if (this.markerClusters[item.type]) {this.map.addLayer(this.markerClusters[item.type]);}}
}
性能优化
在处理大量标记点时,性能优化是至关重要的。Leaflet.markercluster 插件本身已经对性能进行了优化,但我们仍然可以通过以下方式进一步提升性能:
-
限制标记点数量:在地图缩放级别较低时,可以隐藏一些不重要的标记点。
-
使用缓存:对于重复加载的数据,可以使用缓存机制减少重复请求。
-
异步加载数据:如果标记点数据量较大,可以采用分页或懒加载的方式动态加载数据。
总结
通过 Leaflet 和 Leaflet.markercluster 插件,我们成功实现了一个支持多类型点位标记和聚合的地图功能。这个功能不仅提升了用户体验,还为地图应用提供了更强大的数据展示能力。在开发过程中,我们需要注意地图图层的加载、聚合图标的动态设置以及性能优化等方面,以确保应用的稳定性和流畅性。
相关文章:
实现 Leaflet 多类型点位标记与聚合功能的实战经验分享
在现代的地理信息系统(GIS)应用中,地图功能是不可或缺的一部分。无论是展示商业网点、旅游景点还是公共服务设施,地图都能以直观的方式呈现数据。然而,当数据量较大时,地图上可能会出现大量的标记点&#x…...
Linux 环境“从零”部署 MongoDB 6.0:mongosh 安装与数据操作全攻略
前提 完成linux平台部署MongoDB【部署教程】且完成mongosh的安装 由于本人使用的是6.0版本的MongoDB,新版本 MongoDB(尤其是 6.0 及以上版本)已经不再默认捆绑传统的 mongo shell,而改用新的 MongoDB Shell(mongosh&am…...
深度学习五大模型:CNN、Transformer、BERT、RNN、GAN详细解析
# 深度学习五虎将:当CNN遇见Transformer的奇幻漂流 ## 序章:AI江湖的兵器谱排行 2012年,多伦多大学的厨房里,Hinton的学生们用GPU煎了个"AlexNet"荷包蛋,从此开启了深度学习的热兵器时代。如今五大模型各显…...
004 rocketmq集群
1、集群模式 在RocketMQ中,集群的部署模式是比较多的,有以下几种: public class ConsumerDemo {public static void main(String[] args) throws Exception {DefaultMQPushConsumer consumer new DefaultMQPushConsumer("test-group&qu…...
基于 Python 深度学习的电影评论情感分析可视化系统(2.0 全新升级)
基于 Python 深度学习的电影评论情感分析可视化系统,基于 Flask 深度学习,构建了一个 影评情感分析系统,能够 自动分析影评、计算情感趋势 并 可视化展示,对于电影行业具有重要参考价值! 基于 Python 深度学习的电影评…...
Linux内核配置与构建原理
Kconfig文件 Kconfig是Linux内核中用于配置功能的脚本语言系统,由众多内核源码树中每个目录下的Kconfig文件组成。它定义Linux相关的配置选项层次结构和依赖关系。 menuconfig工具,会抓取Kconfig中的信息,为用户输出友好的交互式菜单选项配…...
大语言模型微调的基本概念介绍
大型语言模型(LLMs)正在以惊人的速度发展,LLM微调的潜力更是如此。大型语言模型的生命周期有几个关键步骤,今天我们将要介绍这个周期中最丰富、最耗时的一部分——LLM微调过程。 大语言模型的生命周期 在深入了解大型语言模型&a…...
实例分割 | yolov11训练自己的数据集
前言 因工作要求使用的都是yolov5系列的模型,今天学习一下最先进的yolov11,记录一下环境配置及训练过程。 1.项目下载及环境安装 源码位置:yolov11 可以看到,这里要求python版本大于等于3.8,我这里安装python3.10.…...
vue3:四嵌套路由的实现
一、前言 1、嵌套路由的含义 嵌套路由的核心思想是:在某个路由的组件内部,可以定义子路由,这些子路由会渲染在父路由组件的特定位置(通常是 <router-view> 标签所在的位置)。通过嵌套路由,你可以实…...
AIGC和搜索引擎的异同
AIGC(生成式人工智能)与搜索引擎的核心差异体现在信息处理方式和输出形态上,我们可以从以下维度对比: 一、工作原理的本质差异 信息检索机制 搜索引擎:基于关键词匹配(如"中暑怎么办"→返回相关…...
ES批量查询
在 Elasticsearch 中,multi_search(也称为 msearch)是一种允许你在单个请求中执行多个搜索操作的 API。它可以显著减少网络开销,尤其是在需要执行多个查询时。multi_search 会将多个查询打包成一个请求发送给 Elasticsearch&#…...
Vue2学习
一、Vue3 基础 监视属性 天气案例 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>天气案例</…...
PySide(PyQT)重新定义contextMenuEvent()实现鼠标右键弹出菜单
在 PySide中,contextMenuEvent() 是 QWidget 类(以及继承自它的所有子类)的一个事件处理方法,主要用于处理上下文菜单事件,也就是当用户在控件上右键点击时触发的事件。 • 通过重新定义contextMenuEvent()来实现自定…...
Storm实时流式计算系统(全解)——下
storm编程案例-网站访问来源实时统计-需求 storm编程-网站访问来源实时统计-代码实现 根据以上条件可以只写一个类,我们只需要写2个方法和一个main(),一个读取/发射(spout)。 一个拿到数据统计后发到redis…...
配置Nginx日志url encode问题
文章目录 配置Nginx日志url encode问题方法1-lua方法2-set-misc-nginx-module 配置Nginx日志url encode问题 问题描述: 当自定义日志输出格式,需要输出http请求中url参数时,如果参数中包含中文,是会进行url encode的,…...
JAVA SE 包装类和泛型
文章目录 📕1. 包装类✏️1.1 基本数据类型和对应的包装类✏️1.2 装箱和拆箱✏️1.3 自动装箱和自动拆箱 📕2. 泛型✏️2.1 泛型的语法✏️2.2 泛型类的使用✏️2.3 裸类型(Raw Type)✏️2.4 擦除机制✏️2.5 泛型的上界✏️2.6 泛型方法✏️2.7 通配符…...
基于Linux系统的物联网智能终端
背景 产品研发和项目研发有什么区别?一个令人发指的问题,刚开始工作时项目开发居多,认为项目开发和产品开发区别不大,待后来随着自身能力的提升,逐步感到要开发一个好产品还是比较难的,我认为项目开发的目的…...
从零开始开发纯血鸿蒙应用之语音朗读
从零开始开发纯血鸿蒙应用 〇、前言一、API 选型1、基本情况2、认识TextToSpeechEngine 二、功能集成实践1、改造右上角菜单2、实现语音播报功能2.1、语音引擎的获取和关闭2.2、设置待播报文本2.3、speak 目标文本2.4、设置语音回调 三、总结 〇、前言 中华汉字洋洋洒洒何其多…...
物联网小范围高精度GPS使用
在园区内实现小范围高精度GPS(全球定位系统)定位,通常需要结合多种技术来弥补传统GPS在精度和覆盖范围上的不足。以下是实现小范围高精度GPS定位的解决方案,包括技术选择、系统设计和应用场景。 一、技术选择 在园区内实现高精度…...
一次有趣的前后端跨越排查
进行前后端代码联调的时候,使用axios调用后端请求,因为都是本地进行联调,所以没有考虑跨域的问题,写了一个get的请求接口,请求后端时,突然跳出下面的问题: 错误的信息一看很像就是跨域的问题&…...
5分钟搞定AI 3D建模!TripoSR:图片秒变专业3D模型的终极方案
5分钟搞定AI 3D建模!TripoSR:图片秒变专业3D模型的终极方案 【免费下载链接】TripoSR TripoSR: Fast 3D Object Reconstruction from a Single Image 项目地址: https://gitcode.com/GitHub_Trending/tr/TripoSR 还在为复杂的3D建模软件头疼吗&am…...
基于LSTM的无人艇波浪方向估计:从时序预测到工程实践
1. 项目概述:当无人艇“学会”感知海浪在海洋工程和无人系统领域,让机器“感知”并“理解”它所处的海洋环境,尤其是波浪的动态特性,一直是个核心挑战。想象一下,你驾驶一艘小船,如果能提前几秒甚至更久“预…...
【软考高级架构】论文预测——论基于ATAM的架构评估方法
论基于ATAM的架构评估方法 摘要 软件架构评估是保障系统质量属性满足业务目标的关键环节。架构权衡分析方法(Architecture Trade-off Analysis Method,ATAM)作为一种系统化的架构评估方法,通过场景捕获、质量属性分析、敏感点与权衡点识别、风险与非风险决策分类等结构化…...
GitHub史诗级泄露:3800个核心仓库被窃,TeamPCP如何通过VS Code扩展攻破全球最大代码平台
一、引言:全球开发者的至暗时刻 2026年5月20日,一则消息震惊了整个科技界:微软旗下全球最大代码托管平台GitHub确认,约3800个内部私有仓库被威胁组织TeamPCP窃取,涵盖GitHub Copilot、CodeQL、GitHub Actions、Codespa…...
86、【Agent】【OpenCode】bash 工具提示词(完结)
【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除 背景 上篇 blog 【Agent】【OpenCode】bash 工…...
百度网盘全自动化实录:Hermes Agent + bb-browser
缘起:今天风暴了一下,准备实践一下Hermes自动售卖数字产品实现自动变现的MVP,谁知道刚开始就卡在了操作百度网盘,要么被反爬,要么靠之前开发的computer use for win截图点坐标像瞎子摸象。最后换了条路——用 bb-brows…...
Cadence 17.4 CIS配置踩坑实录:MySQL元件数据库连接失败与中文乱码全解决
Cadence 17.4 CIS配置实战:MySQL元件数据库连接与中文乱码终极解决方案 当工程师尝试将Cadence CIS与MySQL数据库集成时,往往会遇到两个令人头疼的问题:连接失败和中文乱码。这两个问题看似简单,却可能耗费大量调试时间。本文将深…...
【ElevenLabs老挝文语音实战指南】:2024年唯一经实测验证的8步本地化语音合成落地方案
更多请点击: https://kaifayun.com 第一章:ElevenLabs老挝文语音合成的技术背景与本地化价值 ElevenLabs 作为全球领先的AI语音生成平台,长期聚焦于高保真、情感化多语言语音合成技术。尽管其支持语言列表持续扩展,老挝文&#x…...
如何告别模组管理噩梦:XXMI启动器的3个革命性解决方案
如何告别模组管理噩梦:XXMI启动器的3个革命性解决方案 【免费下载链接】XXMI-Launcher Modding platform for GI, HSR, WW and ZZZ 项目地址: https://gitcode.com/gh_mirrors/xx/XXMI-Launcher 你是否曾经为管理多个游戏的模组而感到头疼?每个游…...
从济南话到烟台腔:ElevenLabs山东话语音泛化能力极限测试(覆盖17地市、1362条测试句、WER 8.7%实测数据)
更多请点击: https://codechina.net 第一章:从济南话到烟台腔:ElevenLabs山东话语音泛化能力极限测试(覆盖17地市、1362条测试句、WER 8.7%实测数据) 为验证ElevenLabs语音合成模型对山东方言的跨地域泛化能力&#x…...
