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

news/2025/3/1 2:57:44/

        在现代的地理信息系统(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. 异步加载数据:如果标记点数据量较大,可以采用分页或懒加载的方式动态加载数据。

总结

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


http://www.ppmy.cn/news/1575663.html

相关文章

校园快递平台系统(小程序论文源码调试讲解)

第4章 系统设计 用户对着浏览器操作&#xff0c;肯定会出现某些不可预料的问题&#xff0c;但是不代表着系统对于用户在浏览器上的操作不进行处理&#xff0c;所以说&#xff0c;要提前考虑可能会出现的问题。 4.1 系统设计思想 系统设计&#xff0c;肯定要把设计的思想进行统…

【Python 入门基础】—— 人工智能“超级引擎”,AI界的“瑞士军刀”,

欢迎来到ZyyOvO的博客✨&#xff0c;一个关于探索技术的角落&#xff0c;记录学习的点滴&#x1f4d6;&#xff0c;分享实用的技巧&#x1f6e0;️&#xff0c;偶尔还有一些奇思妙想&#x1f4a1; 本文由ZyyOvO原创✍️&#xff0c;感谢支持❤️&#xff01;请尊重原创&#x1…

白帽黑客系列教程之Windows驱动开发(64位环境)入门教程(二)

为什么要写这篇文章呢&#xff1f; 作为一名白帽黑客&#xff0c;如果想要学习ROOTKIT攻防技术&#xff0c;就必须要有能力进行驱动开发&#xff01; 本文章仅提供学习&#xff0c;切勿将其用于不法手段&#xff01; 在Windows操作系统的64位环境中&#xff0c;进行ROOTKIT攻…

【11】RUST使用cargo组织crate

文章目录 使用cargo组织crate重导出编译文档生成测试 cargo组织工作空间 TODOcrate.io账号 TODO暂时不看发布crate 使用cargo组织crate 重导出 在模块顶部使用pub use self::重导出&#xff0c;方便使用模块时候直接使用use mod_X::xxx。从而隐藏crate内部模块的结构。方便向…

边缘计算收益低的三大指标

边缘计算收益低的三大指标主要包括以下方面&#xff1a; 1. 资源贡献不足&#xff1a; 边缘计算的收益通常基于所提供的带宽、存储和计算资源来计算。如果设备的网络带宽有限、在线时间短或提供的存储容量较小&#xff0c;可能无法满足平台设定的最低贡献标准&#xff0c;从而导…

Java:泛型

文章目录 泛型什么都能传进go&#xff08;&#xff09;限定只能传进Car类ArrayList<Car> 和ArrayList<Bens>不一样 泛型通配符 &#xff1f;extends 上限super 下限 泛型的注意事项第二条 能看懂代码&#xff0c;知道是啥意思就行&#xff0c; 不用非得记住用 泛型…

2025年跟上AI新时代:带AI人工智能的蜜罐系统T-Pot

T-Pot是一个集成式、可选分布式的、支持多架构&#xff08;amd64、arm64&#xff09;的蜜罐平台&#xff0c;它支持20多种蜜罐&#xff0c;并提供了使用Elastic Stack的无数可视化选项、动态实时攻击地图以及众多安全工具&#xff0c;以进一步提升蜜罐系统体验。源码地址&#…

在 Vue 2 中使用 Canvas 绘制多边形

Canvas 是一个强大的工具&#xff0c;用于创建动态图形和交互式可视化内容。Vue.js 提供了便捷的双向数据绑定和组件化开发方式&#xff0c;使得在 Vue 中使用 Canvas 变得更加高效。本文将介绍如何在 Vue 2 中实现一个简单的多边形绘制工具&#xff0c;支持报警区域和安全区域…