目录
- 1 切换二维地图
- 2 删除默认图层
- 3 隐藏版权信息
- 4 加载cesiumlab切片影像出现栅格阴影
- 5 解决相机控制问题
- 6 cesium中限制地图浏览范围
- 7 鼠标移动显示经纬度
- 8 禁用cesium选取实体操作
- 8.1 禁用操作
- 8.2 双击事件改写
- 8.3 信息隐藏(index.html页面)
- 9 自定义动画
- 10 小车轨迹切分
- 11 label跟随模型
- 12 自定义label样式
- 13 轨迹输出坐标点
- 14 识别实体模型
- 14.1 识别3D Titles模型
- 14.2 识别一般实体
- 15 修改3DTitles高度
- 16 解决影像拼接黑色锯齿
- 17 去除cesium默认功能
- 18 vite全局整合cesium
- 19 相机定位问题
我将对我在最近与数字孪生项目的对接过程中所实现的一些功能进行总结。这些功能主要涉及到地理信息系统方面的Cesium详细功能设计。具体来说,我在这个项目中实现了一些功能,包括对接不同地图平台和引擎,实现地图数据可视化和交互式控制,以及在Cesium中添加和操作各种地图元素等。
1 切换二维地图
viewer = new Cesium.Viewer('cesiumContainer', {
sceneMode: Cesium.SceneMode.SCENE2D,//切换2D
})
2 删除默认图层
viewer.imageryLayers.removeAll()
3 隐藏版权信息
viewer._cesiumWidget._creditContainer.style.display = "none"
4 加载cesiumlab切片影像出现栅格阴影
原因:未设置切片影像加载区域
// 矩形区域
bestRect= Cesium.Rectangle.fromRadians(
2.086396706185367,
0.6039399681488924,
2.086691132076495,
0.6042171535375767)
const xinWeiImagery = new Cesium.UrlTemplateImageryProvider({
url: '',
rectangle:bestRect
})
5 解决相机控制问题
// 如果为真,则允许用户旋转相机。如果为假,相机将锁定到当前标题。此标志仅适用于2D和3D。
scene.screenSpaceCameraController.enableRotate = false;
// 如果为true,则允许用户平移地图。如果为假,相机将保持锁定在当前位置。此标志仅适用于2D和Columbus视图模式。
scene.screenSpaceCameraController.enableTranslate = false;
// 如果为真,允许用户放大和缩小。如果为假,相机将锁定到距离椭圆体的当前距离
scene.screenSpaceCameraController.enableZoom = false;
// 如果为真,则允许用户倾斜相机。如果为假,相机将锁定到当前标题。这个标志只适用于3D和哥伦布视图。
scene.screenSpaceCameraController.enableTilt = false;
viewer.scene.screenSpaceCameraController.minimumZoomDistance = 80//相机的高度的最小值
viewer.scene.screenSpaceCameraController.maximumZoomDistance = 40000 //相机高度的最大值
// viewer.scene.screenSpaceCameraController._minimumZoomRate = 100 // 设置相机缩小时的速率
// viewer.scene.screenSpaceCameraController._maximumZoomRate = 12000 //设置相机放大时的速率
6 cesium中限制地图浏览范围
概述:鼠标只能在指定地点移动,出界则返回
// 控制鼠标左键移动问题
let setCameraMove = () => {
viewer.scene.preRender.addEventListener(() => {
let rectangle = viewer.camera.computeViewRectangle();
//设置可浏览经纬度范围
let maxRange = { west: 118.30112, north: 35.21500, east: 120.78832, south: 34.26431 };
//地理坐标(弧度)转经纬度坐标
// 弧度转为经纬度,west为左(西)侧边界的经度,以下类推
let west = Cesium.Math.toDegrees(rectangle.west).toFixed(5)
let south = Cesium.Math.toDegrees(rectangle.south).toFixed(5)
let east = Cesium.Math.toDegrees(rectangle.east).toFixed(5)
let north = Cesium.Math.toDegrees(rectangle.north).toFixed(5)
// 119.13933 34.45136 120.02731 34.79189
// console.log(rectangle);
// console.log(west,south,east,north);
//如果视角超出设置范围则跳转视角
if (west < maxRange.west || south < maxRange.south || east > maxRange.east || north > maxRange.north) {
setCameraPos()
}
})
}
参考文章:cesium中限制地图浏览范围
7 鼠标移动显示经纬度
概述:悬浮框跟随屏幕坐标,将屏幕坐标转为经纬度展示
const showInfoDom = document.querySelector('.show-info')
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
// 鼠标移动显示经纬度
handler.setInputAction((target) => {
// console.log(viewer.scene.camera.heading, viewer.scene.camera.pitch, viewer.scene.camera.roll);
// 鼠标移动终点
const position = target.endPosition
// 椭球面
const ellipsoid = viewer.scene.globe.ellipsoid
const cartesian = viewer.camera.pickEllipsoid(position, ellipsoid)
// 只选取地球表面
if (cartesian && isShowMouseMoveInfo.value) {
// 空间笛卡尔转换为弧度
const cartographic = Cesium.Cartographic.fromCartesian(cartesian)
// 弧度转为经纬度
const lon = Cesium.Math.toDegrees(cartographic.longitude).toFixed(5)
const lat = Cesium.Math.toDegrees(cartographic.latitude).toFixed(5)
const height = cartographic.height;
// console.log(`经度为:${lon},纬度为:${lat},高度为:${height}`);
showInfoDom.innerHTML = `经度为:${lon},<br>纬度为:${lat}`
showInfoDom.style.display = 'block'
showInfoDom.style.top = `${position.y}px`
showInfoDom.style.left = `${position.x + 30}px`
} else {
showInfoDom.style.display = 'none'
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
8 禁用cesium选取实体操作
概述:当我们鼠标双击实体时,cesium默认相机定位并在右侧显示实体的名称
8.1 禁用操作
viewer = new Cesium.Viewer('cesiumContainer', {
selectionIndicator: false,//双击选中实体
sceneModePicker: false,
})
8.2 双击事件改写
handler.setInputAction(() => {
viewer.trackedEntity = undefined
}, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK)
8.3 信息隐藏(index.html页面)
<style>
/* 禁止双击事件弹出框 */
.cesium-viewer-infoBoxContainer{
display: none;
/* background-color: rgb(133, 207, 236); */
}
</style>
9 自定义动画
概述:一般可以用cesium官方的时钟搞,这个我试了一下定时器的效果
// 添加小车模型
let car7
const staticCar = carStore.staticCar
const dynamicCar = carStore.dynamicCar
let loadCarTrail = () => {
// 加载静态小车
for (let i = 0; i < staticCar.length; i++){
viewer.entities.add({
name: staticCar[i].name,
position: Cesium.Cartesian3.fromDegrees(staticCar[i].position.lon,staticCar[i].position.lat,staticCar[i].position.height),
model: {
uri: 'model/car/CesiumMilkTruck.glb'
},
label: {
text: staticCar[i].name,
font: "20px sans-serif",
// showBackground: true,
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
0,
500
),
eyeOffset: new Cesium.Cartesian3(0, 3.5, 0),
}
})
}
// 加载动态小车
car7= viewer.entities.add({
name: dynamicCar.name,
position: Cesium.Cartesian3.fromDegrees(dynamicCar.position.lon, dynamicCar.position.lat, dynamicCar.position.height),
model: {
uri: 'model/car/CesiumMilkTruck.glb',
scale:2
},
label:{
text: '装载机',
font: "15px sans-serif",
// showBackground: true,
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
0,
500
),
eyeOffset: new Cesium.Cartesian3(0, 6.6, 0),
}
})
}
// 车辆开始作业
let timer,i=0
let startDynamicCar = () => {
// 规律:经度增加10,纬度减小2
// 经度偏移量0.0005
setdynamicCarCamera()
if (!timer) {
console.log('开始作业!!!');
let car7Pos = car7.position._value
let temp
if (!timer) {
timer = setInterval(() => {
temp = Cesium.Cartesian3.fromDegrees(dynamicCar.trail[i].lon, dynamicCar.trail[i].lat,0)
car7Pos.x = temp.x
car7Pos.y = temp.y
car7Pos.z = temp.z
i++
if (i >= dynamicCar.trail.length) {
i = 0
}
}, 100);
}
}
}
// 车辆结束作业
let endDynamicCar = () => {
console.log('结束作业!!!');
clearInterval(timer)
timer = null
}
10 小车轨迹切分
概述:比如给定任意三个坐标,将相邻的两个坐标中间的点进行切分,这样动画会流畅,下面的代码还没有写完,但思路差不多
// 轨迹切分
let sliceCarTrail = () => {
const sliceArr = [
{ lon: 119.54520, lat: 34.61526, height: 0 },
{ lon: 119.54750, lat: 34.61496, height: 0 },
{ lon: 119.54980, lat: 34.61462, height: 0 }
]
const startPos = { lon: 119.54520, lat: 34.61525, height: 0 }
const endPos = { lon: 119.54990, lat: 34.61466, height: 0 }
const lonNum = 0.00010
const latNum = 0.00002
const num=0.00005
for (let i = 0; i < sliceArr.length - 1; i++){
// 计算经纬度切片数量
const k1 = Math.abs(Math.round((sliceArr[i + 1].lon - sliceArr[i].lon) / lonNum))
const k2 = Math.abs(Math.round((sliceArr[i + 1].lat - sliceArr[i].lat) / latNum))
const k = Math.max(k1, k2)
for (let j = 0; j < k; j++){
const middlePos = { lon: parseFloat((sliceArr[i].lon + j * lonNum).toFixed(5)), lat: parseFloat((sliceArr[i].lat + j * lonNum).toFixed(5)), height: 0 }
dynamicCar.trail.push(middlePos)
}
}
dynamicCar.trail.push(sliceArr[sliceArr.length - 1])
console.log(dynamicCar.trail);
}
11 label跟随模型
概述:可以在添加实体模型同时添加label实体
viewer.entities.add({
name: loaderCar[i].name,
position: Cesium.Cartesian3.fromDegrees(loaderCar[i].position.lon, loaderCar[i].position.lat, loaderCar[i].position.height),
model: {
uri: 'model/car/CesiumMilkTruck.glb'
},
label: {
text: new Cesium.CallbackProperty((result) => {
let carInfo =`车辆名称:${loaderCar[i].name}\n车辆类型:${loaderCar[i].type}\n工作信息:${loaderCar[i].jobInfo}\n其他信息:${loaderCar[i].other}`
return carInfo
}, false),
font: labelStore.font,
showBackground: labelStore.showBackground,
backgroundColor: labelStore.backgroundColor,
outlineColor: labelStore.outlineColor,
horizontalOrigin: labelStore.horizontalOrigin,
verticalOrigin: labelStore.verticalOrigin,
// pixelOffset: new Cesium.Cartesian2(0,0),
disableDepthTestDistance: labelStore.disableDepthTestDistance,//被建筑物遮挡问题
scaleByDistance: labelStore.scaleByDistance,
distanceDisplayCondition: labelStore.distanceDisplayCondition
}
})
12 自定义label样式
概述:一般来说,label实体内置了很多属性,但是样式比较单一,如果想自定义样式可以构造组件
在Cesium中,Label实体的配置项包括:
text:标签中显示的文本内容。
font:标签的字体样式。
pixelOffset:标签相对于实体位置的偏移量。
showBackground:是否显示标签的背景。
backgroundColor:标签背景的颜色。
backgroundPadding:标签背景的内边距。
fillColor:标签文本的填充颜色。
outlineColor:标签文本的轮廓线颜色。
outlineWidth:标签文本的轮廓线宽度。
scale:标签的缩放比例。
verticalOrigin:标签垂直方向的定位方式。
horizontalOrigin:标签水平方向的定位方式。
translucencyByDistance:标签的透明度随距离变化的效果。
pixelOffsetScaleByDistance:标签的偏移量随距离变化的效果。
distanceDisplayCondition:标签的显示距离条件。
这些配置项可以用于创建和修改Cesium中的Label实体。
参考链接:cesium自定义label标签
13 轨迹输出坐标点
概述:需求按照一定的时间间隔逐步输出打印的点,这里有两种方法
- 先把点实体全部加载到场景(设置为隐藏),通过定时器逐步显示
- 添加点前延时1s添加,这里用的是第二种
// 开始绘制小车打点轨迹
const points = trailCar[0].points
let drawPointTimer=null
let drawPointTrail = async () => {
// viewer.camera.
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(119.54876, 34.61479,300), // 设置位置,北京的坐标
orientation: {
heading: 0,
pitch: Cesium.Math.toRadians(-90.0),
roll: 0
}
})
console.log('开始绘制轨迹!!!');
isShowPointTrail.value=true
for (let i = 0; i < points.length; i++) {
await new Promise(resolve => setTimeout(resolve, 1000)); // 设置延迟,观察效果
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(points[i].position.lon, points[i].position.lat, points[i].position.height),
point: {
color: Cesium.Color.RED,
outlineColor: Cesium.Color.PINK,
outlineWidth: 5,
pixelSize: 20,
// heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,逐步添加有问题注释掉
scaleByDistance: new Cesium.NearFarScalar(100, 1, 1000, 0.05)
},
label: {
text: new Cesium.CallbackProperty((result) => {
let carInfo = `${points[i].time}`
return carInfo
}, false),
showBackground: true,
font: "12px monospace",
horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
// pixelOffset: new Cesium.Cartesian2(0,0),
disableDepthTestDistance: Number.POSITIVE_INFINITY,//被建筑物遮挡问题
scaleByDistance: new Cesium.NearFarScalar(100, 2, 500, 0.1)
}
});
}
}
14 识别实体模型
概述:需要鼠标点击或移动经过实体的时候,识别出来实体并显示信息
14.1 识别3D Titles模型
// 定义一个点击事件
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
handler.setInputAction(function(movement) {
const feature = viewer.scene.pick(movement.position);
if (feature instanceof Cesium.Cesium3DTileFeature) {
feature.color = Cesium.Color.RED; // 将拾取到的3D tiles颜色修改为红色
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
14.2 识别一般实体
// 鼠标左键弹出选择框
handler.setInputAction((target) => {
const position= target.position
// 返回拾取顶端的具有primitive属性的一个对象
const feature = viewer.scene.pick(position)
if (feature && feature.id._id === 'test001' && !isShowPointTrail.value) {
ElMessageBox.confirm(
'是否显示小车近十分钟轨迹?',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
}
)
.then(() => {
drawPointTrail()
ElMessage({
type: 'success',
message: '成功显示',
})
})
.catch(() => {
ElMessage({
type: 'info',
message: '取消显示',
})
})
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
15 修改3DTitles高度
// 接下来解决模型移动缩放的问题
let changeModel = (tileset, height) => {
// 显示3D Tiles包围盒
// tileset.debugShowContentBoundingVolume = true
const cartographic = Cesium.Cartographic.fromCartesian(
tileset.boundingSphere.center
);
const surface = Cesium.Cartesian3.fromRadians(
cartographic.longitude,
cartographic.latitude,
0.0
);
const offset = Cesium.Cartesian3.fromRadians(
cartographic.longitude,
cartographic.latitude,
height
);
const translation = Cesium.Cartesian3.subtract(
offset,
surface,
new Cesium.Cartesian3()
);
tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
}
16 解决影像拼接黑色锯齿
概述:在大疆智图导出的2D影像,进行多个拼接时,边缘部分出现明显的锯齿痕迹。但是后期通过抗锯齿化没有办法解决,这里可以利用加载影像的最大范围缩小。
let loadImageryLayer = () => {
const slice_total = new Cesium.UrlTemplateImageryProvider({
url: 'http://localhost:9003/image/wmts/21Q3Ey3F/{z}/{x}/{y}',
rectangle: Cesium.Rectangle.fromDegrees(119.21356, 34.60827, 119.21464, 34.60928),
})
viewer.imageryLayers.addImageryProvider(slice_total)
const slice_cehui = new Cesium.UrlTemplateImageryProvider({
url: 'http://localhost:9003/image/wmts/mC7YGixZ/{z}/{x}/{y}',
rectangle: Cesium.Rectangle.fromDegrees(119.21362, 34.60876, 119.21451, 34.6090),
})
viewer.imageryLayers.addImageryProvider(slice_cehui)
const slice_wensi = new Cesium.UrlTemplateImageryProvider({
url: 'http://localhost:9003/image/wmts/v46lxY3m/{z}/{x}/{y}',
rectangle: Cesium.Rectangle.fromDegrees(119.21384, 34.60534, 119.21464, 34.60866),
})
viewer.imageryLayers.addImageryProvider(slice_wensi)
const slice_cehui_right = new Cesium.UrlTemplateImageryProvider({
url: 'http://localhost:9003/image/wmts/QvyCMwx1/{z}/{x}/{y}',
// 设置纹理过滤选项
rectangle: Cesium.Rectangle.fromDegrees(119.2144, 34.609, 119.21449, 34.60909)
})
viewer.imageryLayers.addImageryProvider(slice_cehui_right)
}
17 去除cesium默认功能
概述:一般来说,部署的时候cesium很多的功能需要去掉,这里我们可以直接选择CesiumWidget或者添加配置选项。
viewer = new Cesium.Viewer('cesiumContainer', {
geocoder: false, // 隐藏查找位置
homeButton: false, // 隐藏返回视角到初始位置
sceneModePicker: false, // 隐藏视角模式的选择
baseLayerPicker: false, // 隐藏图层选择器
navigationHelpButton: false, // 隐藏帮助
animation: false, // 隐藏动画速度控制器
timeline: false, // 隐藏时间轴
fullscreenButton: false, // 隐藏全屏按钮
shouldAnimate: true
})
18 vite全局整合cesium
// 创建vue项目
npm create vite
//导入cesium和依赖插件
地址:https://github.com/nshen/vite-plugin-cesium
19 相机定位问题
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(119.54876, 34.61479, 300),
orientation: {
heading: 0,
pitch: Cesium.Math.toRadians(-90.0),
roll: 0
}
})