Cesium坐标转换

2,595 阅读8分钟

一、地理中的坐标系

坐标系主要分为两大类:(1)地理坐标系、(2)投影坐标系。

1.1 地理坐标系

地理坐标系(Geographic Coordinate System,GCS),使用三维球面来定义地球上的位置,用经纬度来确定球面上的点位。可根据其所采用的参考椭球体参数求得点的绝对高程值。

大地坐标系是大地测量中以参考椭球面为基准面建立起来的坐标系,也就是地理坐标系。地面点位置使用大地经度L、大地维度B和大地高度H表示,单位为度分秒。某地的大地经度是本初子午线与本地子午线的夹角。某地的大地纬度是将地球表面点投射到地球椭球体上投射点防线与赤道平面的夹角为该点的大地纬度。

地理坐标根据其参考的原点位置不同又划分为: (1)地心坐标系:地心坐标系的原点与地球质心重合,例如:WGS84,国家2000坐标。 (2)参心坐标系:参心坐标系的原点与某一地区或国家所采用的参考椭球中心重合,要求在该方位内椭球和大地水准面最佳密合,而原点不要求定在地球中心。例如:北京54,西安80,地方坐标。

1.2 投影坐标系

投影坐标系(Projected Coordinate System,PCS),从地球近似椭球投影等到,是平面坐标系,测量单位通常为米。它对应于某个地理坐标系。

二、常见坐标系

参考参数WGS坐标系BJZ54坐标系西安80坐标系CGCS2000坐标系
椭球体WGS84椭球克拉索夫斯基椭球IAG椭球CGCS2000坐标系
长半径6378137m6378245ma=6378140ma=6378137m
短半径6356752.3142m6356863.0188mb=6356755m6356752.314m
第一偏心率平方 第二偏心率平方E2=0.00669437999013 e2=0.006739496742227e2=0.006693421622e2=0.00669438499959e2=0.00669438002290
扁率1/298.2572235631/298.31/298.257221011/298.257222101

三、Cesium中的几种坐标

cesium中常用的两种坐标系:WGS84坐标系和笛卡尔空间直角坐标系。 在cesium中,常见的坐标类型有:平面坐标、世界坐标、经纬度坐标、弧度坐标以及屏幕坐标。 WGS84坐标系常用来指明一个地点。WGS84坐标系包括:WGS84经纬度坐标系和WGS84弧度坐标系。 笛卡尔空间坐标系常用来做一些空间位置变换(平移、旋转、缩放)。笛卡尔坐标系包括,笛卡尔空间直角坐标系(Cartesian3)、平面坐标系(Cartesian2)、4D笛卡尔坐标系(Cartesian4)。 Cesium中的坐标单位,默认是弧度制。

3.1 笛卡尔平面坐标

笛卡尔平面坐标(Cartesian2) new Cesium.Cartesian2(x,y)

3.2 笛卡尔空间直角坐标(又称世界坐标)

笛卡尔空间直角坐标(Cartesian3) new Cesium.Cartesian3(x,y,z) 笛卡尔空间直角坐标系

笛卡空间直角坐标系.png

原点:椭球中心 x轴:正方向指向中央经线 y轴:正方向指向东经90度 xz平面:中央经线和180度经线组成的平面

3.3 经纬度

经纬度(longitude,latitude) 经度:参考椭球面上某点的大地子午面与本初子午面间的两面角。东正西负。 纬度:参考椭球面上某点的法线与赤道平面的夹角。北正南负。 Cesium中没有具体的经纬度对象,要得到经纬度需要先计算为弧度,再进行转换。

3.4 地理坐标(弧度)

地理坐标(Cartographic),单位为弧度 new Cesium.Cartographic(longitude,latitude,height)

注:这里的经纬度是用弧度表示的,经纬度其实就是角度。弧度即角度对应弧长是半径的倍数。 角度转弧度: π / 180 × 角度 弧度变角度: 180 / π × 弧度

3.5 屏幕坐标

屏幕坐标,即二维笛卡尔平面坐标。是对屏幕像素(分辨率的位置的描述,左下角为(0,0),右上角为(screen.width,screen.height)

四、Cesium中的坐标转换

4.1 经纬度 <-> 弧度

经纬度转弧度

function degrees2Radian(point) {
    // 方法一
    cartographic = Cesium.Cartographic.fromDegrees(
      point.lng,
      point.lat,
      point.height
    );
    // console.log("经纬度转为弧度 一", point, cartographic);
    // 方法二:
    const lng=Cesium.Math.toRadians(point.lng)
    const lat=Cesium.Math.toRadians(point.lat)
    // console.log("经纬度转为弧度 二", point, {lng,lat});
    // 方法三
    // 将角度转弧度 π / 180 × 角度
    const pai=3.141592654
    const lng1=pai/180*point.lng;
    const lat1=pai/180*point.lat;
    // fromRadians传入的是弧度
    const cartographic1=Cesium.Cartographic.fromRadians(lng1,lat1,point.height);
    // console.log("经纬度转为弧度 三", point, cartographic1);
    return cartographic;
  }

弧度转经纬度

  function radian2Degrees(point) {
    let lng = Cesium.Math.toDegrees(point.longitude);
    let lat = Cesium.Math.toDegrees(point.latitude);
    // console.log("弧度转为经纬度", point, {lng,lat,height:point.height});
  }

4.2 经纬度 <-> 世界坐标

经纬度转世界坐标

function TansferLatToX1(point) {
    //直接转换
    ellipsoid = viewer.scene.globe.ellipsoid;
    let cartesian = Cesium.Cartesian3.fromDegrees(
      point.lng,
      point.lat,
      point.height,
      ellipsoid  //直接转换的区别在这里
    );
    //先转成弧度再转换
    let cartographic3 = Cesium.Cartographic.fromDegrees(
      point.lng,
      point.lat,
      point.height
    );
    const cartesian1 = ellipsoid.cartographicToCartesian(cartographic3);
    console.log("经纬度转世界坐标", point, cartesian, cartesian1);
    return cartographic;
  };

世界坐标转经纬度

    function TansfeX12Lat(cartesian3) {
    // 方法一 :
    const ellipsoid = viewer.scene.globe.ellipsoid;
    var cartographic = ellipsoid.cartesianToCartographic(cartesian3);
    var lat = Cesium.Math.toDegrees(cartographic.latitude);
    var lng = Cesium.Math.toDegrees(cartographic.longitude);
    var height = cartographic.height; //获取到的是椭球上方的高度不是对应的高程。如果想获取高程的话可以根据经纬度获取对应点的高程
    console.log("世界坐标转经纬度标", cartesian3, { lng: lng, lat: lat, height: height });
    // 方法二: 
    var cartographic = Cesium.Cartographic.fromCartesian(cartesian3);
    var lat1 = Cesium.Math.toDegrees(cartographic.latitude);
    var lng1 = Cesium.Math.toDegrees(cartographic.longitude);
    var height1 = cartographic.height;
    console.log("世界坐标转经纬度标", cartesian3, { lng: lng1, lat: lat1, height: height1 });

    return { lng: lng, lat: lat, height: height };
  };

4.3 经纬度 <-> CGS2000

经纬度转大地2000

 /**
  * _lon: 表示中央子午线,根据实际情况配置
  * 这里是102E
  */
  function WGS84ToCGCS2000(lon, lat, _lon) {
    let iPI = 0.0174532925199433; //3.1415926575898/180.0
    let a = 6378137.0; //长半轴
    let f = 1 / 298.257222101; //扁率
    let _lon2rad = _lon * iPI; //中央子午线转为弧度
    let lon2rad = lon * iPI; //经度转为弧度
    let lat2rad = lat * iPI; //纬度转为弧度
    //计算椭圆偏心率
    const e = 2 * f - f * f;
    const ee = e * (1 - e);
    //计算曲率半径
    const NN = a / Math.sqrt(1.0 - e * Math.sin(lat2rad) * Math.sin(lat2rad));
    const T = Math.tan(lat2rad) * Math.tan(lat2rad);
    const C = ee * Math.cos(lat2rad) * Math.cos(lat2rad);
    const A = (lon2rad - _lon2rad) * Math.cos(lat2rad);
    //计算子午线弧长
    const M =
      a *
      ((1 - e / 4 - (3 * e * e) / 64 - (5 * Math.pow(e, 3)) / 256) * lat2rad -
        ((3 * e) / 8 + (3 * e * e) / 32 + (45 * Math.pow(e, 3)) / 1024) *
        Math.sin(2 * lat2rad) +
        ((15 * e * e) / 256 + (45 * Math.pow(e, 3)) / 1024) *
        Math.sin(4 * lat2rad) -
        ((35 * Math.pow(e, 3)) / 3072) * Math.sin(6 * lat2rad));
    let xval =
      NN *
      (A +
        ((1 - T + C) * Math.pow(A, 3)) / 6 +
        ((5 - 18 * T + T * T + 72 * C - 58 * ee) * Math.pow(A, 5)) / 120);
    const yval =
      M +
      NN *
      Math.tan(lat2rad) *
      ((A * A) / 2 +
        ((5 - T + 9 * C + 4 * C * C) * Math.pow(A, 4)) / 24 +
        ((61 - 58 * T + T * T + 600 * C - 330 * ee) * Math.pow(A, 6)) /
        720);
    const X0 = 500000;
    const Y0 = 0;
    xval = xval + X0;
    console.log('经纬度坐标转大地2000坐标',[lon, lat],[xval, yval])
    return [xval, yval];
  }

大地2000转经纬度

   function CGCS2000ToWGS84(X, Y, _lon) {
    let L0 = 102; //中央子午线需根据实际情况设置
    let lat, lon;
    X -= 500000;
    let result = [];
    let iPI = 0.0174532925199433; //pi/180
    let a = 6378137.0; //长半轴 m
    let b = 6356752.31414; //短半轴 m
    let f = 1 / 298.257222101; //扁率 a-b/a
    let e = 0.0818191910428; //第一偏心率 Math.sqrt(5)
    let ee = Math.sqrt(a * a - b * b) / b; //第二偏心率
    let bf = 0; //底点纬度
    let a0 =
      1 +
      (3 * e * e) / 4 +
      (45 * Math.pow(e, 4)) / 64 +
      (175 * Math.pow(e, 6)) / 256 +
      (11025 * Math.pow(e, 8)) / 16384 +
      (43659 * Math.pow(e, 10)) / 65536;
    let b0 = Y / (a * (1 - e * e) * a0);
    let c1 =
      (3 * e * e) / 8 +
      (3 * Math.pow(e, 4)) / 16 +
      (213 * Math.pow(e, 6)) / 2048 +
      (255 * Math.pow(e, 8)) / 4096;
    let c2 =
      (21 * Math.pow(e, 4)) / 256 +
      (21 * Math.pow(e, 6)) / 256 +
      (533 * Math.pow(e, 8)) / 8192;
    let c3 = (151 * Math.pow(e, 8)) / 6144 + (151 * Math.pow(e, 8)) / 4096;
    let c4 = (1097 * Math.pow(e, 8)) / 131072;
    bf =
      b0 +
      c1 * Math.sin(2 * b0) +
      c2 * Math.sin(4 * b0) +
      c3 * Math.sin(6 * b0) +
      c4 * Math.sin(8 * b0); // bf =b0+c1*sin2b0 + c2*sin4b0 + c3*sin6b0 +c4*sin8b0 +...
    let tf = Math.tan(bf);
    let n2 = ee * ee * Math.cos(bf) * Math.cos(bf); //第二偏心率平方成bf余弦平方
    let c = (a * a) / b;
    let v = Math.sqrt(1 + ee * ee * Math.cos(bf) * Math.cos(bf));
    let mf = c / (v * v * v); //子午圈半径
    let nf = c / v; //卯酉圈半径
    //纬度计算
    lat =
      bf -
      (tf / (2 * mf)) *
      X *
      (X / nf) *
      (1 -
        (1 / 12) *
        (5 + 3 * tf * tf + n2 - 9 * n2 * tf * tf) *
        ((X * X) / (nf * nf)) +
        (1 / 360) *
        (61 + 90 * tf * tf + 45 * Math.pow(tf, 4)) *
        (Math.pow(X, 4) / Math.pow(nf, 4)));
    //经度偏差
    lon =
      (1 / (nf * Math.cos(bf))) * X -
      (1 / (6 * Math.pow(nf, 3) * Math.cos(bf))) *
      (1 + 2 * tf * tf + n2) *
      Math.pow(X, 3) +
      (1 / (120 * Math.pow(nf, 5) * Math.cos(bf))) *
      (5 + 28 * tf * tf + 24 * Math.pow(tf, 4)) *
      Math.pow(X, 5);
    // result[0] = lat / iPI;
    // result[1] = formatby6(L0 + lon / iPI);
    const lng1=(L0 + lon / iPI).toFixed(6);
    const lat1= (lat / iPI).toFixed(6);
    console.log('大地两千CGCS2000转经纬度WGS84',[X, Y],[lng1,lat1])
    return [lng1,lat1];
  }

4.4 屏幕坐标 <-> 世界坐标

屏幕坐标转世界坐标

function screenToCartesian(position) {
    const cartesian = viewer.scene.pickPosition(position);
    if (cartesian) {
      const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
      if (cartographic.height < 0) {
        const ray = viewer.camera.getPickRay(position);
        return viewer.scene.globe.pick(ray, viewer.scene);
      } else {
        return cartesian;
      }
    } else {
      return viewer.camera.pickEllipsoid(position, viewer.scene.globe.ellipsoid); //此方法在2D下不适用
    }
    return undefined;
  }

世界坐标转屏幕坐标

function CarTesianToScreen(cartesian) {
    const position =Cesium.SceneTransforms.wgs84ToWindowCoordianates(viewer.scene,cartesian)
    return position
  }

总结:

坐标系分为地理坐标和投影坐标两类,大地坐标系包括在地理坐标系内。大地坐标、地理坐标均是球面的,投影坐标是平面的。三者的区别为:地理是经纬度。投影是XYZ。大地是角度。三者的任何坐标均指某椭球下的。

参考:

zhuanlan.zhihu.com/p/62864791#…

www.cnblogs.com/telwanggs/p…

juejin.cn/post/709719…

blog.csdn.net/qq_34149805…