cesium 使用
十年生死两茫茫,写程序,到天亮。
千行代码,Bug何处藏。
纵使上线又怎样,朝令改,夕断肠。
领导每天新想法,天天改,日日忙。
相顾无言,惟有泪千行。
每晚灯火阑珊处,夜难寐,又加班,鬓如霜。
新版本与旧版本部分使用方法有区别,这里最新版本为例,提供思路. 部分语法为 ts 如果使用 js 注意语法修改
环境
🍇vite
🍇vue3
🍇cesium
🍇ts/js
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;