首先 Cesium 中如果绘制三维物体,都需要将不同的坐标转换为笛卡尔坐标系,也就是 Cartesian3 类型的数据;这是 Cesium 的基础数据类型。Cesium 中常见的坐标系有 wgs84 经纬度坐标系、wgs84 弧度坐标系(Cartographic,弧度= π/180 × 经纬度角度)、笛卡尔空间直角坐标系(Cartesian3)、平面坐标系(Cartesian2)、4D 笛卡坐标系(Cartesian4),下面是列举了我们常见的一些坐标系转换为 Cartesian3 类型数据以及一些坐标的互转。

需要注意的是 cesium 目前支持两种坐标系,wgs84 以及 WebMercator(笛卡尔坐标系)

# 坐标系

# 笛卡尔坐标系(Cartesian3)

  • 笛卡尔坐标系以米为单位;
  • 坐标系原点是地球几何中心;
  • xz 平面是中央经线和 180 度经线组成的平面,其中 x 轴正方向指向的是中央经线,x 轴负方向指向 180 度经线;
  • y 轴正方向指向东经 90 度经线,负方向指向西经 90 度经线。

avatar

# 平面坐标系(Cartesian2)

与 Cartesian3 相比少了一个 z 的分量;

avatar

# 坐标系转换

# 经纬度转换为 Cartesian3 类型

  • Cesium.Cartesian3.fromRadians:从给定的经度和弧度值返回 Cartesian3。注意这里的经纬度是 wgs84 转换成弧度的经纬度。
Cartesian3.fromRadians = function ( longitude, latitude, height, ellipsoid, result ) {
  // 检验经纬度是否符合标准
  Check.typeOf.number("longitude", longitude);
  Check.typeOf.number("latitude", latitude);

  // 如果高度为空,默认为 0
  height = defaultValue(height, 0.0);
  // 如果为传入参考椭球体,默认使用 wgs 84 坐标系下进行转换
  var radiiSquared = defined(ellipsoid)
    ? ellipsoid.radiiSquared
    : wgs84RadiiSquared;

  var cosLatitude = Math.cos(latitude);
  scratchN.x = cosLatitude * Math.cos(longitude);
  scratchN.y = cosLatitude * Math.sin(longitude);
  scratchN.z = Math.sin(latitude);
  scratchN = Cartesian3.normalize(scratchN, scratchN);

  Cartesian3.multiplyComponents(radiiSquared, scratchN, scratchK);
  var gamma = Math.sqrt(Cartesian3.dot(scratchN, scratchK));
  scratchK = Cartesian3.divideByScalar(scratchK, gamma, scratchK);
  scratchN = Cartesian3.multiplyByScalar(scratchN, height, scratchN);

  if (!defined(result)) {
    result = new Cartesian3();
  }
  return Cartesian3.add(scratchK, scratchN, result);
};

示例:

var position = Cesium.Cartesian3.fromRadians(-2.007, 0.645)

# 经纬度和弧度的转换

具体看官方文档 https://cesium.com/docs/cesiumjs-ref-doc/Math.html?classFilter=Math

  • 经纬度转弧度
const radians = Cesium.Math.toRadians(degrees)
  • 弧度转经纬度
const degrees = Cesium.Math.toDegrees(radians)

# wgs84 经纬度坐标和 wgs84 弧度坐标系(Cartographic)的转换

  • 第一种方法就是先将角度用上面的方法转换为弧度,再进行转换
const longitude = Cesium.Math.toRadians(longitude1) // 其中 longitude1 为角度
const latitude= Cesium.Math.toRadians(latitude1) // 其中 latitude1 为角度

const cartographic = new Cesium.Cartographic(longitude, latitude, height)
  • 第二种方法:直接转换
// longitude 和 latitude 为角度
const cartographic= Cesium.Cartographic.fromDegrees(longitude, latitude, height)
  • 第三种
// longitude 和 latitude 为弧度
const cartographic= Cesium.Cartographic.fromRadians(longitude, latitude, height)

# wgs84 坐标系和笛卡尔空间直角坐标系(Cartesian3)的转换

下面是经纬度坐标转换为空间直角坐标系(Cartesian3):

// 高度默认值为 0,可以不用填写,longitude 和 latitude 为角度
const position = Cesium.Cartesian3.fromDegrees(longitude, latitude, height)
// coordinates 为不带高度的数组。例如:[113.0, 37.0, 113.0, 23]
const positions = Cesium.Cartesian3.fromDegreesArray(coordinates)

// coordinates 为带有高度的数组。例如:[-115.0, 37.0, 100000.0, -107.0, 33.0, 150000.0]
const positions = Cesium.Cartesian3.fromDegreesArrayHeights(coordinates)

对应的,有通过弧度转化的方法,具体有 Cesium.Cartesian3.fromRadians,Cesium.Cartesian3.fromRadiansArray,Cesium.Cartesian3.fromRadiansArrayHeights 等方法,用法跟上面相同。

需要注意的是,上面转换函数中最后均有一个默认参数 ellipsoid(默认值为 Ellipsoid.WGS84)这个是参考椭球体。

# 笛卡尔空间直角坐标系(Cartesian3)转换为 wgs84

  • 第一种:直接转换,转换为弧度
const cartographic = Cesium.Cartographic.fromCartesian(cartesian3)
  • 第二种:间接转化,转换为角度
const ellipsoid = viewer.scene.globe.ellipsoid
const cartesian3 = new Cesium.cartesian3(x,y,z)
const cartographic = ellipsoid.cartesianToCartographic(cartesian3)
const lat = Cesium.Math.toDegrees(cartograhphic.latitude)
const lng = Cesium.Math.toDegrees(cartograhpinc.longitude)
const alt = cartographic.height

# 屏幕坐标和笛卡尔空间直角坐标系(Cartesian3)的转换

  • 屏幕转笛卡尔空间直角坐标系(Cartesian3):
const pick1 = new Cesium.Cartesian2(0, 0)
const cartesian3 = viewer.scene.globe.pick(viewer.camera.getPickRay(pick1), viewer.scene)   

注意这里屏幕坐标一定要在球上,否则生成出的 cartesian 对象是 undefined

  • 笛卡尔空间直角坐标系(Cartesian3)转屏幕坐标:
const cartesian2 = Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene, cartesian3)

结果是 Cartesian2 对象,取出 X, Y 即为屏幕坐标;

# Cartesian3 转为 Cartesian2

const cartesian2 = Cesium.Cartesian2.fromCartesian3(cartesian, result)

# 其他方法

# 计算两个三维坐标系之间的距离

// pick1、pick3 都是三维坐标系
const d = Cesium.Cartesian3.distance(
    new Cesium.Cartesian3(pick1.x, pick1.y, pick1.z),
    new Cesium.Cartesian3(pick3.x, pick3.y, pick3.z)
)

# 笛卡尔坐标系转换的一些 API

API 说明
Cesium.Cartesian3.abs(cartesian, result) 计算绝对值
Cesium.Cartesian3.add(left, right, result) 计算两个笛卡尔的分量和
Cesium.Cartesian3.angleBetween(left, right) 计算角度(弧度制)
Cesium.Cartesian3.cross(left, right, result) 计算叉积
Cesium.Cartesian3.distance(left, right) 计算两点距离
Cesium.Cartesian3.distanceSquared(left, right) 计算两点平方距离
Cesium.Cartesian3.divideByScalar(cartesian, scalar, result) 计算标量除法
Cesium.Cartesian3.divideComponents(left, right, result) 计算两点除法
Cesium.Cartesian3.dot(left, right) 计算点乘
Cesium.Cartesian3.equals(left, right) 比较两点是否相等
Cesium.Cartesian3.fromArray(array, startingIndex, result) 从数组中提取3个数构建笛卡尔坐标
Cesium.Cartesian3.fromDegrees(longitude, latitude, height, ellipsoid, result) 将将纬度转换为笛卡尔坐标(单位是度°)
Cesium.Cartesian3.fromDegreesArray(coordinates, ellipsoid, result) 返回给定经度和纬度值数组(以度为单位)的笛卡尔位置数组。
Cesium.Cartesian3.fromDegreesArrayHeights(coordinates, ellipsoid, result) 返回给定经度,纬度和高度的笛卡尔位置数组
Cesium.Cartesian3.fromElements(x, y, z, result) 创建一个新的笛卡尔坐标
Cesium.Cartesian3.fromRadians(longitude, latitude, height, ellipsoid, result) 返回笛卡尔坐标以弧度制的经纬度
Cesium.Cartesian3.fromRadiansArray(coordinates, ellipsoid, result) 返回笛卡尔坐标以弧度制的经纬度数组
Cesium.Cartesian3.fromRadiansArrayHeights(coordinates, ellipsoid, result) 返回笛卡尔坐标以弧度制的经纬度高度数组
Cesium.Cartesian3.fromSpherical(spherical, result) 将提供的球面转换为笛卡尔系
Cesium.Cartesian3.lerp(start, end, t, result) 使用提供的笛卡尔数来计算t处的线性插值或外推。
Cesium.Cartesian3.magnitude(cartesian) 计算笛卡尔长度
Cesium.Cartesian3.magnitudeSquared(cartesian) 计算提供的笛卡尔平方量级
Cesium.Cartesian3.maximumByComponent(first, second, result) 比较两个笛卡尔并计算包含所提供笛卡尔最大成分的笛卡尔。
Cesium.Cartesian3.maximumComponent(cartesian) 计算所提供笛卡尔坐标系的最大分量的值
Cesium.Cartesian3.midpoint(left, right, result) 计算右笛卡尔和左笛卡尔之间的中点
Cesium.Cartesian3.minimumByComponent(first, second, result) 比较两个笛卡尔并计算包含所提供笛卡尔的最小分量的笛卡尔
Cesium.Cartesian3.minimumComponent(cartesian) 计算所提供笛卡尔坐标系的最小分量的值
Cesium.Cartesian3.mostOrthogonalAxis(cartesian, result) 返回与提供的笛卡尔坐标最正交的轴
Cesium.Cartesian3.multiplyByScalar(cartesian, scalar, result) 将提供的笛卡尔分量乘以提供的标量
Cesium.Cartesian3.multiplyComponents(left, right, result) 计算两个笛卡尔的分量积
Cesium.Cartesian3.normalize(cartesian, result) 计算所提供笛卡尔的规范化形式
Cesium.Cartesian3.pack(value, array, startingIndex) 将提供的实例存储到提供的数组中
Cesium.Cartesian3.projectVector(a, b, result) 将向量a投影到向量 b 上
Cesium.Cartesian3.subtract(left, right, result) 计算两个笛卡尔分量差
Cesium.Cartesian3.unpack(array, startingIndex, result) 从压缩的数组中检索实例
Cesium.Cartesian3.unpackArray(array, result) 将笛卡尔分量数组解包为笛卡尔数组

# HeadingPitchRoll

HeadingPitchRoll 表示朝向。

# Camera 一些常用方法

Camera.setView(options) : 立即设置相机位置和朝向。
Camera.zoomIn(amount) : 沿着相机方向移动相机。
Camera.zoomOut(amount) : 沿着相机方向远离
Camera.flyTo(options) : 创建从一个位置到另一个位置的相机飞行动画。
Camera.lookAt(target, offset) : 依据目标偏移来设置相机位置和朝向。
Camera.move(direction, amount) : 沿着direction方向移动相机。
Camera.rotate(axis, angle) : 绕着任意轴旋转相机。

# scene 中时间的配置

Cesium 使用 JulianDate 描述某个时刻。

// 设置时钟和时间线
viewer.clock.shouldAnimate = true; // 当 viewer 开启后,启动动画
viewer.clock.startTime = Cesium.JulianDate.fromIso8601("2017-07-11T16:00:00Z")
viewer.clock.stopTime = Cesium.JulianDate.fromIso8601("2017-07-11T16:20:00Z")
viewer.clock.currentTime = Cesium.JulianDate.fromIso8601("2017-07-11T16:00:00Z")
viewer.clock.multiplier = 2 // 设置加速倍率
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP // 循环播放
viewer.timeline.zoomTo(viewer.clock.startTime, viewer.clock.stopTime) // 设置时间的可见范围

# Cesium 加载其他数据

Cesium 支持 GeoJson 和 KML 等格式的数据,同时也支持自定义格式 CZML。无论最初是什么格式,所有的空间矢量数据在 Cesium 里都是使用 Entity 相关 API 去展示的, Entity是 一种对几何图形做空间和时间展示的数据对象。

下面代码是加载 kml 数据并解析展示对应数据:

// 使用 KmlDataSource.load(optinos) 来从 KML 文件中读取点位数据
const kmlOptions = {
  camera: viewer.scene.camera,
  canvas: viewer.scene.canvas,
  clampToGround: true // 控制数据是否贴地
}
// 加载数据
const geocachePromise = Cesium.KmlDataSource.load('./Source/sample.kml', kmlOptions)

geocachePromise.then(function(dataSource) {
  // 把所有 entities 添加到 viewer 中显示
  viewer.dataSources.add(dataSource)
  // 获得 entity 列表
  const geocacheEntities = dataSource.entities.values
  for (let i = 0; i < geocacheEntities.length; i++) {
    const entity = geocacheEntities[i];
    if (Cesium.defined(entity.billboard)) {
      // 对这个 entity 设置样式
    }
  }
})

更新: 11/29/2020, 1:46:40 AM