抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

简介

Cesium是一个开源的JavaScript库,用于在Web浏览器中创建三维地球和地图可视化。它基于WebGL技术,可以显示全球范围内的地理数据,包括地形、建筑物、卫星图像等。这里暂不介绍基本安装、使用句法(咨询BD,GG)。下面是我在项目中遇到的一些问题,在此记录一下。

1 影像图层的覆盖范围

比如 WebMapTileServiceImageryProvider 使用WMTS服务,为了避免浏览器请求图层以外的数据瓦片 造成 http 400 的错误请求(可能会造成浏览器请求卡顿 ), 这些请求完全可以屏蔽掉!!! 可以这么干:

1
2
3
4
5
6
7
8
9
10
11
12
// (west , south , east , north , result)
const rectangle = new Cesium.Rectangle.fromDegrees(116.15525389033179, 40.05334353581606, 116.18028770403373, 40.06687705042297)
const shadedRelief = new Cesium.WebMapTileServiceImageryProvider({
url: `/cmap/geoserver/gwc/service/wmts/rest/8:hby` +
'{style}/{TileMatrixSet}/{TileMatrixSet}:{TileMatrix}/{TileRow}/{TileCol}?format=image/png',
layer: `hby`,
style: '',
format: 'image/png',
tileMatrixSetID: 'EPSG:900913',
rectangle
})
viewer.imageryLayers.addImageryProvider(shadedRelief)

2 手动执行触发帧渲染

1
2
3
4
5
6
7
8
9
// 渲染频率 66.7帧 改为 20帧 Viewer 初始化选项参数
targetFrameRate: 20, //设置1秒渲染的最大频率数
requestRenderMode: true,
maximumRenderTimeChange: Infinity

// 触发
window.$viewer.scene.requestRender();

// 注意 在Vue中使用的是window.$viewer 而不是 this.viewer 避免viewer 挂载在vue实例下面,触发vue响应式 造成页面卡顿。

3 实时轨迹优化

后端推送的Websocket实时数据, 平滑渲染(减少Entity轨迹变化时的跳跃感)
map.js中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 根据时钟判定场景播放速度
export function correctClockSpeed() {
// 根据系统时间和渲染时钟, 控制播放速度
const diffTime = Cesium.JulianDate.now().secondsOfDay - window.$viewer.clock.currentTime.secondsOfDay
if (diffTime >= 3 ) {
window.$viewer.clock.currentTime = Cesium.JulianDate.now();
} else if (diffTime >= 1 && diffTime < 3) {
window.$viewer.clock.multiplier = 1.1
} else if(diffTime >= 0.5 && diffTime < 1){
window.$viewer.clock.multiplier = 1
} else if(diffTime >= 0 && diffTime < 0.5) {
window.$viewer.clock.multiplier = 0.5
} else if (diffTime < 0) {
window.$viewer.clock.currentTime = Cesium.JulianDate.now();
}
// console.log(`${Cesium.JulianDate.now().secondsOfDay}, ${$viewer.clock.currentTime.secondsOfDay}, ${Cesium.JulianDate.now().secondsOfDay - $viewer.clock.currentTime.secondsOfDay}`, `---> ${window.$viewer.clock.multiplier}`)
}

// 添加抽样值,并设置参数。
export function setSampleProperty(property, time, value) {
property.addSample(time, value);
// 设置插值选项
property.setInterpolationOptions({
interpolationDegree: window.InsertValue || 3, // checked 3 is ok, check 5?
});
// 数据停止更新时,最后一个位置被保留
property.forwardExtrapolationType = Cesium.ExtrapolationType.HOLD;
property.forwardExtrapolationDuration = Infinity; // 保持最后一个值无限时间
}

RunningCarLayer.vue 文件中使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import { correctClockSpeed, setSampleProperty } from "@/utils/map";

// 初始化websocket
const ws = new WebSocket(that.socketUrl)
ws.onmessage = function(evt) {
correctClockSpeed();// 通过控制场景的播放速度,对齐渲染时钟和系统时钟
const jsonData = evt.data ? JSON.parse(evt.data) : ''
if (jsonData) {
that.handlerCars(jsonData)
}
}

// 在handlerCars函数中处理 伪代码
handlerCars(cars) {
cars.forEach(car => {
const position = Cesium.Cartesian3.fromDegrees(car.longitude, car.latitude, 0)
const orientation = this.getOrientation(position, car.heading)
if (this.carMap[car.id]) {
const entity = this.carMap[car.id];
setSampleProperty(entity.position, currentTime, position)
setSampleProperty(entity.orientation, currentTime, this.getOrientation(position, +car.heading))
} else {
const entity = this.$dataSource.entities.add({
id: car.id,
// 模型位置
position: new Cesium.SampledPositionProperty(),
// 模型方向
orientation: new Cesium.SampledProperty(Cesium.Quaternion),
model: {
color: Cesium.Color.fromCssColorString(realCarColor),
uri: this.carModelMap[vehicleType], // 不同类型的车模型不同
runAnimations: true,
scale: this.carModelScale[vehicleType] // 不同类型的车模型大小不同
}
})
this.carMap[car.id] = entity
}
})
}

4 使用高德影像服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 准备 npm install @dvgis/cesium-map@2.6.1 -D
// 声明 import '@dvgis/cesium-map'
const options = Object.assign({}, this.defaultOptions, this.options, {
// 设置底图
// https://blog.csdn.net/jingjing_n/article/details/127298509
imageryProvider: new Cesium.UrlTemplateImageryProvider({
url: window.GAODE_CONFIG?.URL,
minimumLevel: 1,
maximumLevel: 18,
tilingScheme: new Cesium.WebMercatorTilingScheme()
})
})

// options.showRenderLoopErrors = process.env.NODE_ENV === "development";
options.showRenderLoopErrors = false;
window.$viewer = new Cesium.Viewer(this.id, options)

// 添加高德地图并使用插件纠偏
window.$viewer.imageryLayers.addImageryProvider(new Cesium.AmapImageryProvider({
style: 'elec', // img、elec、cva
crs: 'WGS84',
// url: window.GAODE_CONFIG?.URL || '' // 指定域名纠偏
}));

5 使用Gif 和 apng

这里我们做撒点动效(不能指定billboard 宽高,UI切图多大就是多大!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// 准备工作  <script src="./js/libgif.js"></script>
// 使用
const image = new Image();
image.src = require('../assets/imgs/rolling2.gif');
const superGif = new SuperGif({ gif: image });
superGif.load(() => {
this.$alarmData.entities.add({
name: i.junctionName,
curData: i,
id: `alarmicon-${i.junctionId}`,
position: Cesium.Cartesian3.fromDegrees(Number(i.lon), Number(i.lat), 30),
billboard: {
image: new Cesium.CallbackProperty(() => {
return superGif.get_canvas().toDataURL("image/png");
}, false),
height: 55, // 这里是坑
width: 55, // 这里是坑
pixelOffset: new Cesium.Cartesian2(0, -30),
heightReference: Cesium.HeightReference.NONE,
eyeOffset: new Cesium.Cartesian3(0, 0, -30),
outlineWidth: 0,
scaleByDistance: new Cesium.NearFarScalar(600, 1, 20000, 0.5),
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(600, 200000) // 视距从600到20万米显示
}
});
})


// apng准备工作 <script src="./js/apngjs.js"></script>
utils.js
/**
* 加载apng
*/
export async function loadApng(url) {
let canvas = document.createElement('canvas')
let ctx = canvas.getContext('2d')
let blob = await loaderURL(url)
let arrayBuffer = await blobToArrayBuffer(blob)
let apng = apngjs.parseAPNG(arrayBuffer)
let player = await apng.getPlayer(ctx)
player.play()
return player;
}

async function loaderURL(url) {
function createXmlHttpRequest() {
if (window.ActiveXObject) {
return new ActiveXObject('Microsoft.XMLHTTP')
} else if (window.XMLHttpRequest) {
return new XMLHttpRequest()
}
}
return new Promise((resolve) => {
let xhr = createXmlHttpRequest()
xhr.open('get', url, true)
xhr.responseType = 'blob'
xhr.onload = function (res) {
if (this.status == 200) {
var blob = this.response
resolve(blob)
}
}
xhr.send()
})
}

async function blobToArrayBuffer(blob) {
return new Promise((resolve) => {
// Blob 转 ArrayBuffer
let reader = new FileReader()
reader.readAsArrayBuffer(blob)
reader.onload = function () {
resolve(reader.result)
}
})
}

// 使用
let player = this.apngMap[i['congestionStatus']] || await loadApng(resource)
this.apngMap[i['congestionStatus']] = player
let billboard = {
image: new Cesium.CallbackProperty((time, result) => {
return player.currentFrame.imageElement
}, false),
// pixelOffset: new Cesium.Cartesian2(0, -30),
...that.pointDefault
}
if (entity) {
entity.billboard = billboard;
} else {
entity = this.$alarmData.entities.add({
name: i.junctionName,
id: 'alarm' + i.junctionId,
position: Cesium.Cartesian3.fromDegrees(Number(i.lon), Number(i.lat), 30),
billboard
});
}

6 使用png图片作为底图

1
2
3
4
5
6
7
8
9
10
// http://localhost:8000/wuxi-test/  是一个静态图片服务器,里面包含的是 {z}/{x}/{y}.png 这种格式的图片
window.USE_IMAGERY_INTEGRATION3D_PATH = 'http://localhost:8000/wuxi-test/';

var offlineImageryProvider = new Cesium.TileMapServiceImageryProvider({
url: window.USE_IMAGERY_INTEGRATION3D_PATH,
tilingScheme : new Cesium.GeographicTilingScheme(),
});

// 将这个实例添加到Cesium的Viewer中
viewer.imageryLayers.addImageryProvider(offlineImageryProvider);

评论