可以通过czml-writer来生成CZML,这个程序维护在Github上
CZML 是一种 JSON 格式的字符串,用于描述与时间有关的动画场景,CZML 包含点、线、地标、模型、和其他的一些图形元素,并指明了这些元素如何随时间而变化。
格式 cankao
第一个packet代表了cesium场景(cesium时间轴的范围,当前时刻,倍速等信息)
之外,其他的packet都可以理解为描述某一时间范围内的entity的行为。
一个czml文件当中至少有一个packet,也即第一个描述场景的packet,
每一个id不能相同,否则只显示相同id中最后一个entity,但是对于 不同czml文件中可以使用相同id。
let czml=[
//packet1,id一定为document,否则会报错,这里定义的是整个显示场景的信息
{
"id": "document",
"clock": {
"interval": "2022-01-01T10:10:10+0800/2022-01-05T10:10:10+0800",
"currentTime": "2022-01-02T02:10:10",
"step": "SYSTEM_CLOCK_MULTIPLIER",
"range": "LOOP_STOP",
"multiplier": 5
},
"version": "1.0"
},
//packet two
{
"id":"GroundControlStation"
"position":{"cartographicDegrees":[-75.5,40.0,0.0]},
"point":{
"color":{"rgba":[0,0,255,255]},
}
},
// packet three
{
"id":"PredatorUAV",
// ...
}
]
"path":{
"show":[
{
"interval":"2012-03-15T10:00:00Z/2012-03-16T10:00:00Z",
"boolean":true
}
],
"width":1,
"material":{
"solidColor":{
"color":{
"rgba":[
0,255,0,255
]
}
}
},
"resolution":120,
// 提前量
"leadTime":[
{
"interval":"2012-03-15T10:00:00Z/2012-03-15T10:39:30.5752243210009Z",
"epoch":"2012-03-15T10:00:00Z",
"number":[
0,5903.376977238004,
5903.376977238004,0
]
},
{
"interval":"2012-03-16T08:21:36.5644517090113Z/2012-03-16T10:00:00Z",
"epoch":"2012-03-16T08:21:36.5644517090113Z",
"number":[
0,5903.435548290989,
5903.435548290989,0
]
}
],
// 后尾部
"trailTime":[
{
"interval":"2012-03-15T10:00:00Z/2012-03-15T10:39:30.5752243210009Z",
"epoch":"2012-03-15T10:00:00Z",
"number":[
0,0,
5903.376977238004,5903.376977238004
]
},
{
"interval":"2012-03-15T10:39:30.5752243210009Z/2012-03-15T12:17:53.9522015590046Z",
"epoch":"2012-03-15T10:39:30.5752243210009Z",
"number":[
0,0,
5903.376977238004,5903.376977238004
]
}
]
CZML文件加载
let czmldata = new Cesium.CzmlDataSource(id).load(czml);
//id 为 CzmlDataSource对象 id
//或者直接写做 let czmldata = Cesium.CzmlDataSource.load(czml);
let temp;
cesium.viewer.dataSources.add(czmldata).then(function (ds) {
temp = ds;
});
//或者 cesium.viewer.dataSources.add(czmldata)
- 最终czml文件最终成为了CzmlDataSource对象,被加载到viewer的datasources中。
- ds是一个CzmlDataSource对象,通过
ds.entities.getById(id)
,可以获取到czml文件当中定义的entity,此时可以方便的对entity进行一些操作,比如viewer.trackEntity = ds.entities.getById("model_id")
,又或者可以将CzmlDataSource对象保存下来,以便后续进行一些操作。 ds一定要赋值给已经定义好的对象,故先声明对象,而后进行赋值。 注意:当加载多个czml文件时,场景信息会以最后一个czml文件定义的为准。
CZML文件移除
也即CzmlDataSource对象的移除,写做:
cesium.viewer.dataSources.remove(temp,isDestroy)
temp也即刚才保存的CzmlDataSource对象,这样可以将刚才添加的场景信息、entity信息都移除掉,isDestroy是一个布尔对象,代表是否要销毁CzmlDataSource对象。如果只移除某一个entity的信息,可以写做:
let entity = temp.entities.getById("GroundControlStation");
if(entity){
temp.entities.remove(entity);
}
如果删除viewer中的全部dataSources对象,可以写做:
cesium.viewer.dataSources.removeAll(true)
true代表销毁对象
模型的加载
- 在CZML文件中想要加载模型,需要先将其他格式的模型文件转换成gltf文件,然后通过
"model": {
"show": true,
"gltf": "./111.gltf",
"minimumPixelSize": 99,
},
进行加载,minimumPixelSize表示显示的最小像素点,有了这个参数的保证,可以在缩小地球时也保证模型能够看清楚。
轨迹的显示
在场景中加载卫星一般情况下无法满足我们的使用要求,还需要卫星运动起来,并且显示其运行轨迹,在CZML文件中,一般可以用如下格式的文件来表示卫星和其轨迹的显示:
{
"id": "document",
"clock": {
"interval": "2022-03-22T16:08:00+08:00/2022-03-23T16:08:00+08:00",
"currentTime": "2022-03-22T16:08:00+08:00",
"step": "SYSTEM_CLOCK_MULTIPLIER",
"range": "LOOP_STOP",
"multiplier": 60
},
"version": "1.0"
},
{
"label": {
"outlineColor": {
"rgba": [
0,
0,
0,
255
]
},
"horizontalOrigin": "LEFT",
"text": "ppCOSMOS 2426 (717)",
"outlineWidth": 2,
"
": {
"cartesian2": [
12,
0
]
},
"fillColor": {
"rgba": [
"213",
"255",
"0",
255
]
},
"font": "11pt Lucida Console",
"show": true
},
"id": "Satellite/ppCOSMOS 2426 (717)",
"description": "Orbit of Satellite: ppCOSMOS 2426 (717)",
"position": {
"interpolationAlgorithm": "LAGRANGE",
"referenceFrame": "INERTIAL",
"interpolationDegree": 5,
// "interpolationAlgorithm": "LAGRANGE", //插值算法为LAGRANGE,还有HERMITE,GEODESIC
// "interpolationDegree": 5 //1为线性插值,2为平方插值
"epoch": "2012-04-30T12:00Z", //表示时间起点为2012-04-30T12:00:00
"cartesian": [
0.0, 1.0, 2.0, 3.0, //从起点开始,第0秒时坐标为(1,2,3)
60.0, 4.0, 5.0, 6.0, //从起点开始,第60秒时坐标为(4,5,6)
120.0, 7.0, 8.0, 9.0 //从起点开始,第120秒时坐标为(7,8,9)
]
,
"cartesian": [
"2012-04-30T12:00Z", 1.0, 2.0, 3.0, //表示当时间为2012-04-30T12:00Z,坐标为(1,2,3)
"2012-04-30T12:01Z", 4.0, 5.0, 6.0, //表示当时间为2012-04-30T12:01Z,坐标为(4,5,6)
"2012-04-30T12:02Z", 7.0, 8.0, 9.0 //表示当时间为2012-04-30T12:02Z,坐标为(7,8,9),
0,
10246472.183615023,
23315473.78200593,
-99942.77560130549,
300,
9768930.236227807,
23491323.358455077,
-1173388.669547688,
600,
9270175.988391416,
23616164.516070485,
-2244286.2659865934,
],
"epoch": "2022-03-22T16:08:00+08:00"
},
"billboard": {
"image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADJSURBVDhPnZHRDcMgEEMZjVEYpaNklIzSEfLfD4qNnXAJSFWfhO7w2Zc0Tf9QG2rXrEzSUeZLOGm47WoH95x3Hl3jEgilvDgsOQUTqsNl68ezEwn1vae6lceSEEYvvWNT/Rxc4CXQNGadho1NXoJ+9iaqc2xi2xbt23PJCDIB6TQjOC6Bho/sDy3fBQT8PrVhibU7yBFcEPaRxOoeTwbwByCOYf9VGp1BYI1BA+EeHhmfzKbBoJEQwn1yzUZtyspIQUha85MpkNIXB7GizqDEECsAAAAASUVORK5CYII=",
"scale": 1.5,
"show": true
},
"availability": "2022-03-22T16:08:00+08:00/2022-03-23T16:08:00+08:00",
"path": {
"leadTime": [
{
"interval": "2022-03-22T16:08:00+08:00/2022-03-22T17:35:38.857645+08:00",
"number": [
0,
40570.57117734112,
40570.57117734112,
0
],
"epoch": "2022-03-22T16:08:00+08:00"
},
{
"interval": "2022-03-22T17:35:38.857645+08:00/2022-03-23T04:51:49.428822+08:00",
"number": [
0,
40570.57117734112,
40570.57117734112,
0
],
"epoch": "2022-03-22T17:35:38.857645+08:00"
},
{
"interval": "2022-03-23T04:51:49.428822+08:00/2022-03-23T16:07:59.999999+08:00",
"number": [
0,
40570.57117734112,
40570.57117734112,
0
],
"epoch": "2022-03-23T04:51:49.428822+08:00"
}
],
"width": 1,
"trailTime": [
{
"interval": "2022-03-22T16:08:00+08:00/2022-03-22T17:35:38.857645+08:00",
"number": [
0,
0,
40570.57117734112,
40570.57117734112
],
"epoch": "2022-03-22T16:08:00+08:00"
},
{
"interval": "2022-03-22T17:35:38.857645+08:00/2022-03-23T04:51:49.428822+08:00",
"number": [
0,
0,
40570.57117734112,
40570.57117734112
],
"epoch": "2022-03-22T17:35:38.857645+08:00"
},
{
"interval": "2022-03-23T04:51:49.428822+08:00/2022-03-23T16:07:59.999999+08:00",
"number": [
0,
0,
40570.57117734112,
40570.57117734112
],
"epoch": "2022-03-23T04:51:49.428822+08:00"
}
],
"material": {
"solidColor": {
"color": {
"rgba": [
"213",
"255",
"0",
255
]
}
}
},
"resolution": 120,
"show": [
{
"interval": "2022-03-22T16:08:00+08:00/2022-03-23T16:08:00+08:00",
"boolean": true
}
]
}
}
czml_vue
<template>
<div class="ctx">
<div id="cesiumContainer" ref="cesiumContainer"></div>
</div>
</template>
<script setup>
import * as Cesium from "cesium";
import { onMounted, ref } from "vue";
let viewer;
const cesiumContainer = ref("cesiumContainer");
onMounted(async () => {
viewer = new Cesium.Viewer("cesiumContainer", {
// animation: false, // 是否开启动画
// timeline: false, // 是否显示时间轴
// imageryProvider: new Cesium.UrlTemplateImageryProvider({
// url: "https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
// }),
});
let { czml_team } = getdata();
let entityB2, onTickEvent;
let cdata = await Cesium.CzmlDataSource.load(czml_team);
// 加载数据
viewer.dataSources.add(cdata);
entityB2 = cdata.entities.getById("flying_follow_team");
viewer.clock.shouldAnimate = true;
viewer.trackedEntity = entityB2;
// 获取模型对象
// 获取当前模型方向和位置
const orientation = entityB2.orientation;
const position = entityB2.position;
console.log(position, orientation);
console.log(entityB2);
entityB2.model.runAnimations = true;
console.log(entityB2.model);
(entityB2.model.silhouetteColor = getColor("Red", 1.0)), //> 模型轮廓颜色
(entityB2.model.silhouetteSize = parseFloat(2));
// 实时调整位置
// entityB2.orientation = new Cesium.VelocityOrientationProperty(entityB2.position);
/*
// 添加属性:速度向量
entityB2.velocityVector = new Cesium.VelocityVectorProperty(entityB2.position, true);
// 当前时刻速度向量、位置
let curVelocityVector = entityB2.velocityVector.getValue(viewer.clock.currentTime, new Cesium.Cartesian3());
let curPosition = entityB2.position.getValue(viewer.clock.currentTime, new Cesium.Cartesian3());
// 计算朝向四元数
var quaternion = getQuaternion(curPosition, curVelocityVector);
// 设置实体朝向,验证是否指向速度矢量方向
entityB2.orientation = quaternion;
*/
function adjust() {
if (viewer.clock.shouldAnimate === true) {
let ori = orientation.getValue(viewer.clock.currentTime); // 获取偏向角
let center = position.getValue(viewer.clock.currentTime); // 获取位置
console.log(ori, center);
/*
// 1、由四元数计算三维旋转矩阵
var mtx3 = Cesium.Matrix3.fromQuaternion(ori);
// 2、计算四维转换矩阵:
var mtx4 = Cesium.Matrix4.fromRotationTranslation(mtx3, center);
// 3、计算角度:
var hpr = Cesium.Transforms.fixedFrameToHeadingPitchRoll(mtx4);
// 获取角度(弧度)
const headingTemp = hpr.heading;
const pitchTemp = hpr.pitch;
// 调整角度为第一人称
const heading = Cesium.Math.toRadians(
Cesium.Math.toDegrees(headingTemp) + 90
);
const pitch = Cesium.Math.toRadians(
Cesium.Math.toDegrees(pitchTemp) - 12
);
// 视角高度,根据模型大小调整
const range = 140.0;
// 动态改变模型视角
viewer.camera.lookAt(
center,
new Cesium.HeadingPitchRange(heading, pitch, range)
);
*/
entityB2.velocityVector = new Cesium.VelocityVectorProperty(
entityB2.position,
true
);
// 当前时刻速度向量、位置
let curVelocityVector = entityB2.velocityVector.getValue(
viewer.clock.currentTime,
new Cesium.Cartesian3()
);
let curPosition = entityB2.position.getValue(
viewer.clock.currentTime,
new Cesium.Cartesian3()
);
// 计算朝向四元数
var quaternion = getQuaternion(curPosition, curVelocityVector);
// 设置实体朝向,验证是否指向速度矢量方向
entityB2.orientation = quaternion;
// entityB2.orientation = new Cesium.VelocityOrientationProperty(
// entityB2.position
// );
}
}
onTickEvent = viewer.clock.onTick.addEventListener(adjust);
});
/**
* 计算朝向四元数
* X轴正向指向运动方向;Y轴在水平面内垂直于X轴,正向指向右侧;Z轴通过右手法则确定
* @param {Cartesian3} position 位置
* @param {Cartesian3} velocity 速度向量
* @param {*} rotateX 绕X轴旋转的角度(roll)
* @param {*} rotateY 绕Y轴旋转的角度(pitch)
* @param {*} rotateZ 绕Z轴旋转的角度(heading)
* @returns
*/
function getQuaternion(position, velocity, rotateX, rotateY, rotateZ) {
// 1、计算站心到模型坐标系的旋转平移矩阵
// 速度归一化
let normal = Cesium.Cartesian3.normalize(velocity, new Cesium.Cartesian3());
// 计算模型坐标系的旋转矩阵
let satRotationMatrix = Cesium.Transforms.rotationMatrixFromPositionVelocity(
position,
normal,
Cesium.Ellipsoid.WGS84
);
// 模型坐标系到地固坐标系旋转平移矩阵
let m = Cesium.Matrix4.fromRotationTranslation(satRotationMatrix, position);
// 站心坐标系(东北天坐标系)到地固坐标系旋转平移矩阵
var m1 = Cesium.Transforms.eastNorthUpToFixedFrame(
position,
Cesium.Ellipsoid.WGS84,
new Cesium.Matrix4()
);
// 站心到模型坐标系的旋转平移矩阵
let m3 = Cesium.Matrix4.multiply(
Cesium.Matrix4.inverse(m1, new Cesium.Matrix4()),
m,
new Cesium.Matrix4()
);
// 2、模型姿态旋转矩阵
rotateX = rotateX || 0;
rotateY = rotateY || 0;
rotateZ = rotateZ || -90;
let heading = rotateZ,
pitch = rotateY,
roll = rotateX;
let postureHpr = new Cesium.HeadingPitchRoll(
Cesium.Math.toRadians(heading),
Cesium.Math.toRadians(pitch),
Cesium.Math.toRadians(roll)
);
let postureMatrix = Cesium.Matrix3.fromHeadingPitchRoll(postureHpr);
// 3、最终的旋转矩阵
let mat3 = Cesium.Matrix4.getMatrix3(m3, new Cesium.Matrix3());
let finalMatrix = Cesium.Matrix3.multiply(
mat3,
postureMatrix,
new Cesium.Matrix3()
);
let quaternion1 = Cesium.Quaternion.fromRotationMatrix(finalMatrix);
let hpr = Cesium.HeadingPitchRoll.fromQuaternion(quaternion1);
let q2 = Cesium.Transforms.headingPitchRollQuaternion(position, hpr);
return q2;
}
function getdata() {
const czml_team = [
{
id: "document",
name: "flying_follow_team",
version: "1.0",
clock: {
interval: "2023-03-08T10:00:00Z/2023-03-08T12:00:00Z",
currentTime: "2023-03-08T10:00:00Z",
multiplier: 10,
},
},
{
id: "flying_follow_team",
name: "path with GPS flight data",
description: "测试第一人称视角。",
// 可用性
availability: "2023-03-08T10:00:00Z/2023-03-08T12:00:00Z",
path: {
material: {
polylineGlow: {
color: {
rgba: [0, 0, 255, 200],
},
glowPower: 0.1,
taperPower: 0.1,
},
},
width: 20,
// 向前路线
leadTime: 10,
trailTime: 1000,
resolution: 0.5,
show: true,
},
model: {
// 模型参数
gltf: "/b22.glb",
minimumPixelSize: 1000,
maximumScale: 20,
},
orientation: {
// 自动计算方向
velocityReference: "#position",
},
position: {
// 插值算法
interpolationAlgorithm: "LAGRANGE",
interpolationDegree: 5,
epoch: "2023-03-08T10:00:00Z",
// 坐标组
cartographicDegrees: [
0, 118.93830177292894, 25.488280583435404, 0,
300, 119.14034602637892, 25.32388938213355, 2000,
800, 119.43064375816327, 25.230148210056235, 5000, 1500,
120.93105921868252, 24.769194048014963, 12000, 2500,
121.5592902752412, 24.658964292017885, 12000, 3500,
121.56445881860067, 25.16649023047563, 5000,
4500, 119.94263373897657, 25.49632056739945, 12000,
5400, 119.30910179629008, 25.559938450361965, 5000,
6300, 118.96295053426707, 25.571485127594467, 0,
7200, 114, 26, 300,
],
},
// orientation : new Cesium.VelocityOrientationProperty(position),
},
];
return {
czml_team,
};
}
//-----------------------下面是获取颜色方法------------------
//> 获取模型颜色与透明度
function getColor(colorName, alpha) {
const color = Cesium.Color[colorName.toUpperCase()];
return Cesium.Color.fromAlpha(color, parseFloat(alpha));
}
//> 获取目标颜色和图元的源颜色之间混合的不同模式 HIGHLIGHT将源颜色乘以目标颜色 REPLACE将源颜色替换为目标颜色 MIX将源颜色和目标颜色混合在一起
function getColorBlendMode(colorBlendMode) {
return Cesium.ColorBlendMode[colorBlendMode.toUpperCase()];
}
</script>
<style scoped>
.ctx {
box-sizing: border-box;
width: 100%;
height: 100%;
}
</style>
<!--
let geo = await Cesium.GeoJsonDataSource.load(
"/src/assets/hbeiprovince.json",
{
// fill: Cesium.Color.PINK, //填充色
stroke: Cesium.Color.HOTPINK, //轮廓颜色
strokeWidth: 5, //轮廓宽度
}
);
geo.entities.values.forEach((enetity) => {
enetity.polygon.outlineColor = Cesium.Color.RED;
enetity.polygon.material = Cesium.Color.BLUE;
enetity.polygon.height = 1000;
enetity.polygon.extrudedHeight = 2000;
});
viewer.dataSources.add(geo);
let { czml_team } = getdata();
-->