cesium 坐标转换

765 阅读5分钟

简介

cesium 各种坐标转换记录 主要坐标类型说明:

  • 笛卡尔平面坐标(屏幕坐标)

  • 笛卡尔三维空间坐标

  • wgs84 经纬度坐标

  • wgs84 弧度坐标

  • 大地2000 坐标系

弧度与角度相互转换

// 角度转弧度
const radius = Cesium.Math.toRadius(degress)

// 弧度转角度
const degress = Cesium.Math.toDegress(radius)

wgs84 经纬度转笛卡尔坐标

1、直接通过经纬度转换

// wgs84经纬度转笛卡尔坐标
// 角度制与笛卡尔转换
// 格式:[113.21, 25.61, 100.0],高度默认为0,可以不写
let cartesian3 = Cesium.Cartesian3.fromDegrees(lon, lat, height);
// 格式:[113.21, 25.61, 113.54, 25.24],不带高度格式的数组
let cartesian3s = Cesium.Cartesian3.fromDegreesArray(coordinates);
// 格式:[113.21, 25.61, 100.0, 113.54, 25.24, 200.0],带高度格式的数组
let cartesian3s = Cesium.Cartesian3.fromDegreesArrayHeights(coordinates);

// 弧度制也类似,使用Cesium.Cartesian3.fromRadians, Cesium.Cartesian3.fromRadiansArray, Cesium.Cartesian3.fromRadiansArrayHeights

2、使用椭球体转换

let position = Cesium.Cartographic.fromDegrees(lon, lat, height);
// 单个坐标
let cartesian3 = Cesium.Ellipsoid.WGS84.cartographicToCartesian(position);
// 多个坐标
let cartesian3s =
  Cesium.Ellipsoid.WGS84.cartographicArrayToCartesianArray(positions);

屏幕转笛卡尔(Cartesian2 转 Cartesian3)

1、屏幕转椭球面笛卡尔坐标,不包含地形、模型等的坐标,由于不包含材质这里的坐标误差较大
let cartesain3 = viewer.scene.camera.pickEllipsoid(cartesian2);

2、屏幕转场景坐标,包含地形和模型等的场景空间坐标, 拾取点击模型表面的坐标

let cartesian3 = viewer.scene.pickPosition(cartesian2);

3、获取点击处地球表面的世界坐标(有地形),注意:不包括模型、倾斜摄影表面。

var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (event) {
     var ray = viewer.camera.getPickRay(event.position);
     var position = viewer.scene.globe.pick(ray, viewer.scene);
     console.log("获取到的坐标:", position);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

4、屏幕转场景坐标,获取当前位置所有对象列表,适用于多个对象重叠在一个位置,并且要获取到多个对象的情况

var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (event) {
  var pickedObjects = scene.drillPick(event.position);
  // pickedObjects 使用for循环 可以拿到所有entity
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

笛卡尔转屏幕(Cartesian3转Cartesian2)

let cartesian2 = Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene, cartesian3);

椭球笛卡尔坐标与局部笛卡尔坐标

笛卡尔坐标系都是默认指以椭球中心为原点的空间直角坐标系,在绘图时使用。

而在建立模型或者局部计算时,则需要在计算时使用局部坐标,再转换为椭球笛卡尔坐标后绘图。

1、建立转换矩阵

使用Transforms的eastNorthUpToFixedFrame方法建立转换矩阵,API:传送门

image.png 则该点为局部坐标系的中心,坐标为(0, 0, 0),x轴指向东方向,y轴指向北方向,z轴指穿过该位置的椭球曲面法线方向。

// 新建局部坐标系
let modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(cartesian3);

2、局部转椭球笛卡尔

基于上文建立四维转换矩阵,使用Matrix4中的multiplyByPoint方法将局部坐标转换为椭球笛卡尔坐标。API参考:传送门

image.png

// point:局部坐标;result:椭球笛卡尔坐标
let result = Cesium.Matrix4.multiplyByPoint(modelMatrix, point, new Cesium.Cartesian3());

经纬度转大地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];
  }

原文地址