Skip to content

平台的强大之处也在于其可以将时间运用到三维地球上,平台不仅仅是 3D 的地理空间的展示,同时也具备“时间维度”的时序的相关控制,具备空间+时间的管理,可以根据“时间”进行动画、轨迹记录、地球的光照等等所有与时间相关的可视化效果。

1. 打开时间相关控件

为了更好理解 Clock,我们可以在地图中打开 2 个 Cesium 内置时钟 Clock 相关的控件。

js
//合并属性参数,可覆盖config.json中的对应配置
var mapOptions = mars3d.Util.merge(options, {
  control: {
    clockAnimate: true, // 时钟动画控制(左下角)
    timeline: true // 是否显示时间线控件
  }
});
//创建三维地球场景
let map = new mars3d.Map('mars3dContainer', mapOptions);
//合并属性参数,可覆盖config.json中的对应配置
var mapOptions = mars3d.Util.merge(options, {
  control: {
    clockAnimate: true, // 时钟动画控制(左下角)
    timeline: true // 是否显示时间线控件
  }
});
//创建三维地球场景
let map = new mars3d.Map('mars3dContainer', mapOptions);

运行效果: image

2. 时序控制相关对象

2.1 clock 时钟对象

map.clock 时钟对象,由三维地图内部创建的Clock类,可以对时间维度做相关控制。

clock 对象是一个同步系统时间的一个对象,想要实现时间驱动数据的动态化渲染,都需要 clock 对象去创建一个时间序列。一般来说电脑屏幕的刷新率都是 60hz,cesium 定义的是每秒刷新 60 次,每帧需要的时间大概是 16.67mm。

Clock 中默认开始时间(startTime)为当前时间,终止时间(stopTime)为 24 小时后,并能获取当前时间(currentTime)。

clock 对象的构造器有以下几个常用属性:

  • map.clock.shouldAnimate = true : 控制开始和停止
  • map.clock.startTime = Cesium.JulianDate.fromIso8601('2017-08-25'):开始时间
  • map.clock.stopTime = Cesium.JulianDate.fromIso8601('2017-08-26'):结束时间
  • map.clock.currentTime = Cesium.JulianDate.fromIso8601('2017-08-25T12:18:20Z') :当前时间
  • map.clock.multiplier= 2.0:时间速率(快速播放与慢速播放),默认 1.0
  • map.clock.clockRange = Cesium.ClockRange.UNBOUNDED:表示时间轴达到终点之后的行为, 可选值:CLAMPED 达到终止时间后停止,LOOP_STOP 达到终止时间后重新循环,UNBOUNDED 达到终止时间后继续读秒。

2.2 Cesium.JulianDate 时间类

Clock 内部以儒略日(JulianDate)维护时间。其起始日期为公元前 4713 年 1 月 1 日中午 12 时,这和我们常用的格林威治时间略有不同,主要是天文学家使用。

Cesium.JulianDate 类提供了非常丰富的接口,时间的对比,运算,和格林威治时间的转换等,简单易用,完全满足各类需求。同时内部还可以采用国际原子时(TAI)的方式来记录。

3. 时序控制的应用

3.1 动态加载时间序列瓦片

所谓时间序列瓦片是指存在多套瓦片,每套瓦片不是单独的,与时间有关。比如我们每天拍摄一遍地球影像,然后把每天的影像都做成一套瓦片,那么一年下来就会有 365 套瓦片,采用传统方案我们只能写 365 个页面每个页面加载一天的瓦片。这样非常麻烦,并且没有一个动态变化的效果也无法进行对比。

时间序列瓦片与普通瓦片的区别正在此处。其创建时需要多指定与时间有关的参数。如下:

js
//interval表示传入的时间区间,index表示是第几个区间,这两个参数也就分割了times中的完整时间段,所以我们可以给time赋值为任意想要设置的值。
function dataCallback(interval, index) {
  console.log(index);
  var time;
  if (index === 0) {
    time = Cesium.JulianDate.toIso8601(interval.stop);
  } else {
    time = Cesium.JulianDate.toIso8601(interval.start);
  }
  //返回的是key、value形式,此处Time为key,而其必须与创建图层时候的{Time}字符串一致,否则请求的时候无法替换时间信息。
  return {
    Time: time
  };
}

var times = Cesium.TimeIntervalCollection.fromIso8601({
  iso8601: '2015-07-30/2017-06-16/P20D', //iso8601参数为时间范围及间隔,用'/'分割,第一个表示开始时间,第二个表示结束时间,P20D表示间隔20天,还可以是P1M、P1Y、P1Y3M5DT6H7M30S等,代表不同的时间间隔。
  leadingInterval: true,
  trailingInterval: true,
  isStopIncluded: false,
  dataCallback: dataCallback //dataCallback表示在每个时间段内如何取值
});

var tileLayer = new mars3d.layer.WmtsLayer({
  url: 'https://gibs.earthdata.nasa.gov/wmts/epsg4326/{best}/{Layer}/{Style}/{Time}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png', //{TileMatrix}/{TileRow}/{TileCol}表示z、x、y,无需手动设置
  layer: 'AMSR2_Snow_Water_Equivalent',
  style: 'default', //style参数会替换掉url中的{Style}字符串
  tileMatrixSetID: '2km', //tileMatrixSetID会替换掉{TileMatrixSet}字符串
  format: 'image/png',
  dimensions: {
    //dimensions里面的参数只要出现在url中全部会被其value替换掉。
    Layer: 'AMSR2_Snow_Water_Equivalent',
    best: 'best'
  },
  clock: map.clock, //clock表示所使用的时钟,直接设置为系统时钟
  times: times, //重点
  maximumLevel: 5
});
map.addLayer(tileLayer);
//interval表示传入的时间区间,index表示是第几个区间,这两个参数也就分割了times中的完整时间段,所以我们可以给time赋值为任意想要设置的值。
function dataCallback(interval, index) {
  console.log(index);
  var time;
  if (index === 0) {
    time = Cesium.JulianDate.toIso8601(interval.stop);
  } else {
    time = Cesium.JulianDate.toIso8601(interval.start);
  }
  //返回的是key、value形式,此处Time为key,而其必须与创建图层时候的{Time}字符串一致,否则请求的时候无法替换时间信息。
  return {
    Time: time
  };
}

var times = Cesium.TimeIntervalCollection.fromIso8601({
  iso8601: '2015-07-30/2017-06-16/P20D', //iso8601参数为时间范围及间隔,用'/'分割,第一个表示开始时间,第二个表示结束时间,P20D表示间隔20天,还可以是P1M、P1Y、P1Y3M5DT6H7M30S等,代表不同的时间间隔。
  leadingInterval: true,
  trailingInterval: true,
  isStopIncluded: false,
  dataCallback: dataCallback //dataCallback表示在每个时间段内如何取值
});

var tileLayer = new mars3d.layer.WmtsLayer({
  url: 'https://gibs.earthdata.nasa.gov/wmts/epsg4326/{best}/{Layer}/{Style}/{Time}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png', //{TileMatrix}/{TileRow}/{TileCol}表示z、x、y,无需手动设置
  layer: 'AMSR2_Snow_Water_Equivalent',
  style: 'default', //style参数会替换掉url中的{Style}字符串
  tileMatrixSetID: '2km', //tileMatrixSetID会替换掉{TileMatrixSet}字符串
  format: 'image/png',
  dimensions: {
    //dimensions里面的参数只要出现在url中全部会被其value替换掉。
    Layer: 'AMSR2_Snow_Water_Equivalent',
    best: 'best'
  },
  clock: map.clock, //clock表示所使用的时钟,直接设置为系统时钟
  times: times, //重点
  maximumLevel: 5
});
map.addLayer(tileLayer);

3.2 动画轨迹

在平台中,提供SampledPositionProperty 类 来实现矢量对象的时序控制,达到 模型等点状对象 沿轨迹平滑移动的目的。只需要指定每个时间对应的坐标即可,中间平台自动进行按时间平滑动画移动。

js
//构造时序轨迹对象
let propertyFJ = getSampledPositionProperty([
  [116.341348, 30.875522, 500],
  [116.341432, 30.871815, 500],
  [116.341181, 30.85326, 500],
  [116.345028, 30.870436, 500]
]);

//飞机模型
var graphicModel = new mars3d.graphic.ModelEntity({
  position: propertyFJ,
  orientation: new Cesium.VelocityOrientationProperty(propertyFJ),
  style: {
    url: 'http://data.mars3d.cn/gltf/mars/wrj.glb',
    scale: 0.1,
    minimumPixelSize: 20
  }
});
graphicLayer.addGraphic(graphicModel);

//计算演示的轨迹
function getSampledPositionProperty(points) {
  let property = new Cesium.SampledPositionProperty();

  let start = map.clock.currentTime;
  let positions = mars3d.LngLatArray.toCartesians(points);
  for (let i = 0; i < positions.length; i++) {
    let time = Cesium.JulianDate.addSeconds(
      start,
      i * 20,
      new Cesium.JulianDate()
    );
    let position = positions[i];
    property.addSample(time, position); //指定每个时间对应的位置
  }
  return property;
}
//构造时序轨迹对象
let propertyFJ = getSampledPositionProperty([
  [116.341348, 30.875522, 500],
  [116.341432, 30.871815, 500],
  [116.341181, 30.85326, 500],
  [116.345028, 30.870436, 500]
]);

//飞机模型
var graphicModel = new mars3d.graphic.ModelEntity({
  position: propertyFJ,
  orientation: new Cesium.VelocityOrientationProperty(propertyFJ),
  style: {
    url: 'http://data.mars3d.cn/gltf/mars/wrj.glb',
    scale: 0.1,
    minimumPixelSize: 20
  }
});
graphicLayer.addGraphic(graphicModel);

//计算演示的轨迹
function getSampledPositionProperty(points) {
  let property = new Cesium.SampledPositionProperty();

  let start = map.clock.currentTime;
  let positions = mars3d.LngLatArray.toCartesians(points);
  for (let i = 0; i < positions.length; i++) {
    let time = Cesium.JulianDate.addSeconds(
      start,
      i * 20,
      new Cesium.JulianDate()
    );
    let position = positions[i];
    property.addSample(time, position); //指定每个时间对应的位置
  }
  return property;
}