cesium 使用

十年生死两茫茫,写程序,到天亮。
千行代码,Bug何处藏。
纵使上线又怎样,朝令改,夕断肠。
领导每天新想法,天天改,日日忙。
相顾无言,惟有泪千行。
每晚灯火阑珊处,夜难寐,又加班,鬓如霜。

新版本与旧版本部分使用方法有区别,这里最新版本为例,提供思路. 部分语法为 ts 如果使用 js 注意语法修改

环境

🍇vite

🍇vue3

🍇cesium

🍇ts/js

Openlayers笔记

Cesium笔记

1. 安装 cesium

1. 安装

pnpm i cesium vite-plugin-cesium

    

2. 修改vite.config.js⽂件, 引入cesium

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import cesium from 'vite-plugin-cesium'

// https://vitejs.dev/config/
export default defineConfig({
 plugins: [vue(),cesium()]
})

3. 基础创建代码

<template>
  <div id="cesium"></div>
</template>

<script setup>
import { shallowRef, onMounted } from "vue";
import * as Cesium from 'cesium'

const viewer = shallowRef(null)

onMounted(() => {
  // 创建 Viewer
  viewer.value = new Cesium.Viewer('cesium', {})
})
</script>

4. 解决底图南北极空洞缺失

加载的web墨卡托投影的本地切片,该数据纬度范围是在-85~85范围内的,所以南北极是没有影像的。在 web 墨卡托投影中,由于经纬度直投下地物会被明显拉长,角度也会发生变化,为保证投影是正方形,切掉了南北 85.051129° 纬度以上的地区,这就导致了南北极地区在使用该投影方式的数据时出现空洞缺失1。viewer.imageryLayers.removeAll()`方法删除Cesium的默认影像层,解决这个问题。

viewer.imageryLayers.removeAll()

2. cesium 加载3dtiles数据

/**
 * 加载3dtiles数据
 */
const loadTiles3d = async () => {
  const tiles3dLink = "http://192.168.113.100/static/tileset/tileset.json";


  if (!viewer.value) {
    console.error("Cesium Viewer 尚未初始化。");
    return;
  }

  try {
    const tileset = await Cesium.Cesium3DTileset.fromUrl(
      tiles3dLink,
      {
        skipLevelOfDetail: true,  // 跳过LOD
        baseScreenSpaceError: 1024, // 基础屏幕空间误差
        skipScreenSpaceErrorFactor: 16, // 跳过屏幕空间误差因子
        skipLevels: 1,  // 跳过的LOD级别
        immediatelyLoadDesiredLevelOfDetail: false, // 立即加载所需的LOD级别
        loadSiblings: false,  // 加载兄弟节点
        cullWithChildrenBounds: true  // 裁剪与子节点边界
      }
    );
    viewer.value.scene.primitives.add(tileset);
  } catch (error) {
    console.error("创建3D Tileset时发生错误:", error);
  }
}

3. 设置点击事件处理器,用于获取WMS图层的要素信息

/**
 * 设置点击事件处理器,用于获取WMS图层的要素信息
 */
const setupClickHandler = () => {
  if (!viewer.value) return;

  // 点击事件处理
  viewer.value.screenSpaceEventHandler.setInputAction(async (click: any) => {
    const features = await viewer.value?.imageryLayers.pickImageryLayerFeatures(
      viewer.value?.camera.getPickRay(click.position) ?? new Cesium.Ray(),
      viewer.value.scene
    );

    console.log(features, "features");

    if (features && Array.isArray(features) && features.length > 0) {
      // 找到要素
    } else {
      // 如果没有点击到要素
    }
  }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
};

4. 点击事件后高亮点击的面

要结合 3. 设置点击事件处理器,用于获取WMS图层的要素信息 的步骤,获取 feature

/**
 * 高亮显示点击的要素
 * @param feature 要素数据
 */
const highlightFeature = async (feature: FeatureItem) => {
  if (!viewer.value || !feature || !feature.data || !feature.data.geometry) {
    console.log("无效的viewer或feature数据");
    return;
  }

  try {
    // 清除之前的高亮
    clearHighlight();

    // 创建数据源
    const dataSource = new Cesium.GeoJsonDataSource();

    // 加载GeoJSON数据
    await dataSource.load(feature.data.geometry, {
      fill: Cesium.Color.RED.withAlpha(0.7), // 填充颜色,半透明
      clampToGround: true, // 贴地
    });

    // 为所有实体添加动画效果
    const entities = dataSource.entities.values;
    for (let i = 0; i < entities.length; i++) {
      const entity = entities[i];

      // 如果实体有多边形
      if (entity.polygon) {
        // 创建闪烁材质
        entity.polygon.material = new Cesium.ColorMaterialProperty(
          new Cesium.CallbackProperty(function (time) {
            // 使用正弦函数创建脉动效果,值在0.3到0.8之间变化
            const alpha = 0.3 + (Math.sin(Date.now() / 500) + 1) * 0.25;
            return Cesium.Color.YELLOW.withAlpha(alpha);
          }, false)
        );

        // 添加轮廓发光效果
        entity.polygon.outlineColor = new Cesium.ConstantProperty(
          Cesium.Color.RED
        );
        entity.polygon.outlineWidth = new Cesium.ConstantProperty(3);
        entity.polygon.outline = new Cesium.ConstantProperty(true);
      }

      // 如果实体有线条
      if (entity.polyline) {
        entity.polyline.material = new Cesium.PolylineGlowMaterialProperty({
          glowPower: 0.2,
          color: Cesium.Color.YELLOW,
        });
        entity.polyline.width = new Cesium.ConstantProperty(5);
      }
    }

    // 添加数据源到viewer
    viewer.value.dataSources.add(dataSource);

    // 保存当前高亮的数据源,以便后续清除
    planningStore.setHighlightDataSource(dataSource);

    console.log("要素高亮显示成功");
  } catch (error) {
    console.error("高亮要素失败:", error);
  }
};

5. Cesium 修改底图颜色的方法

方法一


modifyBaseMapOnly(viewer.value, {
  //反色?
  invertColor: true,
  //滤色值
  filterRGB: [60, 145, 172],
  //亮度
  brightness: 0.6,
  //对比度
  contrast: 1.8,
  //色调
  hue: 1,
  //饱和度
  saturation: 0,
  //伽马
  gamma: 0.3,
});



/**
 * 修改地图底图颜色,不影响后续添加的图层
 * @param viewer Cesium viewer 实例
 * @param options 颜色配置选项
 */
function modifyBaseMapOnly(viewer: any, options: any) {
  // 只修改第一个图层(通常是底图)
  const baseLayer = viewer.imageryLayers.get(0);

  if (!baseLayer) {
    console.warn("未找到底图图层");
    return;
  }

  // 设置图层级别的颜色属性
  baseLayer.brightness = options.brightness || 0.6;
  baseLayer.contrast = options.contrast || 1.8;
  baseLayer.gamma = options.gamma || 0.3;
  baseLayer.hue = options.hue || 1;
  baseLayer.saturation = options.saturation || 0;

  // 为特定图层创建自定义着色器
  if (
    options.invertColor ||
    (options.filterRGB && options.filterRGB.length > 0)
  ) {
    // 创建自定义材质
    const customMaterial = new Cesium.Material({
      fabric: {
        type: "BaseMapFilter",
        uniforms: {
          image: baseLayer._imageryProvider,
          invertColor: options.invertColor || false,
          filterRGB: options.filterRGB || [255, 255, 255],
        },
        source: `
          czm_material czm_getMaterial(czm_materialInput materialInput) {
            czm_material material = czm_getDefaultMaterial(materialInput);
            vec2 st = materialInput.st;
            vec4 color = texture2D(image, st);

            // 应用颜色反转
            if (invertColor) {
              color.r = 1.0 - color.r;
              color.g = 1.0 - color.g;
              color.b = 1.0 - color.b;
            }

            // 应用RGB滤镜
            color.r = color.r * filterRGB.r / 255.0;
            color.g = color.g * filterRGB.g / 255.0;
            color.b = color.b * filterRGB.b / 255.0;

            material.diffuse = color.rgb;
            material.alpha = color.a;
            return material;
          }
        `,
      },
    });

    // 应用自定义材质到底图
    baseLayer.material = customMaterial;
  }
}

方法二

// xx

6. 事件

1. 相机缩放事件

clock.onTick

优点:响应灵敏,每个时钟都会执行,地图缩放变化能及时捕捉到
缺点:轮循式,无论地图缩放是否有变化都会执行,可自己判断是否有变化,以决定执行业务代码

viewer.value?.clock.onTick.addEventListener(() => {
  if (!viewer.value) return;
  const height = viewer.value.camera.positionCartographic.height;
  console.log("当前相机高度:", height);
});

camera.changed

优点:响应灵敏,每个时钟都会执行,地图缩放变化能及时捕捉到
缺点:轮循式,无论地图缩放是否有变化都会执行,可自己判断是否有变化,以决定执行业务代码

viewer.value?.camera.changed.addEventListener(() => {
  if (!viewer.value) return;
  const height = viewer.value.camera.positionCartographic.height;
  console.log("当前相机高度:", height);
});

    

ScreenSpaceEventHandler.setInputAction

优点:响应式且响应灵敏
缺点:只能监听鼠标滚轮变化触发的地图缩放,如果是界面上按钮触发或直接代码触发等无法监听

const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction((wheelment) => {
  // TODO
}, Cesium.ScreenSpaceEventType.WHEEL);

7. 插件

1. cesium-navigation-es6

导入

// 需要先引入cesium-navigation-es6插件
import CesiumNavigation from "cesium-navigation-es6";

使用

new CesiumNavigation(viewer.value, {
  // 用于在使用重置导航重置地图视图时设置默认视图控制。接受的值是Cesium.Cartographic 和 Cesium.Rectangle.
  defaultResetView: {
    longitude: 105.0,
    latitude: 35.0,
    height: 10000000,
  },
  // 用于启用或禁用罗盘。true是启用罗盘,false是禁用罗盘。默认值为true。
  enableCompass: true,
  // 用于启用或禁用缩放控件。true是启用,false是禁用。默认值为true。
  enableZoomControls: true,
  // 用于启用或禁用距离图例。true是启用,false是禁用。默认值为true。
  enableDistanceLegend: true,
  // 用于启用或禁用指南针外环。true是启用,false是禁用。默认值为true。
  enableCompassOuterRing: true,
});

样式位置

::deep .compass {
  position: absolute;
  left: 2%;
  top: 2%;
}

/* 比例尺位置 */

:deep .distance-legend {
  position: absolute;
  right: 2%;
  bottom: 6%;
}

/* 缩放位置 */

:deep .navigation-controls {
  position: absolute;
  bottom: 10%;
  right: 2%;
}

8. 视图操作

1. Cesium禁止缩放、旋转、平移

// 如果为真,则允许用户旋转相机。如果为假,相机将锁定到当前标题。此标志仅适用于2D和3D。
viewer.value.scene.screenSpaceCameraController.enableRotate = false;
// 如果为true,则允许用户平移地图。如果为假,相机将保持锁定在当前位置。此标志仅适用于2D和Columbus视图模式。
viewer.value.scene.screenSpaceCameraController.enableTranslate = false;
// 如果为真,允许用户放大和缩小。如果为假,相机将锁定到距离椭圆体的当前距离
viewer.value.scene.screenSpaceCameraController.enableZoom = false;
// 如果为真,则允许用户倾斜相机。如果为假,相机将锁定到当前标题。这个标志只适用于3D和哥伦布视图。
viewer.value.scene.screenSpaceCameraController.enableTilt = false;

四下皆无人