Skip to content

标记聚合

上次更新 2025年8月20日星期三 3:16:20 字数 0 字 时长 0 分钟

标记聚合是一种地图可视化技术,可以将地图上密集分布的标记点进行聚合显示。当地图缩小时,相近的标记点会合并成一个聚合点,显示该区域内标记点的数量;当地图放大时,聚合点会分散成单个标记点。这种效果可以有效避免标记点重叠,提升地图的可读性。

示例

新标签页预览

代码实现

vue
<template>
  <div class="h-screen">
    <div id="map" class="w-full h-full"></div>
  </div>
</template>

<script setup>
import { onMounted } from "vue";
import "ol/ol.css";
import Map from "ol/Map";
import View from "ol/View";
import TileLayer from "ol/layer/Tile";
import XYZ from "ol/source/XYZ";
import VectorSource from "ol/source/Vector";
import VectorLayer from "ol/layer/Vector";
import Feature from "ol/Feature";
import Point from "ol/geom/Point";
import { fromLonLat } from "ol/proj";
import Cluster from "ol/source/Cluster";
import { Circle, Fill, Stroke, Style, Text } from "ol/style";

onMounted(() => {
  // 创建地图
  const map = new Map({
    target: "map",
    view: new View({
      center: fromLonLat([117.33083, 39.094899]), // 天津
      zoom: 4,
    }),
  });

  // 添加天地图图层
  const tdtLayer = new TileLayer({
    source: new XYZ({
      url: "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=d32c6748c80f81a44acd8633cea41dfd",
    }),
  });
  map.addLayer(tdtLayer);

  // 标记点数据
  const locations = [
    { lat: 23.1291, lng: 113.2644, name: "广东省" },
    { lat: 34.757, lng: 113.6249, name: "河南省" },
    { lat: 26.8154, lng: 106.3315, name: "贵州省" },
    { lat: 39.0949, lng: 117.3308, name: "天津" },
    { lat: 29.6469, lng: 91.1322, name: "西藏" },
    { lat: 43.666, lng: 126.1924, name: "吉林" },
    { lat: 36.62, lng: 95.66, name: "青海" },
    { lat: 20.044, lng: 110.1999, name: "海南" },
    { lat: 31.2304, lng: 121.4737, name: "上海" },
    { lat: 43.793, lng: 87.6, name: "新疆" },
    { lat: 38.469, lng: 106.2782, name: "宁夏" },
    { lat: 40.8183, lng: 111.6704, name: "内蒙古" },
    { lat: 39.9042, lng: 116.4074, name: "北京" },
    { lat: 41.8057, lng: 123.4315, name: "辽宁" },
    { lat: 29.563, lng: 106.55, name: "重庆" },
    { lat: 36.0583, lng: 102.4572, name: "甘肃" },
    { lat: 37.8734, lng: 112.562, name: "山西" },
    { lat: 34.3293, lng: 108.9402, name: "陕西" },
    { lat: 29.1042, lng: 115.8221, name: "江西" },
    { lat: 30.5928, lng: 114.3055, name: "湖北" },
    { lat: 30.2741, lng: 120.1551, name: "浙江" },
    { lat: 24.882, lng: 102.8329, name: "云南" },
    { lat: 31.82, lng: 117.2272, name: "安徽" },
    { lat: 27.6104, lng: 112.9834, name: "湖南" },
    { lat: 23.1242, lng: 108.32, name: "广西" },
    { lat: 32.0603, lng: 118.7969, name: "江苏" },
    { lat: 26.5783, lng: 106.7135, name: "贵州" },
    { lat: 38.4161, lng: 115.4839, name: "河北" },
  ];

  // 创建矢量要素
  const features = locations.map((location) => {
    return new Feature({
      geometry: new Point(fromLonLat([location.lng, location.lat])),
      name: location.name,
    });
  });

  // 创建矢量源
  const source = new VectorSource({
    features: features,
  });

  // 创建聚合源
  const clusterSource = new Cluster({
    distance: 40,
    source: source,
  });

  // 创建聚合图层样式
  const styleCache = {};
  const clusters = new VectorLayer({
    source: clusterSource,
    style: function (feature) {
      const size = feature.get("features").length;
      let style = styleCache[size];
      if (!style) {
        style = new Style({
          image: new Circle({
            radius: 10 + Math.min(size, 20),
            fill: new Fill({
              color: "rgba(255, 153, 0, 0.8)",
            }),
            stroke: new Stroke({
              color: "#fff",
              width: 2,
            }),
          }),
          text: new Text({
            text: size.toString(),
            fill: new Fill({
              color: "#fff",
            }),
          }),
        });
        styleCache[size] = style;
      }
      return style;
    },
  });

  map.addLayer(clusters);

  // 添加点击事件
  map.on("click", function (e) {
    clusters.getFeatures(e.pixel).then(function (clickedFeatures) {
      if (clickedFeatures.length) {
        const features = clickedFeatures[0].get("features");
        if (features.length === 1) {
          const coords = features[0].getGeometry().getCoordinates();
          map.getView().animate({
            center: coords,
            zoom: map.getView().getZoom() + 2,
            duration: 500,
          });
        } else {
          map.getView().animate({
            center: clickedFeatures[0].getGeometry().getCoordinates(),
            zoom: map.getView().getZoom() + 2,
            duration: 500,
          });
        }
      }
    });
  });
});
</script>

<style scoped>
.h-screen {
  height: 24rem;
}
</style>
关注公众号