前言
补一篇Cesium坐标系知识,草草开稿竟把GIS中重中之重的坐标系抛之脑后,实在草率,各位道友招待不周!
坐标系是一切花里胡哨的基础与开端,倘若坐标系不能明确展示、计算、分析结果将毫无意义,由于Cesium起源于国外团队,其坐标系与国内尚有差别,需要拿出来系统的剖解一下。以下内容均个人浅见,我将以我的方式和大伙聊聊,如有谬误,还望道友们指正!
一、坐标系基础知识
1.三维地理坐标系
简单来说,数据采集时使用的最原始频次最高的坐标系,宏观描述目标点在地球位置的坐标系。可以这么理解,测绘人员用全站仪、水准仪测量地球表面的原始数据就是用这个来描述的,经度、纬度、高程。描述单位为度(degree),米(m),例如A(114°,29°,100000m)(实际采集数据记录时单位不写)。
2.三维投影坐标系
先给结论:
投影坐标系 = 地理坐标系 + 投影方式
这个三维里面用的不多,但还是介绍一下。投影属于地理坐标系的一个属性,这么说吧,一个规范的图层它一定有地理坐标系,但它不一定有投影,但一个投影坐标系必然对应着一个地理坐标系。例如A图层的坐标系是WGS84地理坐标系(4326),将 WGS84 经纬度坐标系通过莫卡托投影方式进行投影后会得到 Web Mercator投影坐标系(3857)。
这个投影你可以理解成在球体内部中心放个光源,在球体外部放个外切圆柱,光影映射到圆柱侧面后将圆柱测面展开得到,其实莫卡托是一种数学映射模型,通过映射公式使得球面上每点的经纬在平面上都能找到一一对应的点。
以这个思路去推演,每一个地理坐标系有0个或多个投影坐标系,关键取决于你需不需要投影坐标系以及你用什么投影方式(当然与地理位置经度带也有关,这里不展开说),中国使用的是高斯克里格投影,这种投影对于我们大中国、俄罗斯这种狭长大陆国家比较适用。
3.三维笛卡尔坐标系(也称世界坐标系)
Cesium、Three.js 等其它三维库都与计算机图形学紧密相关,Cesium中所有运算都在该坐标系下,也被称为三维笛卡尔坐标系Cartesian3。拿大白话来说,我去计算三维平移、翻转、矩阵,你总不能让我拿着经纬度算吧?经纬度单位在数学界很不受待见,不然弧度概念就不会被发明辽。
4.二维笛卡尔坐标系(也称屏幕坐标系)
无论多绚酷的效果,咱用户也是通过屏幕去看,通过屏幕去交互,用户交互输入方式全部与屏幕坐标系锁死,你秀任你秀,我只有二尺小屏幕。
屏幕坐标系 以右上角为原点,单位为像素。
这里有个误区啊,很多人以为原点是屏幕的左上角,其实不是的,在Cesium里面,原点为实例化Cesim的dom元素的左上角,网上很多说的都是错的。
二、Cesium中坐标系
进入正题,现在将上述的坐标系依次在Cesium具象化。
1.经纬度地理坐标系
Cesium对你抛出了一个错误,表示查无此类。Cesium中没有定义经纬度地理坐标系的对象,需要通过其它去转换,转换下面会展开讲。
2.弧度地理坐标系(Cartographic)
引入弧度是为了进行数值统一,更方便进行数学计算。
const cartographic = new Cesium.Cartographic(lon,lat,height)(lon,lat单位度,height单位米)
3.三维笛卡尔坐标系(Cartesian3
)
const cartesian3 = new Cesium.Cartesian3(x, y, z)(x,y单位弧度,z单位米)
4.二维笛卡尔坐标系(Cartesian2)
const cartesian2 = new Cesium.Cartesian2(x,y)(x,y单位像素)
三、Cesium坐标转换
1. 经纬度地理坐标转弧度地理坐标
引入弧度是为了进行数值统一,更方便进行计算,只需记得180°=π (单位rad)即可,那么会有下面的结论:
度到弧度:L = (θ * π) / 180 单位(rad)
弧度到度:θ = (L * 180) / π 单位(°)
为了方面起见Cesium将换算过程封装成了方法:
弧度到经度:const degrees = Cesium.Math.toRadians(degrees)
经度转弧度:const radians = Cesium.Math.toDegrees(radians)
2.经纬度地理坐标与Cartesian3坐标互转
2.1 经纬度地理坐标转Cartesian3坐标
这个场景是采集到的真实世界数据放到Cesium球体中使用,使用频次很高,还有其它的说法,例如地理坐标系转世界坐标系,经纬度转C3等等都是指这个。
const cartesian3 = new Cesium.Cartesian3.fromDegrees(lon, lat, height);
const cartesian3 = new Cesium.Cartesian3.fromDegrees(114, 29, 1000);
2.2 Cartesian3坐标转经纬度地理坐标
Cesium中的点转回经纬度,由于Cesium中没有经纬度地理坐标系的类,所以不能像上面一步完成,需要借助弧度(Cartographic)进行中转。
第一步:世界坐标转地理弧度坐标
const cartographic = new Cesium.Cartographic.fromCartesian
第二步:将地理弧度坐标转地理经纬度坐标系
const lon = Cesium.Math.toDegrees(cartographic.longidute)
const lat = Cesium.Math.toDegrees(cartographic.latgitude)
const height = cartographic.height
3.Cartesian2与Cartesian3互转
3.1 Cartesian2转Cartesian3
该转换使用频次很高,是个重点,场景是用书鼠标拾取一个操作点后,该点产生的坐标是屏幕坐标(像素点Cartesian2坐标),要进行计算分析的话一定得先转成Cartesian3。
const cartesian3 = viewer.scene.globe.pick(viewer.camera.getPickRay(cartesian2),viewer.scene);
检验代码:
//监听鼠标左键点击事件,捕捉event上的位置信息const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);handler.setInputAction(event => {//type为Cartesian2const windowPosition = event.position;console.log("windowPosition", windowPosition);const cartesian3_pick = viewer.scene.globe.pick(viewer.camera.getPickRay(windowPosition),viewer.scene);console.log("cartesian3_pick", cartesian3_pick);}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
3.2 Cartesian3转Cartesian2
const C2 =Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene, C3)
再将3.1中获取的C3转回C2即可检验该方法。
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);handler.setInputAction(event => {//type为Cartesian2const windowPosition = event.position;console.log("windowPosition", windowPosition);const cartesian3_pick = viewer.scene.globe.pick(viewer.camera.getPickRay(windowPosition),viewer.scene);console.log("cartesian3_pick", cartesian3_pick);// 再把C3转回C2let cartesian2 = Cesium.SceneTransforms.wgs84ToWindowCoordinates(viewer.scene,cartesian3_pick);console.log("cartesian2", cartesian2);}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
四、Cesium初始化坐标系
针对不同项目,在初始化球体时就要设置好基准的坐标系,配置项在Viewer对象上:
(tip:无论是二维还是三维里面初始化都会有projection、mapProjection类的配置项用于设置坐标系,其实这些名称以及文档解释是不合理的,最贴切的应该改成coordinate或者其它的,再不济也不能把它解释称地图投影,你解释称坐标投影都可以。解释成地图投影意味着这个配置项只能设置投影坐标系,就没有意义了)
//初始化Viewer设置坐标系new Cesium.Viewer("cesiumContainer", {mapProjection:new Cesium.GeographicProjection(Cesium.Ellipsoid.WGS84)});//Cesium.Ellipsoid.WGS84是Cesium默认的坐标系,不传时就相当于上述这样设置
那在我国,要使用4490坐标系咋办?关于Cesium初始化设置球体坐标系为4490后面单独开一个文章来说,比较麻烦,涉及到改源码。
五、SuperMap iClient for Cesium设置适合我国坐标系
1.设置坐标系
iClient for Cesium毕竟是国产化封装,必然对我国坐标系进行了适配,对一些坐标进行了底层注入,在iClient for Cesium下设置CGCS2000、西安80都很便捷。
设置代码:
//设置4490坐标系new Cesium.Viewer("cesiumContainer", {mapProjection:new Cesium.GeographicProjection(Cesium.Ellipsoid.CGCS2000)});//设置西安80坐标系new Cesium.Viewer("cesiumContainer", {mapProjection:new Cesium.GeographicProjection(Cesium.Ellipsoid.XIAN80)});
2.检验是否设置成功
检验标准在globe上的ellipsoid属性,设置成功后,该属性会和下图标红的椭球体参数对应上。
// 获取当前坐标系var ellipsoid = viewer.scene.globe.ellipsoid;console.log("ellipsoid", ellipsoid);
大家可以尝试一下,这里有一个比较有趣的点,就是默认的球体时圆球体而不是椭球体,不设置坐标系直接打印ellipsoid将会得到x=y=z=6378137。自己动手进行椭球化后才能和WGS84的椭球参数对上。
// 将WGS84椭球化,默认是x=y=z=6378137mCesium.Ellipsoid.WGS84 = Object.freeze(new Cesium.Ellipsoid(6378137.0, 6378137.0, 6356752.3142451793));
设置完4490之后的椭球参数对比:
收工,下次见!