Cesuim基础用法(Vue3 + Pinna)

130 阅读2分钟

Cesuim基础用法(Vue3 + Pinna)

1.1确认一张地图只需要

1.1定义状态变量
1.viewer (Cesium视图对象)
2.tilesets 链接数组
3.entities 实体集合
4.clusterPrimitives 聚合原始集
  1. 设置视图

    • setViewer(v: any):设置 Cesium 视图对象。
  2. 热力图管理

    • addHeatMap(data: any, zoom: boolean = true):添加热力图,支持设置是否缩放到热力图层。
    • removeHeatMap():移除所有热力图。
  3. 点的管理

    • addPoint(data: any):在地图上添加一个点,使用经纬度和图像。
    • removePoint(id: any):根据 ID 移除指定的点。
    • removeAllPoint():清空所有点。
  4. GeoJSON 数据管理

    • addGeoJson(geojson: any, param: any):加载 GeoJSON 数据并添加到视图中。
    • removeGeoJson():移除所有 GeoJSON 数据源。
  5. 聚合点的管理

    • formatClusterPoint(features: any, type: string):格式化聚合点,设置聚合的距离和图标。
    • removeClusterPoint(type: string):移除指定类型的聚合点。
  6. 3D Tiles 管理

    • add3DTile(url: any):添加 3D Tiles 数据。
    • removeTiles():移除所有 3D Tiles。
  7. 视图重置

    • resetViewer(type: '2D' | '3D'):根据类型重置视图为 2D 或 3D。
  8. 线条管理

    • addLine(id: string, positions: any[], category: string, color: Cesium.Color):添加线条。
    • removeAllLines():移除所有线条。
    • removeLinesByCategory(category: string):根据类别移除线条。
  9. 区域管理

    • addRegion(id: string, positions: any[], properties: any, color: Cesium.Color):添加区域。

    • removeRegion(id: string):移除指定区域。

    • removeAllRegions():移除所有区域。

  10. 合成图标

import { defineStore } from 'pinia';
import * as Cesium from 'cesium';
import { CesiumHeatmap } from 'cesium-heatmap-es6';
import PrimitiveCluster from '@/utils/cesium/primitiveCluster.js';
import { ref } from 'vue';
​
export const useCesiumStore = defineStore('cesium', () => {
  let viewer: any = null;
  const tilesets: any = [];
  const heatmaps: any = [];
  const entities: any = {};
  const clusterPrimitives: any = {};
  const isLoading = ref(false);
​
  const setViewer = (v: any) => {
    viewer = v;
  };
​
  const addHeatMap = (data: any, zoom: boolean = true) => {
    const heatmapLayer = new CesiumHeatmap(viewer, {
      zoomToLayer: zoom,
      points: data,
      heatmapOptions: {
        radius: 5,
        maxOpacity: 1,
        minOpacity: 0
      },
      heatmapDataOptions: {
        max: 300,
        min: 0
      }
    });
​
    heatmaps.push(heatmapLayer);
  };
​
  const removeHeatMap = () => {
    heatmaps.forEach((heatmap: any) => {
      heatmap.remove();
    });
  };
​
  const addPoint = (data: any) => {
    const entity = viewer?.entities.add({
      position: Cesium.Cartesian3.fromDegrees(data.lng, data.lat, 30),
      billboard: {
        image: data.img,
        scale: data.scale || 1.0,
        width: 30,
        height: 40,
        pixelOffset: new Cesium.Cartesian2(0, -20)
      },
      properties: {
        data: data.data
      }
    });
    if (entities[data.id]) {
      entities[data.id].push(entity);
    } else {
      entities[data.id] = [entity];
    }
  };
​
  const removePoint = (id: any) => {
    if (entities[id]) {
      entities[id].forEach((entity: any) => {
        viewer?.entities.remove(entity);
      });
    }
  };
​
​
  // 清空所有点
const removeAllPoint = () => {
  Object.keys(entities).forEach(id => {
    entities[id].forEach((entity: any) => {
      viewer?.entities.remove(entity);
    });
  });
  // 清空 entities 对象
  for (const id in entities) {
    if (entities.hasOwnProperty(id)) {
      delete entities[id];
    }
  }
};
  const addGeoJson = (
    geojson: any,
    param = { stroke: Cesium.Color.HOTPINK, fill: Cesium.Color.PINK, strokeWidth: 5, markerSymbol: '', markerSize: 0 }
  ) => {
    Cesium.GeoJsonDataSource.load(geojson, param).then((dataSource: any) => {
      viewer?.dataSources.add(dataSource);
      // 可以调整视角来适应加载的数据
      viewer.flyTo(dataSource);
    });
  };
​
  const removeGeoJson = () => {
    viewer?.dataSources.removeAll();
  };
​
  const formatClusterPoint = (features: any, type: string) => {
    const primitivesCollection = new Cesium.PrimitiveCollection();
    const billboardsCollectionCombine = new Cesium.BillboardCollection();
    const primitivecluster: any = new PrimitiveCluster();
​
    //与entitycluster相同设置其是否聚合 以及最大最小值
    primitivecluster.enabled = true;
    primitivecluster.pixelRange = 30;
    primitivecluster.minimumClusterSize = 2;
    // primitivecluster._pointCollection = pointCollection;
    // primitivecluster._labelCollection = labelCollection;
​
    //后面设置聚合的距离及聚合后的图标颜色显示与官方案例一样
    for (let i = 0; i < features.length; i++) {
      const feature = features[i];
      const coordinates = feature.geometry.coordinates;
      const position = Cesium.Cartesian3.fromDegrees(coordinates[0], coordinates[1], 100);
​
      // 带图片的点
      billboardsCollectionCombine.add({
        id: feature.properties,
        image: import.meta.env.BASE_URL + `/texture/${type}.png`,
        width: 32,
        height: 40,
        position
      });
    }
​
    primitivecluster._billboardCollection = billboardsCollectionCombine;
    // 同时在赋值时调用_initialize方法
    primitivecluster._initialize(viewer.scene);
​
    primitivesCollection.add(primitivecluster);
    const clusterPrimitive = viewer.scene.primitives.add(primitivesCollection);
​
    clusterPrimitives[type] = clusterPrimitive;
​
    primitivecluster.clusterEvent.addEventListener((clusteredEntities: any, cluster: any) => {
      // console.log('clusteredEntities', clusteredEntities)
      // console.log('cluster', cluster)
      // 关闭自带的显示聚合数量的标签
      cluster.label.show = false;
      cluster.billboard.clusterNumber = clusteredEntities.length;
      cluster.billboard.clusterData = JSON.parse(JSON.stringify(clusteredEntities));
      cluster.billboard.show = true;
      cluster.billboard.disableDepthTestDistance = Number.POSITIVE_INFINITY;
      cluster.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM;
​
      // 根据聚合数量的多少设置不同层级的图片以及大小
      if (clusteredEntities.length >= 100) {
        cluster.billboard.image = combineIconAndLabel(import.meta.env.BASE_URL + `/texture/cluster-5.png`, clusteredEntities.length, 64);
      } else if (clusteredEntities.length >= 50) {
        cluster.billboard.image = combineIconAndLabel(import.meta.env.BASE_URL + `/texture/cluster-3.png`, clusteredEntities.length, 64);
      } else if (clusteredEntities.length >= 10) {
        cluster.billboard.image = combineIconAndLabel(import.meta.env.BASE_URL + `/texture/cluster-2.png`, clusteredEntities.length, 64);
      } else {
        cluster.billboard.image = combineIconAndLabel(import.meta.env.BASE_URL + `/texture/cluster-1.png`, clusteredEntities.length, 64);
      }
​
      // cluster.billboard.image = "/images/school-icon.png";
      cluster.billboard._imageHeight = 60;
      cluster.billboard._imageWidth = 60;
      cluster.billboard._dirty = false;
      cluster.billboard.width = 60;
      cluster.billboard.height = 60;
    });
    return primitivecluster;
  };
​
  /**
   * @description: 将图片和文字合成新图标使用(参考Cesium源码)
   * @param {*} url:图片地址
   * @param {*} label:文字
   * @param {*} size:画布大小
   * @return {*} 返回canvas
   */
  function combineIconAndLabel(url: string, label: string, size: number) {
    // 创建画布对象
    const canvas = document.createElement('canvas');
    canvas.width = size;
    canvas.height = size;
    const ctx: any = canvas.getContext('2d');
​
    // @ts-ignore
    const promise = new Cesium.Resource.fetchImage(url).then((image: any) => {
      // 异常判断
      try {
        ctx.drawImage(image, 0, 0, size, size);
      } 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);
​
      return canvas;
    });
    return promise;
  }
​
  const removeClusterPoint = (type: string) => {
    viewer?.scene.primitives.remove(clusterPrimitives[type]);
  };
​
  const add3DTile = async (url: any) => {
    const tileset = await Cesium.Cesium3DTileset.fromUrl(url);
    viewer?.scene.primitives.add(tileset);
    tilesets.push(tileset);
    // viewer.flyTo(tileset)
  };
​
  const removeTiles = () => {
    tilesets.forEach((item: any) => {
      viewer?.scene.primitives.remove(item);
    });
    tilesets.length = 0;
  };
​
  const resetViewer = (type: '2D' | '3D') => {
    if (type === '2D') {
      viewer?.camera.flyTo({
        destination: Cesium.Cartesian3.fromDegrees(114.25859171304447, 30.606768727988367, 200000)
      });
      removeTiles();
      viewer.scene.morphTo2D(0);
    }
    if (type === '3D') {
      viewer.scene.morphTo3D(0);
      viewer?.camera.flyTo({
        destination: Cesium.Cartesian3.fromDegrees(114.25859171304447, 30.606768727988367, 485),
        orientation: {
          heading: Cesium.Math.toRadians(0.0),
          pitch: Cesium.Math.toRadians(-30.0),
          roll: 0.0
        }
      });
    }
  };
​
​
  let lines: any = {}; // 用于存储不同类别的线条
​
const addLine = (id: string, positions: any[], category: string, color: Cesium.Color = Cesium.Color.RED) => {
  const polyline = viewer?.entities.add({
    polyline: {
      positions: Cesium.Cartesian3.fromDegreesArray(positions),
      width: 2,
      material: Cesium.Color.BLUE,
​
    }
  });
​
  if (!lines[category]) {
    lines[category] = [];
  }
  lines[category].push({ id, entity: polyline });
};
​
//删除所有线
const removeAllLines = () => {
  Object.keys(lines).forEach(category => {
    lines[category].forEach((line: any) => {
      viewer?.entities.remove(line.entity);
    });
  });
  lines = {};
};
​
const removeLinesByCategory = (category: string) => {
  if (lines[category]) {
    lines[category].forEach((line: any) => {
      viewer?.entities.remove(line.entity);
    });
    delete lines[category];
  }
};
​
​
​
let regions: any = {}; // 用于存储不同的区域
​
const addRegion = (id: string, positions: any[], properties: any, color: Cesium.Color = Cesium.Color.ORANGE) => {
  let area = Cesium.Cartesian3.fromDegreesArray(positions);
  const regionEntity = viewer?.entities.add({
    polygon: {
      hierarchy: new Cesium.PolygonHierarchy(area),
      material: Cesium.Color.ORANGE.withAlpha(0.4),
      outline: true,
     outlineWidth: 4,
      outlineColor: Cesium.Color.WHITE,
    },
    properties: properties
  });
​
//  viewer?.camera.flyTo({
//   destination: Cesium.Cartesian3.fromDegrees(positions[0], positions[1], 1000)
// });
  regions[id] = regionEntity;
};
​
// 删除指定区域图层
const removeRegion = (id: string) => {
  if (regions[id]) {
    viewer?.entities.remove(regions[id]);
    delete regions[id];
  }
};
​
// 删除所有区域
const removeAllRegions = () => {
  Object.keys(regions).forEach(id => {
    viewer?.entities.remove(regions[id]);
    delete regions[id];
  });
};
​
​
​
  return {
    viewer,
    isLoading,
    add3DTile,
    removeTiles,
    addLine,
    removeAllLines,
    addRegion,
    removeAllRegions,
    removeRegion,
    removeLinesByCategory,
    setViewer,
    addHeatMap,
    removeHeatMap,
    addPoint,
    removePoint,
    removeAllPoint,
    formatClusterPoint,
    removeClusterPoint,
    resetViewer,
    addGeoJson,
    removeGeoJson
  };
});

index.vue

<template>
<div id="cesiumcontainer" ></div>
</template><script setup lang="ts">
import { onMounted } from 'vue'
import * as Cesium from 'cesium'
import { useCesiumStore } from '@/store/modules/cesiummap'
import { bus } from '@/utils/mitt'
const cesiumStore = useCesiumStore()
onMounted(()=>{
   Cesium.Ion.defaultAccessToken = 'xxxxxx'
   cesiumStore.isLoading = true
   
   const MODE = import.meta.env.MODE
   let layer = null
   if(MODE == 'development')
   {
      layer = newCesium.UrlTemplateImagerProvider({
      url:'xxxx',
      minimumLevel:3,
      maximumLevel:18
      })
   }else{
      layer = new Cesium.WebMapTileServiceImageryProvider({
​
      })
​
​
    }
    
    const viewer = new Cesium.Viewer('cesiumcontainer',{
        timeline:false,
        animation:false,
        baseLayerPicker:false,
        geocoder:false,
        homeButtom:fasle,
        navigationHelpButtom:false,
        infoBox:fasle,
    })
    
    viewer.scene.morphTo2D(0);
    viewer.imagerLayers.addImgeryProvider(layer)
    
    cesiumStore.setViewer(viewer)
    
    viewer.camera.setView({
      destination: Cesium.Cartesian3.fromDegrees(114.25859171304447, 30.606768727988367, 200000)
    })
​
    const loadEnd=(e:any)=>{
    if(e===0)
    {
       cesiumStore.isLoading = false
       bus.$emit('cesiumReady',viewer)
       viewer.scene.globe.tileLoadProgressEvent.removeEventListener(loadEnd)
    }
    }
    viewer.scene.globe.tileLoadProgressEvent.addEventListener(loadEnd)
    viewer.scene.screenSpaceCameraController.minmumZoomDistance = 10
    viewer.scene.screenSpaceCameraController.maxmumZoomDistance = 30000
    
    viewer.scene.postProcessStages.fxaa.enabled = false
})
</script><style scoped lang="scss">
  #cesiumcontainer {
    width: 100%;
    height: 100%;
​
    :deep(.cesium-viewer-bottom) {
      display: none;
    }
  }
</style>

在别的页面引入,进行出事化渲染,增加交互逻辑等

<cesium-map></cesium-map>


const initMap = (viewer: any) => {

viewerObj.value = viewer;

// 左键点击

viewer.screenSpaceEventHandler.setInputAction((e: any) => {

const pick = viewer.scene.pick(e.position);

if (Cesium.defined(pick) && pick.id && pick.id.id && pick.id.properties) {

const data = pick.id.properties?.data?._value;



}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

// 右键点击事件

viewer.screenSpaceEventHandler.setInputAction((e: any) => {

const pick = viewer.scene.pick(e.position);

if (Cesium.defined(pick) && pick.id && pick.id.id && pick.id.properties) {


} else {
}

}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);



};

加载成功

image.png

撒点

image.png