"全局"坐标系
-
① ECEF坐标系(蓝色)
地心地固坐标系(Earth-Centered, Earth-Fixed,简称ECEF)简称地心坐标系,是一种以地心为原点的地固坐标系,是一种笛卡儿坐标系。其原点为地球的质心,z轴延伸通过的北极(即与地球旋转轴重合),x轴延伸通过本初子午线(0度经度)和赤道(0度纬度)的交点,y 轴垂直于xOz平面(即东经90度与赤道的交点)构成右手坐标系。
引入ECEF坐标系的主要原因是为了在全球范围内提供统一的三维坐标参考系。这在卫星导航、地球观测和全球地理信息系统中非常重要。在计算机图形学中,三维笛卡尔坐标系更适合进行旋转、缩放等几何变换。
-
② BLH(橙色)
大地坐标系,其中B、L、H分别代表纬度、经度、高程。
两者都可用于表达空间中的某点,只不过一个是经纬度坐标(BLH),一个是笛卡尔坐标(XYZ),而两者是可以相互转换的。篇幅有限,推导部分可参看 👇
"局部"坐标系
-
① ENU(绿色)
简介:站心坐标系,指通过给定一个世界坐标点来创建一个局部坐标系。这个局部坐标系以X轴指向东,Y轴指向北,Z轴指向垂直向上的方向,它是一种局部空间直角坐标系。
用处:引入ENU,是为了方便局部测量和导航。它直观地反应了观测者周围的空间关系,便于理解和使用。
注意:站心天向(Z轴)与赤道面相交不一定会经过球心。
比较:相较于用ECEF表示物体位置,ENU可以精简数据体量,比如倾斜摄影数据会有一个xml文件记录建模原点,这个建模原点很多就是ENU坐标系原点在地球上的位置,而此时倾斜摄影的顶点数据就是在ENU坐标系进行描述的,极大的减少了顶点数据的体量,便于进行空间计算。
ENU与ECEF的转换同样可参看 👇
链接:地心地固坐标系(ECEF)与站心坐标系(ENU)的转换-CSDN博客
示例
通常我们会给模型数据一个模糊的大致位置,但放入场景中有时必须给模型定位、微调位置以适应场景。
当模型定位的位置不是我们想要的位置,可以通过修改该3dtiles的矩阵来改变它,如平移:
/**基于本地的ENU坐标系的偏移,也就是垂直于地表向上为Z,东为X,北为Y
* @param tileset Cesium3DTileset
* @param dx x轴偏移量。单位:米
* @param dy y轴偏移量。单位:米
* @param dz z轴偏移量。单位:米
*/
function translate(tileset: Cesium3DTileset, dx: number, dy: number, dz: number) {
if (dx === 0 && dy === 0 && dz === 0) return
// 对于3DTileset,我们需要的结果是一个模型矩阵,那么平移就是计算一个世界坐标下的平移矩阵。
// 获取中心点
const origin = tileset.boundingSphere.center
// 以该点建立ENU坐标系
const toWorldMatrix = Transforms.eastNorthUpToFixedFrame(origin)
// 该坐标系下平移后的位置
const translatePosition = new Cartesian3(dx, dy, dz)
// 获取平移后位置的世界坐标
const worldPosition = Matrix4.multiplyByPoint(toWorldMatrix, translatePosition, new Cartesian3())
// 计算世界坐标下的各个平移量
const offset = Cartesian3.subtract(worldPosition, origin, new Cartesian3())
// 从世界坐标下的平移量计算世界坐标的平移矩阵
const translateMatrix = Matrix4.fromTranslation(offset)
// 应用平移矩阵。这里应该与原本的模型矩阵点乘,而不是直接赋值
tileset.modelMatrix = Matrix4.multiply(translateMatrix, tileset.modelMatrix, new Matrix4())
}
同理对模型做旋转操作:
/**基于本地的ENU坐标系的偏移,也就是垂直于地表向上为Z,东为X,北为Y
* @param tileset Cesium3DTileset
* @param rx X轴方向旋转角度(单位:度)
* @param ry Y轴方向旋转角度(单位:度)
* @param rz Z轴方向旋转角度(单位:度)
*/
function rotation(tileset: Cesium3DTileset, rx: number, ry: number, rz: number) {
const origin = tileset.boundingSphere.center;
// 以该点建立ENU坐标系
const m = Transforms.eastNorthUpToFixedFrame(origin);
//旋转
var mx = Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(rx));
var my = Cesium.Matrix3.fromRotationY(Cesium.Math.toRadians(ry));
var mz = Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(rz));
var rotationX = Cesium.Matrix4.fromRotationTranslation(mx);
var rotationY = Cesium.Matrix4.fromRotationTranslation(my);
var rotationZ = Cesium.Matrix4.fromRotationTranslation(mz);
// 旋转矩阵相乘
Cesium.Matrix4.multiply(m, rotationX, m);
Cesium.Matrix4.multiply(m, rotationY, m);
Cesium.Matrix4.multiply(m, rotationZ, m);
// 赋值给tileset
tileset._root.transform = m;
}