前端~三维地图(cesium)点位聚合

发布于:2025-06-12 ⋅ 阅读:(21) ⋅ 点赞:(0)

实现效果如下

在这里插入图片描述

实现步骤

  1. 后端接口获取全部点位数据
  2. EntityCluster 是DataSource的一个属性,DataSource 中在通过加载entities 自动展示聚合数据,DataSouce 可以之前加载行政边界时候用过,可以通过load 加载数据,DataSource 是一个接口,有很多实现类,但是我们这边是从后端接口获取的,不是geoJson 数据,可以使用CustomDataSource 自定义数据源,然后赋值entities
  3. 修改聚合样式,Cesium 可以通过pinBuilder 实现点位以及聚合后的数字,修改里面的文字即可,也可以通过设置自定义图片实现。
  4. 自定义样式,按照聚合数量的多少选择不同的图片,同时设置图片的大小
  5. 很多人也可能是为了找这个图标,我提供一个方法,阿里云图标库 ,直接搜索聚合找自己想要的样式就可以

具体代码实现 MapCollection.ts

// 百瑞通泄露预警平台地图业务相关
import * as Cesium from 'cesium'
import { EgasService } from '@/api/installService'
import { MapEntityType } from '@/views/egas/gis/MapEvent'
import { CustomDiffuseCircleMaterialProperty } from '@/views/egas/gis/material/CustomDiffuseCircleMaterialProperty'

export const addBaithonEquipmentPoints = async (viewer: Cesium.Viewer) => {
  // 1.清空地图上所有点位
  await clearAllPoints(viewer)
  // 2.获取站点数据
  const result: any = await EgasService.baithonEquipmentPointDataList()
  // 3.通过DataSource 设置点位聚合
  const dataSource = new Cesium.CustomDataSource('myData')
  result.data.forEach((item: any) => {
    if (item.isAlarms) {
      dataSource.entities.add({
        name: MapEntityType.BAITHON_POINT,
        // 通过properties 携带参数
        properties: new Cesium.PropertyBag(item),
        position: Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude, 0),
        billboard: {
          // 图片路径
          image: `/imgs/map_icon/location_warning.png`,
          // 像素偏移
          pixelOffset: new Cesium.Cartesian2(0, -10)
        },
        ellipse: {
          semiMajorAxis: 150,
          semiMinorAxis: 150,
          material: new CustomDiffuseCircleMaterialProperty({
            color: new Cesium.Color(1.0, 0.0, 0.0, 1.0),
            speed: 2.0,
            circleCount: 2,
            gradient: 0.2
          })
        }
      })
    } else {
      dataSource.entities.add({
        name: MapEntityType.BAITHON_POINT,
        properties: new Cesium.PropertyBag(item),
        position: Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude, 0),
        billboard: {
          image: `/imgs/map_icon/location_normal.png`,
          // 像素偏移
          pixelOffset: new Cesium.Cartesian2(0, -10)
        }
      })
    }
  })

  // 4.设置聚合属性
  dataSource.clustering.enabled = true
  dataSource.clustering.pixelRange = 15
  dataSource.clustering.minimumClusterSize = 3
  // 设置聚合属性监听
  dataSource.clustering.clusterEvent.addEventListener(async (clusteredEntities, cluster) => {
    cluster.label.show = false
    cluster.label.font = '14px Helvetica'
    cluster.billboard.heightReference = Cesium.HeightReference.CLAMP_TO_GROUND
    cluster.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM
    // 按照聚合数量进行图片设置
    if (clusteredEntities.length >= 20) {
      combineIconAndLabel(
        './imgs/map_icon/cluster/cluster_04.png',
        clusteredEntities.length,
        64
      ).then((item) => {
        cluster.billboard.image = item.toDataURL()
        cluster.billboard.width = 72
        cluster.billboard.height = 72
      })
    } else if (clusteredEntities.length >= 12) {
      combineIconAndLabel(
        './imgs/map_icon/cluster/cluster_03.png',
        clusteredEntities.length,
        64
      ).then((item) => {
        cluster.billboard.image = item.toDataURL()
        cluster.billboard.width = 56
        cluster.billboard.height = 56
      })
    } else if (clusteredEntities.length >= 8) {
      combineIconAndLabel(
        './imgs/map_icon/cluster/cluster_02.png',
        clusteredEntities.length,
        64
      ).then((item) => {
        cluster.billboard.image = item.toDataURL()
        cluster.billboard.width = 48
        cluster.billboard.height = 48
      })
    } else {
      combineIconAndLabel(
        './imgs/map_icon/cluster/cluster_01.png',
        clusteredEntities.length,
        64
      ).then((item) => {
        cluster.billboard.image = item.toDataURL()
        cluster.billboard.width = 40
        cluster.billboard.height = 40
      })
    }

    // cluster.billboard.image = pinImg
    cluster.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM
    cluster.billboard.heightReference = Cesium.HeightReference.CLAMP_TO_GROUND
    cluster.billboard.show = true
  })

  // 5.dataSource 添加到图层中
  await viewer.dataSources.add(dataSource)
  await viewer.flyTo(dataSource)
}

export const clearAllPoints = (viewer: Cesium.Viewer) => {
  const entitiesToRemove: Cesium.Entity[] = []

  viewer.entities.values.forEach((entity) => {
    if (entity.name === MapEntityType.BAITHON_POINT) {
      entitiesToRemove.push(entity)
    }
  })

  entitiesToRemove.forEach((entity) => {
    viewer.entities.remove(entity)
  })
}
/**
 * @description: 将图片和文字合成新图标使用(参考Cesium源码)
 * @param {*} url:图片地址
 * @param {*} label:文字
 * @param {*} size:画布大小
 * @return {*} 返回canvas
 */
const combineIconAndLabel = (url, label, size) => {
  // 创建画布对象
  const canvas = document.createElement('canvas')
  canvas.width = size
  canvas.height = size
  const ctx = canvas.getContext('2d')
  return Cesium.Resource.fetchImage(url).then((image) => {
    // 异常判断
    try {
      ctx.drawImage(image, 0, 0)
    } catch (e) {
      console.log(e)
    }

    // 渲染字体
    // font属性设置顺序:font-style, font-variant, font-weight, font-size, line-height, font-family
    ctx.fillStyle = Cesium.Color.WHITE.toCssColorString()
    ctx.font = 'bold 20px Microsoft YaHei'
    ctx.textAlign = 'center'
    ctx.textBaseline = 'middle'
    ctx.fillText(label, size / 2, size / 2 + 4)
    return canvas
  })
}
export const clearWarning = async (viewer: Cesium.Viewer) => {
  await addBaithonEquipmentPoints(viewer)
}