三维地球可视化从入门到进阶 – 坐标系统

本文为稀土掘金技术社区首发签约文章,14 天内禁止转载,14 天后未获授权禁止转载,侵权必究!

哈喽,大家好 我是 xy👨🏻‍💻。这篇文章是 Cesium 三维地球可视化从入门到进阶专栏的第四篇。

本节内容与 Cesium 本身无关,属于地理背景知识介绍,本节的写作目的在于让没有地理背景的读者,对地理坐标系投影坐标系有一个大致的了解,避免在 Cesium 应用开发中遇到相关概念时不知所措,但本节的内容并不专业也不够详尽,对这方面感兴趣的读者可以查阅其他资料。

背景

若想要通过一个数学模型来描述一个几何体,那么该几何体至少要是一个规则体。众所周知地球并不是一个标准的圆形球体,而是一个椭圆球体,并且地球表面并不是规则的,而是起伏不平的,如下图所示:

为了将地球拟合成一个规则的椭球体,前人做出了诸多设想,其中最典型的有”大地水准面“和”地球椭球面“这两种。

大地水准面

由于地球表面 72% 的面积为海水,假设当海水处于完全静止的平衡状态时,从海平面延伸到所有大陆下部,而与地球重力方向处处于正交的一个连续、闭合的水准面,这个水准面就是大地水准面,如下图所示:

地球椭球面

大地水准面能够从一定程度上解决地表起伏不平的问题,但平静的海水面和地球自然表面仍然存在着起伏,如下图所示:

因此需要选择一个旋转椭球体作为地球理想的模型,如下图所示:

椭球公式可以表示为:

  • a:长半轴(近似等于地球赤道半径);
  • b:极轴半径(近似等于南极/北极到赤道面的距离);
  • α:扁率。

地理坐标系

由于不同的国家地区可能会根据当地的地貌特征选取不同的参考椭球体,因此存在着很多不同的参考椭球体,这些参考椭球体分别对应着不同的地理坐标系:

坐标系名 北京 54(已弃用) 西安 80(已弃用) WGS84 CGCS2000
参考椭球体 Krasovsky_1940 Xian_1980(IAG75) WGS_84 CGCS2000
b 6356863.018773 6356755.288158 6356752.314245 6356752.314140
a 6378245.000000 6378140.000000 6378137.000000 6378137.000000
α 1/298.3 1/298.25722101 1/298.257223563 1/298.257222101
基准面 D_Beijing_1954 D_Xian_1980 D_WGS_1984 D_China_2000

使用经度和纬度用来表达地球面上点位的坐标:

  • 经度是地球上一个点位离本初子午线的南北方向走线以东或以西的度数。本初子午线的经度是 0°,地球上其它地点的经度是向东到 180° 或向西到 180°。
  • 纬度是指过椭球面上某点作法线,该点法线与赤道平面的线面角,其数值在 0 至 90 度之间。位于赤道以北的点的纬度叫北纬,记为 N;位于赤道以南的点的纬度称南纬,记为 S。

投影坐标系

使用地理坐标系描述球面某个点位时,使用的单位为度(°),不方便进行距离、方位、面积等参数的量算,把三维球面转换为二维平面更符合视觉心理,并易于进行距离、方位、面积等量算和各种空间分析。

为了将三维球体的表面转换成二维的平面,使用的方法就是投影,如下图中存在各种投影方法:

无论怎么投影都是存在着误差的,因为一个三维球体的表面是无法平整的展开成一个二维的平面的,展开过程中必然会产生褶皱和形变,如下图所示:

地图投影解决由球面向平面的转换,并不能保持平面与球面之间长度(距离)、角度(形状)、面积等方面完全不变。

常见的投影坐标系

  1. 墨卡托投影(Mercator projection)

墨卡托投影以其创立者荷兰地图学家墨卡托命名,其学名为“正轴等角圆柱投影”,假设地球被包围在圆柱体中,地球的赤道与圆柱相接触,然后再假想地球中心有一个光源,光源把地球表面上的图像投影到圆柱体上,再将圆柱体展开,展开后的地图就是墨卡托投影的世界地图,过程如下图所示:

  1. 高斯-克吕格投影(Gauss-Kruger)

高斯-克吕格投影以其创立者高斯和克吕格命名,其学名为“横轴墨卡托投影”,以中央经线与圆柱体相切,再进行投影,如下图所示:

  1. UTM 投影(Universal Transverse Mercator)

UTM 投影,其全称为“通用横轴墨卡托投影”,UTM 投影与高斯-克吕格投影十分相似,但圆柱体并不是与地球相切,而是穿过地球,如下图所示:

  1. 网络墨卡托投影(Web Mercator)

网络墨卡托投影由 Google Map 发明,借鉴于墨卡托投影,但在投影时并不是把地球当作一个椭球体,而是当作一个正球体。

坐标分类

在 Cesium 中共有 4 种坐标表示方法:

  • 2D 笛卡尔平面直角坐标系:Cartesian2,使用较少;
  • 3D 笛卡尔空间直角坐标系:Cartesian3,常用;
  • 4D 笛卡尔空间直角坐标系:Cartesian4,使用较少,下文不做介绍;
  • WGS84 经纬度坐标弧度制:Cartographic,常用。

3D 笛卡尔空间直角坐标系:Cartesian3

该坐标表示方法的示意图如下:

3D 笛卡尔空间直角坐标系的原点就是地球的中心点,通过xyz三个分量来表达某一个点的位置信息,创建该坐标的方法如下:

const cartesian3 = new Cesium.Cartesian3(x, y, z)

也可以使用Cesium.Cartesian3.fromDegrees方法直接传入经纬度坐标创建Cartesian3对象:

const cartesian3 = Cesium.Cartesian3.fromDegrees(longitude, latitude, height)

2D 笛卡尔平面直角坐标系:Cartesian2

该坐标表示方法的示意图如下:

与笛卡尔空间直角坐标系Cartesian3相比,2D 笛卡尔平面直角坐标系因为是平面坐标系,所以不包含 z 轴的分量,该坐标系通常用来表达屏幕坐标,创建该坐标的方法如下::

const cartesian2 = new Cesium.Cartesian2(x, y)

WGS84 经纬度坐标弧度制:Cartographic

Cesium 中默认使用的坐标系为 WGS84(World Geodetic System 1984)坐标系,坐标原点为地球质心,该坐标系统的示意图如下:

  • 经度:参考椭球面上某点的大地子午面与本初子午面间的两面角,东正西负。
  • 纬度:参考椭球面上某点的法线与赤道平面的夹角,北正南负。

在 Cesium 中没有直接使用经纬度实例化坐标对象的方法,只能通过Cartographic对象,提供经纬度的弧度制来实例化对象,但日常使用最多的坐标表示方法为经纬度坐标,因此需要进行坐标转换,将弧度转换为经纬度。

WGS84 弧度坐标

创建Cartographic坐标的方法如下:

const cartographic = new Cesium.Cartographic(longitude, latitude, height)

其中longitudelatitude为弧度,height为高度,单位为米。这里的经纬度是用弧度表示的,经纬度其实就是角度,弧度即角度对应弧长是半径的倍数。

WGS84 经纬度坐标

由于 Cesuim 中没有具体的经纬度对象来表达 WGS84 经纬度坐标系,要得到经纬度需要利用弧度来转换:

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

也可以使用Cesium.Cartographic.fromDegrees方法直接传入经纬度坐标创建Cartographic对象:

const cartographic = Cesium.Cartographic.fromDegrees(longitude, latitude, height)

坐标拾取

在 Cesium 中,想要获取某个对象(某个坐标点,或者某个实体),需要使用pick类方法,在 Cesium 中有很多与pick相关方法,但常见的方法及区别如下:

  • viewer.scene对象下的:
    • pickpick,根据窗口坐标,返回拾取顶端的具有primitive属性的一个对象Cesium3DTileFeature,该方法适用于拾取一个 3D tiles 对象,并修改该 3D tiles 对象的某个属性,如实现点击 3D tiles 高亮的功能;
    • drillpickdrillpick,根据窗口坐标,返回窗口坐标位置处的所有具有primitive属性的一个对象Cesium3DTileFeature集合,集合中的对象按其在场景中的视觉顺序(从前到后)排序。
    • pickPositionpickPosition,根据窗口坐标,从场景的深度缓冲区中拾取相应的位置,最后返回笛卡尔坐标,该方法拾取的高程可能不准确。
  • viewer.camera对象下的:
    • getPickRaygetPickRay,在相机的位置创建一个射线,取射线与窗口坐标的交点,以笛卡尔坐标的形式返回射线的起点和方向,适用于选取地表坐标,不包括模型、倾斜摄影等表面高度。
    • pickEllipsoidpickEllipsoid,返回椭圆球体表面的一个笛卡尔坐标,适用于裸球表面的选取,是基于数学模型的椭圆球体。
  • viewer.globe对象下的:
    • pickpick,返回射线与地表相交的一个笛卡尔坐标,适用于拾取有地形高程的点,但不包括模型、倾斜摄影等表面高度。

示例

viewer.scene.pick

viewer.scene.pick的使用方法如下,传入一个Cartesian2对象,若该窗口位置有 3D tiles 对象则返回一个Cesium3DTileFeature对象,否则返回undefined

const feature = viewer.scene.pick(cartesian2)

该方法常用于 3D tiles 的拾取,如改变拾取 3D tiles 对象的颜色:

// 定义一个点击事件
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
handler.setInputAction(function(movement) {
    const feature = viewer.scene.pick(movement.position);
    if (feature instanceof Cesium.Cesium3DTileFeature) {
        feature.color = Cesium.Color.RED; // 将拾取到的3D tiles颜色修改为红色
    }
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

viewer.scene.drillPick

viewer.scene.drillPick的使用方法如下,传入一个Cartesian2对象,返回该窗口位置下所有具有primitive属性的一个对象Cesium3DTileFeature集合:

const featureArray = viewer.scene.drillPick(cartesian2)

viewer.scene.pickPosition

viewer.scene.pickPosition的使用方法如下,传入一个Cartesian2对象,从场景的深度缓冲区中拾取相应的位置,最后返回笛卡尔Cartesian3坐标,该方法拾取的高程可能不准确。

// 定义一个事件,鼠标左键点击地球pickPosition拾取Cartesian3对象
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
handler.setInputAction((movement) => {
  const cartesian3 = viewer.scene.pickPosition(movement.position)
  console.log(cartesian3)
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)

viewer.camera.getPickRay

viewer.camera.getPickRay的使用方法如下,传入一个Cartesian2对象,在相机的位置创建一个射线 Ray,取射线与窗口坐标的交点,以笛卡尔坐标的形式返回射线的起点和方向。

// 定义一个事件,鼠标左键点击地球getPickRay拾取Cartesian3对象
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
handler.setInputAction((movement) => {
  const ray = viewer.camera.getPickRay(movement.position)
  console.log(ray)
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)

viewer.camera.pickEllipsoid

viewer.camera.pickEllipsoid的使用方法如下,传入一个Cartesian2对象和一个Ellipsoid对象,返回椭圆球体表面的一个笛卡尔坐标,适用于裸球表面的选取,是基于数学模型的椭圆球体。

// 定义一个事件,鼠标左键点击地球getPickRay拾取Cartesian3对象
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
handler.setInputAction((movement) => {
  const ellipsoid = viewer.scene.globe.ellipsoid
  const cartesian3 = viewer.camera.pickEllipsoid(movement.position, ellipsoid)
  console.log(cartesian3)
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)

viewer.globe.pick

viewer.globe.pick的使用方法如下,提供一个Ray对象和Scene对象,返回射线与地表相交的一个笛卡尔坐标:

const cartesian3 = viewer.scene.globe.pick(ray, viewer.scene)

该方法适用于拾取有地形高程的点,但不包括模型、倾斜摄影等表面高度。

// 定义一个事件,鼠标左键点击地球pick拾取Cartesian3对象
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
handler.setInputAction((movement) => {
  const ray = viewer.camera.getPickRay(movement.position)
  const cartesian3 = viewer.scene.globe.pick(ray, viewer.scene)
  console.log(cartesian3)
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)

坐标转换

弧度与经纬度转换

// 经纬度转弧度
let radians = Cesium.Math.toRadians(degrees)
// 弧度转经纬度
let degress = Cesium.Math.toDegrees(radians)

笛卡尔三维转二维:Cartesian3Cartesian2

const cartesian2 = viewer.scene.cartesianToCanvasCoordinates(cartesian3) // Cartesian3 转 Cartesian2
// 或
const cartesian2 = Cesium.SceneTransforms.wgs84ToWindowCoordinates(viewer.scene, cartesian3) // Cartesian3 转 Cartesian2

笛卡尔二维转三维:Cartesian2Cartesian3

根据上一节“Cesium 位置拾取”中的内容可知,以下三种方法可以传入一个cartesian2对象参数再将其转换为cartesian3对象:

  • viewer.camera.pickEllipsoid
  • viewer.scene.pickPosition
  • viewer.globe.pick

转换方法如下:

// viewer.camera.pickEllipsoid
const cartesain3 = viewer.camera.pickEllipsoid(cartesian2) // Cartesian2 转 Cartesian3

// viewer.scene.pickPosition
const cartesian3 = viewer.scene.pickPosition(cartesian2) // Cartesian2 转 Cartesian3

// viewer.globe.pick
const ray = viewer.camera.getPickRay(cartesian2) // 取相机与屏幕的射线
const cartesian3 = globe.pick(ray, viewer.scene) // Cartesian2 转 Cartesian3

笛卡尔空间直角坐标与 WGS84 转换:Cartesian3Cartographic转换

  1. Cartesian3 转 Cartographic
const cartographic = Cesium.Cartographic.fromCartesian(cartesian3) // Cartesian3 转 Cartographic
// 或
const cartographic = viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian3) // Cartesian3 转 Cartographic
  1. Cartographic 转 Cartesian3
const cartesian3 = Cesium.Ellipsoid.WGS84.cartographicToCartesian(cartographic) // Cartographic 转 Cartesian3

笛卡尔空间直角坐标转换:创建Cartesian3的方法

直接通过x,y,z创建笛卡尔空间直角坐标:

const cartesian3 = new Cesium.Cartesian3(x, y, z)

使用度单位的经度纬度高度longitude,latitude,height转换为x,y,z

const cartesian3 = Cesium.Cartesian3.fromDegrees(longitude, latitude, height) // longitude, latitude为度°

使用弧度制的经度纬度高度longitude, latitude, height转换为x,y,z

const cartesian3 = Cesium.Cartesian3.fromDegrees(longitude, latitude, height) // longitude, latitude为弧度

WGS84 经纬度坐标转换:创建Cartographic的方法

直接通过弧度制的经度纬度高度创建:

const cartographic = new Cesium.Cartographic(longitude, latitude, height) // longitude, latitude为弧度
// 或
const cartographic = Cesium.Cartographic.fromRadians(longitude, latitude, height) // longitude, latitude为弧度

使用度单位的经度纬度高度创建:

const cartographic = Cesium.Cartographic.fromDegrees(longitude, latitude, height) // longitude, latitude为度°

🎯 这篇文章是 Cesium 三维地球可视化从入门到进阶 专栏的第四篇文章,主要是对让没有地理背景的读者,对地理坐标系投影坐标系有一个大致的了解,避免在 Cesium 应用开发中遇到相关概念时不知所措

🎯 在后续的文章中, 将会分享更多实践案例,如果你也对 三维可视化比较感兴趣的话,欢迎关注我一起学习

🎯 Github 仓库地址:https://github.com/xushanpei/Cesium_Study_Cases

写在最后

公众号前端开发爱好者 专注分享 web 前端相关技术文章视频教程资源、热点资讯等,如果喜欢我的分享,给 🐟🐟 点一个 👍 或者 ➕关注 都是对我最大的支持。

大家好,我 xy,是一名前端 🤫 爱好:瞎折腾

如果你也是一名瞎折腾的前端欢迎加我微信交流哦…

🤫 一定要点我

© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容