cesium CZML

CZML


CZML官方指南

CZML是一种JSON格式,用于描述时间动态图形场景,主要用于在运行Cesium的Web浏览器中显示。它描述了线条、点、广告牌、模型和其他图形基元,并指定它们如何随时间变化。

虽然Cesium具有丰富的客户端API,但CZML允许它是数据驱动的,以便通用的Cesium查看器可以显示丰富的场景,而无需任何自定义代码。

在许多方面,Cesium和CZML之间的关系类似于谷歌地球和KML之间的关系。

CZML 和 KML 都是用于在各自的客户端中描述场景的数据格式,旨在由各种应用程序生成,甚至可能是手动编写的。两者都意味着与客户端完全无关,以便其他兼容的客户端可以渲染其中描述的场景。

CZML具有许多重要特征,其中一些特征将其与KML区分开来:

  1. CZML 基于 JSON。
  2. CZML可以准确地描述随时间变化的属性。例如,一条线在一个时间间隔内可以是红色,在另一个时间间隔内可以是蓝色。客户还希望能够对带有时间标记的样本进行插值。
    如果车辆的位置指定了两次,则客户端可以使用 CZML 指定的插值算法在这两次之间准确地显示车辆的位置。每个属性都是时间动态的。
  3. CZML 的结构可实现高效的增量流式传输到客户端。在显示场景之前,不需要整个文档出现在客户端上。在许多情况下,单个客户端甚至可以在流进行过程中加入和离开流。
  4. CZML高度优化,旨在解析时更紧凑也更容易,让人工的读写更容易。
  5. CZML是可扩展的,尽管CZML的主要作用在与虚拟地球客户端程序与场景的交流,但它可以很容易的通过扩展来满足其他一些辅助的程序对静态或动态数据的需求。例如,随时间动态变化在数据就可以用在某些2D的图表程序中。
  6. CZML 是一种开放格式。我们希望尽可能多的项目能够利用它,并希望有朝一日能与一个标准机构(如OGC)正式合作。
  7. 可以通过czml-writer来生成CZML,这个程序维护在Github上。
    ● CZML Structure(CZML结构) - CZML 文档/流的整体结构。
    ● Packet(数据包) - CZML 文档/流中存在的标准内容的架构。
    更多示例可在Cesium官网示例查看。


CZML格式解析


解析文件格式

这里我们以JavaScript中的数据类型来理解CZML,我们可以把它当成就是一个对象数组,即格式如下:

const czml = [{...},{...}];

数组中的每一个对象都是一个packet(CZML数据包),每个数据包都是一个对象,数据包则用于描述在场景中的单个对象(比如一个点、线、广告牌、模型等)的属性信息,每个数据包都需要一个唯一标识,即id,如果不手动设置id的话,客户端也会自动赋予id一个唯一的值,但为了方便后续我们基于id来对相应的数据包进行操作,因此建议在构建每一个数据包时都赋予它们唯一标识。


声明对象

数组的第一个对象,即czml[0],为声明对象,用于声明该数据格式为CZML,声明对象包含了id(唯一标识)、name(名称)、version(CZML的版本,暂时只有1.0)、clock(用于设置整个数据集的时钟)等属性。
clock数据类型为ClockStep对象,用于定义时钟在每个嘀嗒时刻是如何前进的,ClockStep对象包含了interval、currentTime、multiplier、range、step等属性,具体解析如下:

  • interval - 设置开始时间和结束时间

  • currentTime - 设置当前时间

  • multiplier - 乘数/倍率,当step被设置为TICK_DEPENDENT时,这是每个嘀嗒时刻前进的秒数;当step设置为SYSTEM_CLOCK_DEPENDENT时,它乘以每个嘀嗒时刻之间的系统时间;该值在SYSTEM_CLOCK模式下被忽略;默认值为1.0

  • range - 当前时间达到开始时间或结束时间的行为,包含了"UNBOUNDED"、“CLAMPED”、“LOOP_STOP"等选项值,默认值为"LOOP_STOP”,具体解析如下:

    ○ UNBOUNDED - 时钟将继续朝当前的方向前进
    ○ CLAMPED - 时钟会停止
    ○ LOOP_STOP - 当前进过程中到达结束时间时,时钟将跳转到开始时间;当前进过程中到达开始时间时,时钟将停止

  • step - 设置当前时间是如何前进的,包含了"TICK_DEPENDENT"、“SYSTEM_CLOCK_MULTIPLIER”、“SYSTEM_CLOCK"等选项值;默认值为"SYSTEM_CLOCK_MULTIPLIER”,具体解析如下:

    ○ TICK_DEPENDENT - 当前时间每个嘀嗒时刻就前进multiplier 秒
    ○ SYSTEM_CLOCK_MULTIPLIER - 当前时间按自最后一个嘀嗒时刻以来的系统时间前进,乘以multiplier
    ○ SYSTEM_CLOCK - 时钟总是设置为当前系统时间

const exampleObj = {
    id: "document",
    name: "My Document",
    version: "1.0",
    clock: {
        interval: "2012-03-15T10:00:00Z/2012-03-16T10:00:00Z",
        currentTime: "2012-03-15T10:00:00Z",
        multiplier: 60,
        range: "LOOP_STOP",
        step: "SYSTEM_CLOCK_MULTIPLIER"
    }
}



实体对象

数组的第二个对象开始,都是实体对象,实体对象用于描述实体在三维场景中的信息和状态。

基本属性

  • id - 唯一标识,用于后续针对实体进行操作
  • name - 名称
  • delete - 是否删除该实体对象的所有现有数据
  • parent - 父对象id(如果有的话)
  • description - 实体对象的HTML描述(设置了貌似没生效,可自行测试)
  • availability - 可用性,用于表示对象的数据在什么时候是可用的,比如:“2012-08-04T10:00:00Z/2012-08-04T15:00:00Z”
  • properties - 自定义属性,键值对的格式,比如:{ detail : “该实体是模型” }
  • position - 实体的位置,格式为对象,常用的位置表达方式如下:
    ○ cartesian - 基于笛卡尔空间直角坐标系的坐标,格式为 [ X, Y, Z ] 或 [ Time, X, Y, Z, Time, X, Y, Z, … ]
const exampleObj = {
  ...,
  position: {
    cartesian: [
  		0, 4650397.56551457, -3390535.52275848, -4087729.48877329,
      300, 3169722.12564676, -2787480.80604407, -5661647.74541255,
      600, 1369743.14695017, -1903662.23809705, -6663952.07552171
  	]
  }
}

○ cartographicRadians - 基于WGS84坐标系的坐标,格式为 [ Longitude, Latitude, Height ] 或 [ Time, Longitude, Latitude, Height,Time, Longitude, Latitude, Height, … ]

const exampleObj = {
  position: {
    cartographicRadians: [ -1.3439035240356338, 0.6457718232379019, 100000 ]
  }
}

○ cartographicDegrees - 基于WGS84坐标系的坐标,格式为 [ Longitude, Latitude, Height ] 或 [ Time, Longitude, Latitude, Height,Time, Longitude, Latitude, Height, … ]

const exampleObj = {
  position: {
    cartographicDegrees: [ -77, 37, 100000 ]
  }
}
  • orientation - 实体的朝向,主要包含了以下属性:
    ○ unitQuaternion - 一组用于表示三维空间中旋转的四维坐标,格式为 [ X, Y, Z, W ] 或 [ Time, X, Y, Z, W, Time, X, Y, Z, W, … ]
    ○ reference - 指定为对另一个属性的引用的偏移量,比如:“object1#billboard.scale”
    ○ velocityReference - 指定为位置属性的归一化速度向量的方向,引用必须指向位置属性,比如:“#position”
  • viewFrom - 观看实体时建议的相机位置,主要包含了以下属性:
    ○ cartesian - 基于笛卡尔空间直角坐标系的坐标,格式为 [ X, Y, Z ] 或 [ Time, X, Y, Z, Time, X, Y, Z, … ]
    ○ reference - 指定为对另一个属性的引用的偏移量,比如:“object1#billboard.scale”

实体类型

除了基本属性,还有一类属性是用于描述实体类型的,比如billboard(广告牌,即图标)、box(盒子)、corridor(走廊)、cylinder(圆柱体)、ellipse(椭圆)、ellipsoid(椭圆体)、label(文字)、model(模型)、path(路径)、point(点)、polygon(多边形)、polyline(折线)、polylineVolume(具有体积的折线)、rectangle(矩形)、tileset(3Dtileset)、wall(墙)等

由于不同实体类型的配置项不同,且配置项较多,这里就不做详细介绍了,在后续的应用示例中会涉及到一些相关应用。

const exampleObj = {
	id: "uav_001",
  name: "001号无人机",
	availability: "2012-08-04T10:00:00Z/2012-08-04T15:00:00Z",
	position: {
		epoch: "2012-08-04T10:00:00Z",
		cartographicDegrees: [
			0, 104.374422, 28.117186, 150, 
      100, 104.375259, 28.116129, 180, 
      200, 104.375164, 28.115771, 220, 
      300, 104.376489, 28.114326, 250
    ]
	},
	model: {
		gltf: "/public/model/uav.glb",
		scale: 3.0,
	},
}

间隔

间隔是通过interval属性来设置的,其值为两个时间间隔。

应用场景:比如添加一架飞机模型,但是要求它并不是一直显示的,它在某个时间段是隐藏的,而在某个时间段则是显示状态,那这时候就可以通过interval来对show属性(该属性为布尔值,用于控制显示和隐藏)进行配置,从而实现所要的效果。

const exampleObj = {
	id: "air_001",
  name: "001号飞机",
	availability: "2012-08-04T10:00:00Z/2012-08-04T15:00:00Z",
	position: {
    cartographicDegrees: [ 104.374422, 28.117186, 180 ]
  },
	model: {
		show: [
			{
				interval: "2012-08-04T10:00:00Z/2012-08-04T10:02:00Z",
				boolean: false
			},
      {
        interval: "2012-08-04T10:02:00Z/2012-08-04T11:00:00Z",
        boolean: true
      },
      {
        interval: "2012-08-04T11:00:00Z/2012-08-04T15:00:00Z",
        boolean: false
      }
		],
		gltf: "/public/model/air.glb",
		scale: 1.0
	}
}

代码解析:在上述代码中,飞机模型在早上10点到10点02分这段时间内是隐藏状态,十点02分到11点这段时间为显示状态,而11点到下午3点这段时间又回到隐藏状态。



属性值采样


epoch用法

表达不同时间,位置信息的不同可以用以下写法:

const exampleObj = {
  ...,
  position: {
    cartesian: [
  		"2012-04-30T12:00Z", 4650397.56551457, -3390535.52275848, -4087729.48877329,
      "2012-04-30T12:01Z", 3169722.12564676, -2787480.80604407, -5661647.74541255,
      "2012-04-30T12:02Z", 1369743.14695017, -1903662.23809705, -6663952.07552171
  	]
  }
}

但是,上述写法需要给每个位置信息指定时间,写起来比较麻烦,因此可以采用以下写法:

const exampleObj = {
  ...,
  position: {
    epoch: "2012-04-30T12:00Z",
    cartesian: [
  		0, 4650397.56551457, -3390535.52275848, -4087729.48877329,
      60, 3169722.12564676, -2787480.80604407, -5661647.74541255,
      120, 1369743.14695017, -1903662.23809705, -6663952.07552171
  	]
  }
}

epoch表示起始时间,其使用ISO8601规范来表示日期和时间,即使用"2012-04-30T12:00Z"这种写法来表示时间,采用距离起始时间的秒数来改变数据。

代码解析:比如上述代码中,0相当于距离epoch的秒数为0秒,即0 == "2012-04-30T12:00Z";60相当于距离epoch的秒数为60秒,即60 == "2012-04-30T12:01Z";120相当于距离epoch的秒数为120秒,即120 == "2012-04-30T12:02Z"


附加属性

  • nextTime - 在时间间隔内下一个采样的时间,可以通过ISO8061方式,也可以通过与epoch距离的秒数来定义。它决定了不同数据包之间的采样是否有停顿
  • previousTime - 在时间间隔内前一个采样的时间,可以通过ISO8061方式,也可以通过与epoch距离的秒数来定义,它决定了不同数据包之间的采样是否有停顿
  • interpolationAlgorithm - 用于插值的算法,包含了"LAGTANGE",“HERMITE"和"GEODESIC”;默认值为"LAGRANGE";如果位置不在该采样区间,那么这个属性值会被忽略
  • interpolationDegree - 定义了用来插值所使用的多项式的次数,1表示线性差值,2表示二次插值法,默认值为1;如果使用"GEODESIC"插值算法,那么这个属性将被忽略

应用场景:我们有一个需要插值的属性,时间为0到10秒,间隔为1.0秒,第一个数据包为0到3秒,第二个数据包为8到10秒,在客户端还没有接收到包含4到7秒的数据包时,如何渲染时间为5的场景。

const exampleObj = {
  ...,
  position: {
    epoch: "2012-04-30T12:00Z",
    cartesian: [
  		0, 4650397.56551457, -3390535.52275848, -4087729.48877329,
      1.0, 3169722.12564676, -2787480.80604407, -5661647.74541255,
      2.0, 1369743.14695017, -1903662.23809705, -6663952.07552171,
      3.0, 1369743.14695017, -1903662.23809705, -6663952.07552171
  	],
    previousTime: -1.0,
    nextTime: 4.0
  }
}

代码解析:上述代码中,nextTime的作用是告诉客户端在3.0后下一个时间是4.0;3的后面是8,根据nextTime我们就知道3和8之间肯定还有一段数据没有接收到,所以在开始插值之前我们就需要先等待数据读取完成。

没有必要同时设置previousTime和nextTime,在不同的情况下选择使用其中最方便的一个就可以了,只要定义其中的一个,在进行插值前就会首先对数据进行完整性检查。



事件源与流式处理

每个数据包将作为单独的事件流式传输到客户端,从而解决了大json文件读取慢的问题。

我们通常会使用一个数据包来描述一个对象,其包含了该对象所有的图形属性;不过还可以使用其他的方式来表示,比如使用多个数据包来描述同一个对象,只需要让这些数据包拥有相同的id即可,一般在当对象的属性跨越多个时间间隔或者一个时间间隔又多个时间戳采样时使用这种方式。

当客户端接受到一个数据包时,会遍历数据包的每一个属性;针对每个属性,会遍历属性定义的每个时间间隔;针对每个时间间隔,会判断该时间间隔是否已定义,如已定义,则更新已存在的间隔,如没定义,则根据这个时间间隔来创建一个新的。

当更新一个已存在的时间间隔时,如果有子属性,则会覆盖掉原有的值;而当已有的属性和新接收到的属性都包含时间戳采样时,新接收的采样不会覆盖已有的,而是加到已有的采样列表之中。

当新的时间间隔与已有的发生重叠时,新的时间间隔拥有较高的优先级,原有的时间间隔将被截断或者整个移除。

在同一个数据包中的时间间隔的时间必须以增序排列,不同的数据包之间则没有要求,但是对于不连续的采样还是应考虑合理的插值顺序。

可用性

使用availability属性来判断对象的数据在什么时间段内是可用的。

const exampleObj = {
	id: "uav",
  // 实体在什么时间范围可用
	availability: "2012-08-04T10:00:00Z/2012-08-04T15:00:00Z",
	position: {
		epoch: "2012-08-04T10:00:00Z",
		cartographicDegrees: [
  		0, 104.374422, 28.117186, 150,
  		100, 104.375259, 28.116129, 180,
  		200, 104.375164, 28.115771, 220,
  		300, 104.376489, 28.114326, 250
		],
	},
	model: {
		gltf: "/public/model/uav.glb",
		scale: 3.0,
	}
}



扩展

可以给CZML添加自定义属性,最好采用特定的前缀进行区分。

前端开发时的加载方式


错误的加载方式

以import…from…的方式导入,项目会报错。(禁用)

import test from '/public/test.czml';
cosnt promiseData = Cesium.CzmlDataSource.load(test);

正确的加载方式

以.czml文件格式进行加载。(不太推荐×)

cosnt promiseData = Cesium.CzmlDataSource.load('public/test.czml');

编写一个对象数组来代替.czml文件进行加载。(推荐√)

// 创建czml
const czml = [
	{
    id: "document",
    name: "MYCZML",
    version: "1.0",
	},
	{
    id:"id1",
    ...
	}
];
// 加载czml数据
const promiseData = Cesium.CzmlDataSource.load(czml);

以.json文件格式取代.czml格式进行加载。(推荐√)

const promiseData = Cesium.CzmlDataSource.load('public/test.json');



案例

// 加载运动的装甲车

const czml = [
    {
      "id": "document",
      "clock": {
          "interval": "2022-10-14T10:08:00+08:00/2022-10-15T16:08:00+08:00",
          "currentTime": "2022-10-14T10:08:00+08:00",
          "step": "SYSTEM_CLOCK_MULTIPLIER",
          "range": "CLAMPED",
          "multiplier": 60,
      },
      "version": "1.0"
    },
    {
        "id": "Air",
        "name": "Cesium Air",
        "description": "Cesium Air",
        "position": {
            "epoch": "2022-10-14T10:08:00+08:00",
            "cartographicDegrees": [
                    0, -94.030766, 41.827829, 0, 
                    1000, -94.035766, 41.827829, 0, 
                    2000, -94.035766, 41.829013, 0
            ]
        },
        "model": {
            "gltf": new URL('./assets/GroundVehicle.glb', import.meta.url).href,
            "scale": 1,
            "minimumPixelSize": 200,
        },
        "path" : {
             material: {
                polylineOutline: {
                    color: {
                        rgba: [255, 0, 255, 255],
                    },
                },
            },
            width: 8,
            leadTime: 10,
            trailTime: 1000,
            resolution: 5,
        },
        label: {
            fillColor: {
                rgba: [255, 255, 255, 255],
            },
            horizontalOrigin: "LFET",
            pixelOffset: {
                cartesian2: [0, 60],
            },
            // style: "FILL",
            text: "汽车",
            showBackground: true,
            backgroundColor: {
                rgba: [112, 89, 57, 0],
            },
        },
    },
];


const dataSourcePromise = viewer.dataSources.add(
    Cesium.CzmlDataSource.load(czml)
);

dataSourcePromise
    .then(function (dataSource) {
        viewer.trackedEntity = dataSource.entities.getById(
            "Air"
        );// 对应czml实体id
        viewer.dataSources.add(dataSource).then(function(ds){
            var s = ds.entities.getById("Air"); // 对应czml实体id
            s.orientation =new Cesium.VelocityOrientationProperty(s.position);
        });
        
        // viewer.flyTo(dataSource)
    })
    .catch(function (error) {
        window.alert(error);
    });
// 加载透明红色球体

const czml = [
        {
            "id": "document",
            "name": "CZML typhoon",
            "version": "1.0",
            "clock": {
                "interval": "2022-10-14T10:08:00+08:00/2022-10-15T16:08:00+08:00",
                "currentTime": "2022-10-14T10:08:00+08:00",
                "step": "SYSTEM_CLOCK_MULTIPLIER",
                "range": "CLAMPED",
                "multiplier": 60,
            },
        },
        {
            "id": "typhoon",
            "name": "Red sphere with black outline",
            "position": {
                "epoch": "2022-10-14T10:08:00+08:00", // 开始时间
                "cartographicDegrees": [
					0, 100, 20, 0,
					500, 102, 20, 0,
					100, 102, 22, 0
				],
            },
            "ellipsoid": {
                "radii": {
                    "cartesian": [30000, 30000, 30000],
                },
                "fill": true,
                "material": {
                    "solidColor": {
                        "color": {
                            "rgba": [255, 0, 0, 100],
                        },
                    },
                },
                "outline": true,
                "outlineColor": {
                    "rgbaf": [0, 0, 0, 1],
                },
            },
        }
    ]

viewer.dataSources.add(Cesium.CzmlDataSource.load(czml))

你可能感兴趣的:(WebGIS,javascript,前端,人工智能)