目录
2.4 采样的属性值(InterpolatableProperty)
1. czml介绍
CZML是一种JSON格式,用于描述时间动态图形场景,主要用于在运行Cesium的Web浏览器中显示。它描述线条、点、广告牌、模型和其他图形基元,并指定它们如何随时间变化。虽然Cesium具有丰富的客户端API,但CZML允许它是数据驱动的,因此通用的Cesium查看器可以显示丰富的场景,而无需任何自定义代码。在许多方面,Cesium和CZML之间的关系类似于谷歌地球和KML之间的关系。CZML 和 KML 都是用于描述其各自客户端中的场景的数据格式,旨在由各种应用程序生成,甚至可能由手写。两者都意味着与客户端完全无关,以便其他兼容的客户端可以渲染其中描述的场景。CZML具有许多重要特征,其中一些特征将其与KML区分开来:
- CZML基于JSON。
- CZML可以准确地描述随时间变化的属性。例如,一条线在一个时间间隔内可以是红色的,在另一个时间间隔内可以是蓝色的。客户还应该能够对带时间标记的样本进行插值。如果指定了两次车辆的位置,则客户端可以使用CZML指定的插值算法准确地显示车辆在这两次之间的位置。每个属性都是时间动态的。
- CZML 的结构是用于向客户端进行高效、增量的流式处理。在显示场景之前,整个文档不需要存在于客户端上。在许多情况下,单个客户端甚至可能在流进行时加入和离开流。
- CZML针对客户端消费进行了优化;它旨在紧凑且易于解析。它也是人类合理可读和可写的。
- CZML 是可扩展的。虽然CZML的主要目标是将场景传达给类似虚拟地球仪的客户端,但该格式可以轻松扩展,以将其他静态或时间动态数据传达给更复杂的客户端。例如,时间动态数据可以显示在 2D 图表上。
- CZML是一种开放格式。我们希望尽可能多的项目能够利用它,并希望有朝一日能与OGC等标准机构正式合作。
- 用于编写CZML的开源库czml-writer在Github上维护。
总结: CZML 可以理解为 Cesium Language 的简写,是cesium中很重要的一个概念,使得cesium很酷很炫地展示动态数据成为可能。
2. czml详解
2.1 数据结构
CZML 是 JSON 的子集,这意味着有效的 CZML 文档也是有效的 JSON 文档。具体而言,CZML 文档包含单个 JSON 数组,其中数组中的每个对象文本元素都是一个 CZML 数据包。CZML 数据包描述场景中单个对象(如单个飞机)的图形属性。
注意:我们在这些示例中使用javascript注释,即使JSON在技术上不允许使用注释。
[
// packet one
{
"id": "GroundControlStation"
"position": { "cartographicDegrees": [-75.5, 40.0, 0.0] },
"point": {
"color": { "rgba": [0, 0, 255, 255] },
}
},
// packet two
{
"id": "PredatorUAV",
// ...
}
]
每个数据包都有一个属性来标识它所描述的对象。ID 不需要是 GUID,但它们确实需要唯一标识 CZML 源中的单个对象以及加载到同一作用域中的任何其他 CZML 源。id
如果未指定 ,客户端将自动生成唯一的。但是,这会阻止以后的数据包引用此对象,以便例如向其添加更多数据。id
除此之外,数据包还包含零个或多个(但通常是一个或多个)属性,用于定义要绘制的与对象相关的图形项。在上面的示例中,我们指定“GroundControlStation”对象在 WGS 84 经度 -75.5 度、纬度 40.0 度和高度 0.0 米处具有固定位置,并在其位置绘制一个蓝点。id
为 CZML 定义了许多标准属性,包括用于向场景添加点、广告牌、模型、线条和其他图形的属性。“数据包”页以及子属性和子类型的链接页详细介绍了可用属性。在此页面上,我们主要关注数据的结构。例如,我们描述了如何指定一个属性,使其在两个不同的时间间隔内具有两个不同的值。
2.2 间隔(interval)
在最一般的情况下,CZML 属性的值是一个 JSON 数组,其中数组中的每个元素都是一个对象文本,用于定义不同时间间隔的属性值。数组中任何给定对象文本描述的间隔使用属性中的 ISO8601 间隔字符串指定。interval
{
"id": "myObject",
"someProperty": [
{
"interval": "2012-04-30T12:00:00Z/13:00:00Z",
"number": 5
},
{
"interval": "2012-04-30T13:00:00Z/14:00:00Z",
"number": 6
},
]
}
在这里,我们按两个时间间隔定义属性,第一个间隔从中午到下午 1:00 UTC,其中属性的值为 5,另一个间隔从下午 1:00 到下午 2:00 UTC,其中属性的值为 6。当跨越两个间隔之间的边界时,该值将立即更改。我们用于指示值,因为这是一个数字类型的属性。某些属性(尤其是表示位置的属性)允许以多种格式指定值,例如笛卡尔 X、Y、Z 位置或制图经度、纬度、高度位置。每种类型的页面列出了每个属性支持的数据类型,以及用于每种属性的值名称。someProperty
number
该属性是可选的。如果未指定,则假定间隔跨越所有时间。指定多个无限间隔或一般重叠的间隔没有多大意义,但如果这样做,则 CZML 文件或流中后面的间隔优先。interval
在属性的值仅超过一个间隔的常见情况下,可以完全省略间隔列表数组。
{
"id": "myObject",
"someProperty": {
"interval": "2012-04-30T12:00:00Z/14:00:00Z",
"number": 5
}
}
就像以前一样,如果该属性跨越所有时间,则可以省略该属性。对于具有简单值的属性,如上面显示的数字属性,并且始终具有单个值,可以更紧凑地给出该值:interval
{
"id": "myObject",
"someProperty": 5
}
此缩写表示法对于其值可以用简单 JSON 数据类型之一表示的任何属性都有效:字符串、数字或布尔值。
2.3 复合值 (ComplexProperty)
更复杂的复合值(如笛卡尔位置或颜色)使用 JSON 数组表示。对于笛卡尔位置,数组有三个元素,分别对应于位置的 X、Y 和 Z 分量。
{
"id": "myObject",
"someComplexProperty": {
"cartesian": [1.0, 2.0, 3.0]
}
}
必须始终在某个间隔内指定复合值,即使该间隔是无限的,如此处所示。如果允许将 值 , 作为直接的值,则解释 CZML 的客户端需要查看数组的内容,以确定数组是间隔列表还是单个值。因此,为简单起见,我们不允许这样做。[1.0, 2.0, 3.0]
complexProperty
2.4 采样的属性值(
InterpolatableProperty)
到目前为止,我们已经讨论了如何始终为属性指定单个值,以及如何在不同的离散间隔内为属性指定不同的值。某些属性还允许您指定时间标记的样本,客户端将对这些样本进行插值以计算任何给定时间的属性值。时间使用 ISO8601 字符串指定。
{
// ...
"someInterpolatableProperty": {
"cartesian": [
"2012-04-30T12:00Z", 1.0, 2.0, 3.0,
"2012-04-30T12:01Z", 4.0, 5.0, 6.0,
"2012-04-30T12:02Z", 7.0, 8.0, 9.0
]
}
}
在这里,我们指定该值在中午,一分钟后,一分钟后。如果客户端的当前时钟是中午 30 秒后,则该值将是 和 之间的线性插值,或者 。[1.0, 2.0, 3.0]
[4.0, 5.0, 6.0]
[7.0, 8.0, 9.0]
[1.0, 2.0, 3.0]
[4.0, 5.0, 6.0]
[2.5, 3.5, 4.5]
为简洁起见,时间也可以以自纪元以来的秒为单位指定。虽然这可能不如使用 ISO8601 字符串指定每次精确,但当样本跨度小于一天或偏移量为整数秒时,这通常就足够了。
{
// ...
"someInterpolatableProperty": {
"epoch": "2012-04-30T12:00Z",
"cartesian": [
0.0, 1.0, 2.0, 3.0,
60.0, 4.0, 5.0, 6.0,
120.0, 7.0, 8.0, 9.0
]
}
}
最后,使用时间标记样本指定的属性具有一些额外的可选子属性来控制插值。
{
// ...
"someInterpolatableProperty": {
"epoch": "2012-04-30T12:00Z",
"cartesian": [
0.0, 1.0, 2.0, 3.0,
60.0, 4.0, 5.0, 6.0,
120.0, 7.0, 8.0, 9.0
],
"interpolationAlgorithm": "LAGRANGE",
"interpolationDegree": 5
},
}
指定用于在与提供的数据不同的时间插值值的算法。有关可能的值,请参阅下表。该属性指定用于插值的多项式的次数。如果未指定这些属性,则使用线性插值。有关与插值相关的属性的完整列表,请参阅可插值属性。interpolationAlgorithm
interpolationDegree
不必使每个样本的时间都落在包含它的区间内,但样本不会在其区间之外使用。这对于通过更高程度的插值提供更好的精度非常有用。
2.5 事件源和流式处理(EventSource)
将整个 CZML 文档放在一个大 JSON 数组中会使文档难以增量加载。今天的Web浏览器允许在流完成之前对其进行一些访问,但是解析和解释不完整的数据需要缓慢而繁琐的字符串操作。为了促进高性能的流式传输,CZML也可以使用现代浏览器的服务器发送事件()API进行流式传输。使用此 API 时,每个 CZML 数据包都作为单独的事件流式传输到客户端:EventSource
event: czml
data: {
// packet one
}
event: czml
data: {
// packet two
}
因此,浏览器在收到每个数据包时会引发一个事件,仅包含该数据包的数据。这使我们能够以优异的性能增量处理CZML数据。
到目前为止,我们可能已经暗示使用单个数据包来表示单个对象,该数据包描述了与该对象关联的所有图形。但事实并非如此。一个CZML流或文档可以包含多个相同的包,描述同一对象的不同方面。id
实际上,在某些情况下,两个数据包甚至可以描述对象的相同属性。当属性是在多个时间间隔内定义的,或者当一个间隔包含许多时间标记的样本时,这很有用。通过将属性的完整定义分解为多个数据包,我们可以更快地将相关数据放入铯中,以最大限度地减少用户在Cesium开始渲染场景之前必须等待的时间。
当客户端收到 CZML 数据包时,它会遍历数据包中包含的每个属性。对于每个属性,它遍历定义属性的每个时间间隔。对于每个间隔,它确定是否已经为属性定义了指定的间隔。如果已经定义了间隔,则更新现有间隔;否则,将创建一个新。
更新现有间隔时,提供的任何子属性值都将替换现有值(如果有)。唯一的例外是以前的属性值和新的属性值都包含时间标记的样本。在这种情况下,样本将添加到该间隔的样本列表中。
当新间隔与现有间隔重叠时,新间隔优先,现有间隔将被截断或完全删除。请务必记住这一点,因为在确定间隔是新的间隔还是对现有间隔的更新时,将针对截断的间隔测试以后的间隔。
间隔中的样本必须通过增加单个数据包内的时间进行排序。但是,在数据包之间,不必以任何特定顺序提供样本。但是,在流式传输非连续样本时,必须注意确保合理的插值。
2.6 可用性(availability)
除了该属性之外,CZML 数据包还有一个附加的特殊属性:。id
availability
{
"id": "PredatorUAV",
"availability": "2012-04-30T12:00:00Z/14:00:00Z",
// ...
}
该属性指示对象的数据何时可用。如果已知某个对象的数据在当前动画时间可用,但客户端尚未拥有该数据(可能是因为它将在以后的数据包中到达),则客户端可能会暂停并显示类似“正在缓冲...”的消息。当它等待接收数据时。该属性可以是指定单个间隔的单个字符串,也可以是表示间隔的字符串数组。availability
如果以后的铯数据包发生更改或发现不正确,则可以更新此可用性。例如,SGP4 传播器可能会报告所有时间的可用性,但随后传播器会引发异常,并且需要调整可用性。如果此可选属性不存在,则假定该对象始终可用。可用性的作用域为特定的 CZML 流,因此两个不同的流可以列出单个对象的不同可用性。在单个流中,为对象声明的最后一个可用性是有效的可用性,并且将忽略以前数据包中的任何可用性。如果某个对象一次可用,则客户端希望该对象至少具有一个属性,并且它期望在该时需要定义的所有属性。如果对象没有任何属性,或者定义了所需的属性,但在动画时没有定义,则客户端可以暂停动画并等待更多数据。
2.7 拓展czml
CZML 可以使用自定义属性进行扩展。为了最大程度地减少冲突,我们建议用户在其自定义属性前面加上某种标识符。
3. czml案例-加载立方体
3.1 直接加载czml格式数据:
// 加载czml数据
const czml = [
{
id: "document",
name: "box",
version: "1.0",
},
{
id: "shape1",
name: "Blue box",
position: {
cartographicDegrees: [-114.0, 40.0, 300000.0],
},
box: {
dimensions: {
cartesian: [400000.0, 300000.0, 500000.0],
},
material: {
solidColor: {
color: {
rgba: [0, 0, 255, 255],
},
},
},
},
},
];
console.log(czml);
console.log(JSON.stringify(czml));
// 加载czml数据
let promiseData = Cesium.CzmlDataSource.load(czml);
promiseData.then((dataSource) => {
console.log(dataSource);
viewer.dataSources.add(dataSource);
viewer.flyTo(dataSource);
});
实现效果:
3.2 通过引用czml数据加载立方体(url)
let czmlUrl = "./Assets/box.czml";
// 加载czml数据
let promiseData = Cesium.CzmlDataSource.load(czml);
promiseData.then((dataSource) => {
console.log(dataSource);
viewer.dataSources.add(dataSource);
viewer.flyTo(dataSource);
});
box.czml:
[{"id":"document","name":"box","version":"1.0"},{"id":"shape1","name":"Blue box","position":{"cartographicDegrees":[-114,40,300000]},"box":{"dimensions":{"cartesian":[400000,300000,500000]},"material":{"solidColor":{"color":{"rgba":[0,0,255,255]}}}}}]
实现效果:
4. czml案例-时序移动
<template>
<div id="cesiumContainer" ref="cesiumContainer"></div>
</template>
<script setup>
// yarn add cesium
// 将cesium目录下的Build/Cesium4个目录拷贝到public,然后将widgets目录拷贝一份到src下
import * as Cesium from "cesium";
import "./Widgets/widgets.css";
import { onMounted } from "vue";
// 设置cesium token
Cesium.Ion.defaultAccessToken =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJhMzNkNTE5Zi1mMjY4LTRiN2QtOTRlZC1lOTUyM2NhNDYzNWYiLCJpZCI6NTU0OTYsImlhdCI6MTYyNTAyNjMyOX0.a2PEM4hQGpeuMfeB9-rPp6_Gkm6O-02Dm4apNbv_Dlk";
// 设置cesium静态资源路径
window.CESIUM_BASE_URL = "/";
onMounted(() => {
var viewer = new Cesium.Viewer("cesiumContainer", {
// 是否显示信息窗口
infoBox: false,
// terrainProvider: Cesium.createWorldTerrain(),
//设置开始是否开启动画
shouldAnimate: true,
});
// 隐藏logo
viewer.cesiumWidget.creditContainer.style.display = "none";
// 加载czml数据
const czml = [
{
id: "document",
name: "CZML Point - Time Dynamic",
version: "1.0",
},
{
id: "point",
// 物体在什么时间范围可用
availability: "2012-08-04T16:00:00Z/2012-08-04T16:05:00Z",
position: {
// 设置物体的起始时间
epoch: "2012-08-04T16:00:00Z",
// 设置了四个维度,四个维度为一个整体;1维是时间,2维是经度,3维是纬度,4维是高度
cartographicDegrees: [
0, -70, 20, 150000, 100, -80, 44, 150000, 200, -90, 18, 150000, 300,
-98, 52, 150000,
],
},
point: {
color: {
rgba: [255, 255, 255, 128],
},
outlineColor: {
rgba: [255, 0, 0, 128],
},
outlineWidth: 3,
pixelSize: 15,
},
},
];
// 加载czml数据
let promiseData = Cesium.CzmlDataSource.load(czml);
promiseData.then((dataSource) => {
console.log(dataSource);
viewer.dataSources.add(dataSource);
// viewer.flyTo(dataSource);
});
});
</script>
<style>
* {
margin: 0;
padding: 0;
}
#cesiumContainer {
width: 100vw;
height: 100vh;
}
</style>
实现效果: