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

在现代的地理信息系统(GIS)应用中,地图功能是不可或缺的一部分。无论是展示商业网点、旅游景点还是公共服务设施,地图都能以直观的方式呈现数据。然而,当数据量较大时,地图上可能会出现大量的标记点,这不仅会影响用户体验,还会导致性能问题。因此,我们需要一种方法来智能地聚合这些标记点,同时允许用户在需要时查看详细信息。

Leaflet 是一个轻量级的开源 JavaScript 地图库,它提供了丰富的 API 和插件支持,非常适合实现这种功能。结合 Leaflet.markercluster 插件,我们可以轻松实现标记点的聚合和动态显示。

项目背景

在最近开发中,我遇到了一个有趣的需求:实现基于 Leaflet 的地图功能,支持多类型点位标记和聚合。这个功能需要展示不同类型的数据点,并在地图缩放时智能聚合,以避免地图过于拥挤。经过一番努力,我成功实现了这一功能,并在此分享我的经验和代码实现。

实现思路

1. 数据结构设计

在实现功能之前,我们需要定义清晰的数据结构,以便更好地管理和展示不同类型的数据点。以下是点位数据的格式示例:

javascript 复制代码
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"
  }
  ...
]

此外,我们还需要定义支持的标记点类型和对应的样式配置:

javascript 复制代码
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 服务加载矢量地图和影像地图图层。

javascript 复制代码
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 服务的规范进行拼接。

javascript 复制代码
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 动态设置聚合图标的颜色和样式。

javascript 复制代码
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. 添加标记点

标记点的数据通常以数组的形式存储,每个数据项包含类型、名称、地址和坐标等信息,可以点击展示点位信息。我们根据数据项的类型,将其添加到对应的聚合图层中。

javascript 复制代码
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. 动态过滤标记点

为了提升用户体验,我们允许用户通过点击按钮来筛选特定类型的标记点。当用户点击按钮时,我们移除所有聚合图层,并根据用户的选择重新添加对应的图层。

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

相关推荐
aiguangyuan2 小时前
React Hooks 基础指南
react·前端开发
aiguangyuan18 小时前
React 项目初始化与搭建指南
react·前端开发
aiguangyuan1 天前
React 组件异常捕获机制详解
react·前端开发
aiguangyuan1 天前
深入理解 JSX:React 的核心语法
react·前端开发
aiguangyuan2 天前
React 基础语法
react·前端开发
aiguangyuan3 天前
React 核心概念与生态系统
react·前端开发
aiguangyuan3 天前
React 18 生命周期详解与并发模式下的变化
react·前端开发
aiguangyuan5 天前
Vue 3.0 中的路由导航守卫详解
前端开发·vue 3.0
aiguangyuan6 天前
Vue 3.0 状态管理方案Vuex详解
前端开发·vue 3.0
vivo互联网技术8 天前
纯前端实现图片伪3D视差效果
webgl·前端开发·pixi.js