当前位置: 首页 > news >正文

实现 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 插件本身已经对性能进行了优化,但我们仍然可以通过以下方式进一步提升性能:

  1. 限制标记点数量:在地图缩放级别较低时,可以隐藏一些不重要的标记点。

  2. 使用缓存:对于重复加载的数据,可以使用缓存机制减少重复请求。

  3. 异步加载数据:如果标记点数据量较大,可以采用分页或懒加载的方式动态加载数据。

总结

通过 Leaflet 和 Leaflet.markercluster 插件,我们成功实现了一个支持多类型点位标记和聚合的地图功能。这个功能不仅提升了用户体验,还为地图应用提供了更强大的数据展示能力。在开发过程中,我们需要注意地图图层的加载、聚合图标的动态设置以及性能优化等方面,以确保应用的稳定性和流畅性。

相关文章:

实现 Leaflet 多类型点位标记与聚合功能的实战经验分享

在现代的地理信息系统&#xff08;GIS&#xff09;应用中&#xff0c;地图功能是不可或缺的一部分。无论是展示商业网点、旅游景点还是公共服务设施&#xff0c;地图都能以直观的方式呈现数据。然而&#xff0c;当数据量较大时&#xff0c;地图上可能会出现大量的标记点&#x…...

Linux 环境“从零”部署 MongoDB 6.0:mongosh 安装与数据操作全攻略

前提 完成linux平台部署MongoDB【部署教程】且完成mongosh的安装 由于本人使用的是6.0版本的MongoDB&#xff0c;新版本 MongoDB&#xff08;尤其是 6.0 及以上版本&#xff09;已经不再默认捆绑传统的 mongo shell&#xff0c;而改用新的 MongoDB Shell&#xff08;mongosh&am…...

深度学习五大模型:CNN、Transformer、BERT、RNN、GAN详细解析

# 深度学习五虎将&#xff1a;当CNN遇见Transformer的奇幻漂流 ## 序章&#xff1a;AI江湖的兵器谱排行 2012年&#xff0c;多伦多大学的厨房里&#xff0c;Hinton的学生们用GPU煎了个"AlexNet"荷包蛋&#xff0c;从此开启了深度学习的热兵器时代。如今五大模型各显…...

004 rocketmq集群

1、集群模式 在RocketMQ中&#xff0c;集群的部署模式是比较多的&#xff0c;有以下几种&#xff1a; public class ConsumerDemo {public static void main(String[] args) throws Exception {DefaultMQPushConsumer consumer new DefaultMQPushConsumer("test-group&qu…...

基于 Python 深度学习的电影评论情感分析可视化系统(2.0 全新升级)

基于 Python 深度学习的电影评论情感分析可视化系统&#xff0c;基于 Flask 深度学习&#xff0c;构建了一个 影评情感分析系统&#xff0c;能够 自动分析影评、计算情感趋势 并 可视化展示&#xff0c;对于电影行业具有重要参考价值&#xff01; 基于 Python 深度学习的电影评…...

Linux内核配置与构建原理

Kconfig文件 Kconfig是Linux内核中用于配置功能的脚本语言系统&#xff0c;由众多内核源码树中每个目录下的Kconfig文件组成。它定义Linux相关的配置选项层次结构和依赖关系。 menuconfig工具&#xff0c;会抓取Kconfig中的信息&#xff0c;为用户输出友好的交互式菜单选项配…...

大语言模型微调的基本概念介绍

大型语言模型&#xff08;LLMs&#xff09;正在以惊人的速度发展&#xff0c;LLM微调的潜力更是如此。大型语言模型的生命周期有几个关键步骤&#xff0c;今天我们将要介绍这个周期中最丰富、最耗时的一部分——LLM微调过程。 大语言模型的生命周期 在深入了解大型语言模型&a…...

实例分割 | yolov11训练自己的数据集

前言 因工作要求使用的都是yolov5系列的模型&#xff0c;今天学习一下最先进的yolov11&#xff0c;记录一下环境配置及训练过程。 1.项目下载及环境安装 源码位置&#xff1a;yolov11 可以看到&#xff0c;这里要求python版本大于等于3.8&#xff0c;我这里安装python3.10.…...

vue3:四嵌套路由的实现

一、前言 1、嵌套路由的含义 嵌套路由的核心思想是&#xff1a;在某个路由的组件内部&#xff0c;可以定义子路由&#xff0c;这些子路由会渲染在父路由组件的特定位置&#xff08;通常是 <router-view> 标签所在的位置&#xff09;。通过嵌套路由&#xff0c;你可以实…...

AIGC和搜索引擎的异同

AIGC&#xff08;生成式人工智能&#xff09;与搜索引擎的核心差异体现在信息处理方式和输出形态上&#xff0c;我们可以从以下维度对比&#xff1a; 一、工作原理的本质差异 信息检索机制 搜索引擎&#xff1a;基于关键词匹配&#xff08;如"中暑怎么办"→返回相关…...

ES批量查询

在 Elasticsearch 中&#xff0c;multi_search&#xff08;也称为 msearch&#xff09;是一种允许你在单个请求中执行多个搜索操作的 API。它可以显著减少网络开销&#xff0c;尤其是在需要执行多个查询时。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中&#xff0c;contextMenuEvent() 是 QWidget 类&#xff08;以及继承自它的所有子类&#xff09;的一个事件处理方法&#xff0c;主要用于处理上下文菜单事件&#xff0c;也就是当用户在控件上右键点击时触发的事件。 • 通过重新定义contextMenuEvent()来实现自定…...

Storm实时流式计算系统(全解)——下

storm编程案例-网站访问来源实时统计-需求 storm编程-网站访问来源实时统计-代码实现 根据以上条件可以只写一个类&#xff0c;我们只需要写2个方法和一个main&#xff08;&#xff09;&#xff0c;一个读取/发射&#xff08;spout&#xff09;。 一个拿到数据统计后发到redis…...

配置Nginx日志url encode问题

文章目录 配置Nginx日志url encode问题方法1-lua方法2-set-misc-nginx-module 配置Nginx日志url encode问题 问题描述&#xff1a; 当自定义日志输出格式&#xff0c;需要输出http请求中url参数时&#xff0c;如果参数中包含中文&#xff0c;是会进行url encode的&#xff0c…...

JAVA SE 包装类和泛型

文章目录 &#x1f4d5;1. 包装类✏️1.1 基本数据类型和对应的包装类✏️1.2 装箱和拆箱✏️1.3 自动装箱和自动拆箱 &#x1f4d5;2. 泛型✏️2.1 泛型的语法✏️2.2 泛型类的使用✏️2.3 裸类型(Raw Type)✏️2.4 擦除机制✏️2.5 泛型的上界✏️2.6 泛型方法✏️2.7 通配符…...

基于Linux系统的物联网智能终端

背景 产品研发和项目研发有什么区别&#xff1f;一个令人发指的问题&#xff0c;刚开始工作时项目开发居多&#xff0c;认为项目开发和产品开发区别不大&#xff0c;待后来随着自身能力的提升&#xff0c;逐步感到要开发一个好产品还是比较难的&#xff0c;我认为项目开发的目的…...

从零开始开发纯血鸿蒙应用之语音朗读

从零开始开发纯血鸿蒙应用 〇、前言一、API 选型1、基本情况2、认识TextToSpeechEngine 二、功能集成实践1、改造右上角菜单2、实现语音播报功能2.1、语音引擎的获取和关闭2.2、设置待播报文本2.3、speak 目标文本2.4、设置语音回调 三、总结 〇、前言 中华汉字洋洋洒洒何其多…...

物联网小范围高精度GPS使用

在园区内实现小范围高精度GPS&#xff08;全球定位系统&#xff09;定位&#xff0c;通常需要结合多种技术来弥补传统GPS在精度和覆盖范围上的不足。以下是实现小范围高精度GPS定位的解决方案&#xff0c;包括技术选择、系统设计和应用场景。 一、技术选择 在园区内实现高精度…...

一次有趣的前后端跨越排查

进行前后端代码联调的时候&#xff0c;使用axios调用后端请求&#xff0c;因为都是本地进行联调&#xff0c;所以没有考虑跨域的问题&#xff0c;写了一个get的请求接口&#xff0c;请求后端时&#xff0c;突然跳出下面的问题&#xff1a; 错误的信息一看很像就是跨域的问题&…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

go 里面的指针

指针 在 Go 中&#xff0c;指针&#xff08;pointer&#xff09;是一个变量的内存地址&#xff0c;就像 C 语言那样&#xff1a; a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10&#xff0c;通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...

nnUNet V2修改网络——暴力替换网络为UNet++

更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...

springboot 日志类切面,接口成功记录日志,失败不记录

springboot 日志类切面&#xff0c;接口成功记录日志&#xff0c;失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...

【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权

摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题&#xff1a;安全。文章将详细阐述认证&#xff08;Authentication) 与授权&#xff08;Authorization的核心概念&#xff0c;对比传统 Session-Cookie 与现代 JWT&#xff08;JS…...

aardio 自动识别验证码输入

技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”&#xff0c;于是尝试整合图像识别与网页自动化技术&#xff0c;完成了这套模拟登录流程。核心思路是&#xff1a;截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...