LastPoem / Restart

2 stars 0 forks source link

Cesium 坐标系统 #24

Open LastPoem opened 4 years ago

LastPoem commented 4 years ago

Cesium中主要包括了以下几种坐标系统:

1.Cartesian3 笛卡尔空间直角坐标系。又称世界坐标。 new Cesium.Cartesian3(x,y,z) 表示以地球椭球体中心为原点的空间直角坐标系中的一点

2.Cartographic 由经纬度和高程确定某一点。经纬度都是由弧度表示。 new Cesium.Cartographic(longitude, latitude, height)

Cesium中没有具体的直接以经纬度表达的对象,需要转换为弧度或空间直角坐标来使用。

相互转换:

经纬度转笛卡尔空间坐标: 可以直接转换: Cesium.Cartesian3.fromDegrees(longitude, latitude, height, elliposoid, result) 或者先把经纬度转为弧度,再由弧度转笛卡尔

let ellipsoid = viewer.scene.globe.ellipsoid;
let cartographic = Cesium.Cartographic.fromDegrees(lng,lat,alt);
let cartesian3 = ellipsoid.cartographicToCartesian(cartographic)

世界坐标转换为经纬度

var ellipsoid=viewer.scene.globe.ellipsoid;
var cartesian3=new Cesium.cartesian3(x,y,z);
var cartographic=ellipsoid.cartesianToCartographic(cartesian3);
var lat=Cesium.Math.toDegrees(cartograhphic.latitude);
var lng=Cesium.Math.toDegrees(cartograhpinc.longitude);
var alt=cartographic.height;

同理,得到弧度还可以用 Cartographic.fromCartesian

弧度和经纬度 经纬度转弧度: Cesium.CesiumMath.toRadians(degrees)

弧度转经纬度: Cesium.CesiumMath.toDegrees(radians)

屏幕坐标和世界坐标相互转换 屏幕坐标转世界坐标:

var pick1= new Cesium.Cartesian2(0,0);
var cartesian = viewer.scene.globe.pick(viewer.camera.getPickRay(pick1),viewer.scene);

这里的屏幕坐标一定是在地球上,否则会返回undefined

世界坐标转屏幕坐标: Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene, Cartesian3);

结果是Cartesian2对象。

火星坐标,84坐标,百度地图坐标相互转换

//定义一些常量
var x_PI = 3.14159265358979324 * 3000.0 / 180.0;
var PI = 3.1415926535897932384626;
var a = 6378245.0;
var ee = 0.00669342162296594323;

/**
 * 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换
 * 即 百度 转 谷歌、高德
 * @param bd_lon
 * @param bd_lat
 * @returns {*[]}
 */
function bd09togcj02(bd_lon, bd_lat) {
    var x_pi = 3.14159265358979324 * 3000.0 / 180.0;
    var x = bd_lon - 0.0065;
    var y = bd_lat - 0.006;
    var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
    var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
    var gg_lng = z * Math.cos(theta);
    var gg_lat = z * Math.sin(theta);
    return [gg_lng, gg_lat]
}

/**
 * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换
 * 即谷歌、高德 转 百度
 * @param lng
 * @param lat
 * @returns {*[]}
 */
function gcj02tobd09(lng, lat) {
    var z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_PI);
    var theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_PI);
    var bd_lng = z * Math.cos(theta) + 0.0065;
    var bd_lat = z * Math.sin(theta) + 0.006;
    return [bd_lng, bd_lat]
}

/**
 * WGS84转GCj02
 * @param lng
 * @param lat
 * @returns {*[]}
 */
function wgs84togcj02(lng, lat) {
    if (out_of_china(lng, lat)) {
        return [lng, lat]
    }
    else {
        var dlat = transformlat(lng - 105.0, lat - 35.0);
        var dlng = transformlng(lng - 105.0, lat - 35.0);
        var radlat = lat / 180.0 * PI;
        var magic = Math.sin(radlat);
        magic = 1 - ee * magic * magic;
        var sqrtmagic = Math.sqrt(magic);
        dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
        dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
        var mglat = lat + dlat;
        var mglng = lng + dlng;
        return [mglng, mglat]
    }
}

/**
 * GCJ02 转换为 WGS84
 * @param lng
 * @param lat
 * @returns {*[]}
 */
function gcj02towgs84(lng, lat) {
    if (out_of_china(lng, lat)) {
        return [lng, lat]
    }
    else {
        var dlat = transformlat(lng - 105.0, lat - 35.0);
        var dlng = transformlng(lng - 105.0, lat - 35.0);
        var radlat = lat / 180.0 * PI;
        var magic = Math.sin(radlat);
        magic = 1 - ee * magic * magic;
        var sqrtmagic = Math.sqrt(magic);
        dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
        dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
        mglat = lat + dlat;
        mglng = lng + dlng;
        return [lng * 2 - mglng, lat * 2 - mglat]
    }
}

function transformlat(lng, lat) {
    var ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
    ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
    ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0;
    ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0;
    return ret
}

function transformlng(lng, lat) {
    var ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
    ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
    ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0;
    ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0;
    return ret
}

/**
 * 判断是否在国内,不在国内则不做偏移
 * @param lng
 * @param lat
 * @returns {boolean}
 */
function out_of_china(lng, lat) {
    return (lng < 72.004 || lng > 137.8347) || ((lat < 0.8293 || lat > 55.8271) || false);
}
LastPoem commented 4 years ago

大地水准面 (geoid) 大地水准面 是海洋表面在排除风力、潮汐等其它影响,只考虑重力和自转影响下的形状,这个形状延伸过陆地,生成一个密闭的曲面。虽然我们通常说地球是一个球体或者椭球体,但是由于地球引力分布不均(因为密度不同等原因),大地水准面是一个不规则的光滑曲面。虽然不规则,但是可以近似地表示为一个椭球体,这个椭球体被称为 参考椭球体(Reference ellipsoid)。大地水准面相对于参考椭球体的高度被称为 Undulation of the geoid 。这个波动并不是非常大,最高在冰岛为 85m,最低在印度南部为 −106 m,一共不到 200m。

参考椭球体(Reference ellipsoid) 参考椭球体(Reference ellipsoid) 是一个数学上定义的地球表面,它近似于大地水准面。因为是几何模型,可以用长半轴、短半轴和扁率来确定它。我们通常所说的经度、纬度以及高度都以此为基础。 一方面,我们对地球形状的测量随着时间迁移而不断精确,另一方面,因为大地水准面并不规则,地球上不同地区往往需要使用不同的参考椭球体,来尽可能适合当地的大地水准面。历史上出现了很多不同的参考椭球体,很多还仍然在使用中。国内过去使用过“北京54”和“西安80”两个坐标系,其中“北京54”使用的是克拉索夫斯基(Krasovsky)1940 的参考椭球,“西安80”使用的是1975年国际大地测量与地球物理联合会第16届大会推荐的参考椭球。当前世界范围内更普遍使用的是WGS所定义的参考椭球。

坐标系(coordinate system) 有了参考椭球体这样的几何模型后,就可以定义坐标系来进行描述位置,测量距离等操作,使用相同的坐标系,可以保证同样坐标下的位置是相同的,同样的测量得到的结果也是相同的。通常有两种坐标系 地理坐标系(geographic coordinate systems) 和 投影坐标系(projected coordinate systems)。

地理坐标系(Geographic coordinate system) 地理坐标系一般是指由经度、纬度和高度组成的坐标系,能够标示地球上的任何一个位置。前面提到了,不同地区可能会使用不同的参考椭球体,即使是使用相同的椭球体,也可能会为了让椭球体更好地吻合当地的大地水准面,而调整椭球体的方位,甚至大小。这就需要使用不同的大地测量系统(Geodetic datum)来标识。因此,对于地球上某一个位置来说,使用不同的测量系统,得到的坐标是不一样的。我们在处理地理数据时,必须先确认数据所用的测量系统。事实上,随着我们对地球形状测量的越来越精确,北美使用的 NAD83 基准和欧洲使用的 ETRS89 基准,与 WGS 84 基准是基本一致的,甚至我国的 CGCS2000 与 WGS84 之间的差异也是非常小的。但是差异非常小,不代表完全一致,以 NAD83 为例,因为它要保证北美地区的恒定,所以它与 WGS84 之间的差异在不断变化,对于美国大部分地区来说,每年有1-2cm的差异。

投影坐标系(Projected coordinate systems) 地理坐标系是三维的,我们要在地图或者屏幕上显示就需要转化为二维,这被称为 投影(Map projection)。显而易见的是,从三维到二维的转化,必然会导致变形和失真,失真是不可避免的,但是不同投影下会有不同的失真,这让我们可以有得选择。常用的投影有 等距矩形投影(Platte Carre) 和 墨卡托投影(Mercator)

等距矩形投影和墨卡托投影的变形

左图表示地球球面上大小相同的圆形,右上为墨卡托投影,投影后仍然是圆形,但是在高纬度时物体被严重放大了。右下为等距投影,物体的大小变化不是那么明显,但是图像被拉长了。Platte Carre 投影因为在投影上有扭曲,并不适合于航海等活动,但是因为坐标与像素之间的对应关系十分简单,非常适合于栅格图的展示,Platte Carre 投影是很多GIS 软件的默认投影。 需要注意的是,对于墨卡托投影来说,越到高纬度,大小扭曲越严重,到两极会被放到无限大,所以,墨卡托投影无法显示极地地区。下图来自 维基百科,可以看到墨卡托投影下每个国家的大小和实际大小的差异。但是 conformality(正形性) 和 straight rhumb lines 这两个特点,让它非常适合于航海导航。

实际大小与墨卡托投影变形后的大小

对于开发人员来说,最常用的是EPSG:4326 和 EPSG:3857。实际上这是坐标系统的代号。国际上,每个坐标系统都会被分配一个EPSG代码。EPSG4326是GPS使用的WGS84坐标系统的代码。由于GPS是基于WGS84的,因此我们得到的坐标数据都是WGS84的,在存储数据时一般也按WGS84存储。 EPSG3857是球体墨卡托(伪墨卡托)投影,把坐标投影到了球体上。另外,伪墨卡托投影还切掉了南北85.051129°纬度以上的地区,以保证整个投影是正方形的。因为墨卡托投影等正形性的特点,在不同层级的图层上物体的形状保持不变,一个正方形可以不断被划分为更多更小的正方形以显示更清晰的细节。很明显,伪墨卡托坐标系是非常显示数据,但是不适合存储数据的,通常我们使用 WGS84 存储数据,使用伪墨卡托显示数据。