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

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


http://www.kler.cn/a/569154.html

相关文章:

  • 2025最新Nginx高频面试题
  • 数据库操作命令详解:CREATE、ALTER、DROP 的使用与实践
  • C# 装箱(Boxing)与拆箱(Unboxing)
  • fastadmin 后台商品sku(vue)
  • 前后端传值响应下载文件压缩包
  • Docker入门指南:Windows下docker配置镜像源加速下载
  • 【计算机网络】TCP协议相关总结,TCP可靠性的生动讲解
  • Android Studio中gradle一栏中出现nothing to show 提示的解决方法
  • ubuntu中ollama设置记录
  • git 鼓励频繁提交commit early, commit often,用好分支,多用分支
  • 类和对象(6)——Object类、内部类
  • 大模型工程师学习日记(六):Embedding 与向量数据库
  • leetcode 598. 区间加法 II 简单
  • springboot417-基于Spring Boot的酒店后台管理系统(源码+数据库+纯前后端分离+部署讲解等)
  • ESP-WIFI-MESH组网方案,设备物联网无线交互,WiFi通信智能联动
  • c语言getchar
  • 从统计学视角看机器学习的训练与推理
  • Svelte 开发 AI 应用:高效轻量级前端框架的 AI 集成探索
  • LM studio 加载ollama的模型
  • 阿里云ECS Ubuntu PPTP VPN无法访问以太网