Cesium中空间计算 、坐标转换、相机姿态

/**
 * 获取当前相机姿态信息
 * 包括经度、纬度、高程、Heading、Pitch、Roll
 * @param viewer
 */
let getCameraInfo = (viewer) => {
  if (viewer && viewer.camera && viewer.camera.position && viewer.camera.heading) {
    let p = toDegrees(viewer.camera.position);
    let heading = Cesium.Math.toDegrees(viewer.camera.heading);
    let pitch = Cesium.Math.toDegrees(viewer.camera.pitch);
    let roll = Cesium.Math.toDegrees(viewer.camera.roll);
    return {
      heading: parseFloat(heading).toFixed(5),
      pitch: parseFloat(pitch).toFixed(5),
      roll: parseFloat(roll).toFixed(5),
      lng: parseFloat(p.lng).toFixed(7),
      lat: parseFloat(p.lat).toFixed(7),
      alt: parseFloat(p.alt).toFixed(2)
    }
  } else {
    throw new Error("Error in Parameter!");
  }
};

/**
 * 距离(米)转换为纬度  一米对应的纬度为定值
 * @param meter 距离多少米
 * @returns {number}
 */
let meter2Lat = (meter) => {
  if (!meter) {
    throw new Error("Error in Parameter!");
  }
  let pi = Math.PI;
  let lngInMeter = (6371 * 2 * pi) / 360;
  return (meter / lngInMeter) / 1000;
};

/**
 * 距离(米)转换为经度  不同纬度下一米对应的经度不同
 * @param meter 距离
 * @param lat 所在纬度
 * @returns {number}
 */
let meter2Lng = (meter, lat) => {
  if ((!meter) || (!lat)) {
    throw new Error("Error in Parameter!");
  }
  let pi = Math.PI;
  let latInMeter = (Math.cos(lat * pi / 180) * 6371 * 2 * pi) / 360;
  return (meter / latInMeter) / 1000;
};

/**
 * 判断该点是否是经纬度或者笛卡尔坐标
 * @param point
 */
let isDegreesOrCartesian = (point) => {
  if (!point) {
    throw new Error("Error in Parameter!");
  }
  if (('number' === typeof point.x) && ('number' === typeof point.y) && ('number' === typeof point.z)) {
    return true
  }
  if (('number' === typeof point.lng) && ('number' === typeof point.lat)) {
    return true
  }
  return false;
};

/**
 * 转化成经纬度
 * @param point
 */
let toDegrees = (point) => {
  if (isDegreesOrCartesian(point)) {
    /**
     * 笛卡尔坐标转地理坐标
     * @param point
     */
    let toDegreesFromCartesian = (point) => {
      let cartesian33 = new Cesium.Cartesian3(point.x, point.y, point.z);
      let cartographic = Cesium.Cartographic.fromCartesian(cartesian33);
      return {
        lng: parseFloat(Cesium.Math.toDegrees(cartographic.longitude).toFixed(8)),
        lat: parseFloat(Cesium.Math.toDegrees(cartographic.latitude).toFixed(8)),
        alt: parseFloat(cartographic.height.toFixed(8))
      };

    };
    if (point.x) {
      point = toDegreesFromCartesian(point);
    }
    return point;
  }
};

/**
 * 转化成笛卡尔坐标
 * @param point
 */
let toCartesian = (point) => {
  if (isDegreesOrCartesian(point)) {
    /**
     * 地理坐标转笛卡尔坐标
     * @param point
     */
    let toCartesianFromDegrees = (point) => {
      return Cesium.Cartesian3.fromDegrees(point.lng, point.lat, point.alt || 0);
    };
    if (point.lng) {
      point = toCartesianFromDegrees(point);
    }
    return point;
  }
};

/**
 * 转屏幕坐标
 * @param point
 * @param viewer
 */
let toWindowCoordinates = (point, viewer) => {
  if (viewer && point && point.x && point.y && point.z) {
    return Cesium.SceneTransforms.wgs84ToWindowCoordinates(viewer.scene, point);
  } else if (viewer && point.lng && point.lat && point.alt) {
    return Cesium.SceneTransforms.wgs84ToWindowCoordinates(viewer.scene, toCartesianFromDegrees(point));
  } else {
    throw new Error("Error in Parameter!");
  }
};

/**
 * 点到线段的最短距离
 * @param a 线段端点
 * @param b 线段端点
 * @param s 该点到ab的最短距离
 * @returns {number}
 */
let point2LineDistance = (a, b, s) => {
  a = toCartesian(a);
  b = toCartesian(b);
  s = toCartesian(s);
  let ab = Math.sqrt(Math.pow((a.x - b.x), 2.0) + Math.pow((a.y - b.y), 2.0) + Math.pow((a.z - b.z), 2.0));
  let as = Math.sqrt(Math.pow((a.x - s.x), 2.0) + Math.pow((a.y - s.y), 2.0) + Math.pow((a.z - s.z), 2.0));
  let bs = Math.sqrt(Math.pow((s.x - b.x), 2.0) + Math.pow((s.y - b.y), 2.0) + Math.pow((s.z - b.z), 2.0));
  let cos_A = (Math.pow(as, 2.0) + Math.pow(ab, 2.0) - Math.pow(bs, 2.0)) / (2 * ab * as);
  let sin_A = Math.sqrt(1 - Math.pow(cos_A, 2.0));
  let t = ((a.x - s.x) * (a.x - b.x) + (a.y - s.y) * (a.y - b.y) + (a.z - s.z) * (a.z - b.z)) / (Math.pow((a.x - b.x), 2.0) + Math.pow((a.y - b.y), 2.0) + Math.pow((a.z - b.z), 2.0));
  if (t < 0) {
    return as;
  } else if (t <= 1 && t >= 0) {
    return as * sin_A;
  } else if (t > 1) {
    return bs;
  }
};

/**
 * 求多边形的面积
 * @param arr
 * @returns {*}
 */
let countArea = (arr) => {
  if ((!arr) || (arr.length < 3)) {
    throw new Error("Error in Parameter!");
  } else {
    let area = 0;
    for (let i = 0; i < arr.length; i++) {
      let j = (i + 1) % arr.length;
      let p1 = arr[i], p2 = arr[j];
      p1 = toCartesian(p1);
      p2 = toCartesian(p2);
      area += p1.x * p2.y;
      area -= p1.y * p2.x;
    }
    area /= 2;
    return Math.abs(area);
  }
};

/**
 * 求三角形面积;返回-1为不能组成三角形;
 * @param a
 * @param b
 * @param c
 * @returns {*}
 */
let countAreaByThreePoints = (a, b, c) => {
  a = toCartesian(a);
  b = toCartesian(b);
  c = toCartesian(c);
  let area = -1;
  let side = [];//存储三条边的长度;
  side[0] = Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2) + Math.pow(a.z - b.z, 2));
  side[1] = Math.sqrt(Math.pow(a.x - c.x, 2) + Math.pow(a.y - c.y, 2) + Math.pow(a.z - c.z, 2));
  side[2] = Math.sqrt(Math.pow(c.x - b.x, 2) + Math.pow(c.y - b.y, 2) + Math.pow(c.z - b.z, 2));
  //不能构成三角形;
  if (side[0] + side[1] <= side[2] || side[0] + side[2] <= side[1] || side[1] + side[2] <= side[0]) {
    return area;
  }
  //利用海伦公式。area =sqr(p*(p-a)(p-b)(p-c));
  let p = (side[0] + side[1] + side[2]) / 2; //半周长;
  area = Math.sqrt(p * (p - side[0]) * (p - side[1]) * (p - side[2]));
  return area;
};

/**
 * 计算空间上两点之间的距离
 * @param p1
 * @param p2
 * @returns {null|number}
 */
let getDistance = (p1, p2) => {
  if ((!p1) || (!p2)) {
    throw new Error("Error in Parameter!");
  }
  p1 = toCartesian(p1);
  p2 = toCartesian(p2);
  return Math.sqrt(Math.pow((p1.x - p2.x), 2) + Math.pow((p1.y - p2.y), 2) + Math.pow((p1.z - p2.z), 2));
};

/**
 * 已知三点坐标,求平面的法向量
 * @param p1
 * @param p2
 * @param p3
 * @returns {{x: number, y: number, z: number}}
 */
let getNormal = (p1, p2, p3) => {
  p1=toCartesian(p1);
  p2=toCartesian(p2);
  p3=toCartesian(p3);
  let x = ((p2.y - p1.y) * (p3.z - p1.z) - (p2.z - p1.z) * (p3.y - p1.y));
  let y = ((p2.z - p1.z) * (p3.x - p1.x) - (p2.x - p1.x) * (p3.z - p1.z));
  let z = ((p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x));
  return {"x": x, "y": y, "z": z};
};

/**
 * 求线面交点 线面平行返回undefined //参考网址[https://blog.csdn.net/abcjennifer/article/details/6688080]
 * @param planeVector 平面的法线向量
 * @param planePoint 平面经过的一点坐标
 * @param lineVector 直线的方向向量
 * @param linePoint 直线经过的一点坐标
 * @returns {Array}  返回交点坐标
 * @constructor
 */
let countIntersectionOfLineAndPlane = (planeVector, planePoint, lineVector, linePoint) => {
  let vp1, vp2, vp3, n1, n2, n3, v1, v2, v3, m1, m2, m3, t, vpt;
  vp1 = planeVector.x;
  vp2 = planeVector.y;
  vp3 = planeVector.z;
  n1 = planePoint.x;
  n2 = planePoint.y;
  n3 = planePoint.z;
  v1 = lineVector.x;
  v2 = lineVector.y;
  v3 = lineVector.z;
  m1 = linePoint.x;
  m2 = linePoint.y;
  m3 = linePoint.z;
  vpt = v1 * vp1 + v2 * vp2 + v3 * vp3;
  //首先判断直线是否与平面平行
  let result = {};
  if (vpt === 0) {
    return undefined;
  } else {
    t = ((n1 - m1) * vp1 + (n2 - m2) * vp2 + (n3 - m3) * vp3) / vpt;
    result.x = m1 + v1 * t;
    result.y = m2 + v2 * t;
    result.z = m3 + v3 * t;
  }
  return result;
};

/**
 * 求交点 线面相交 求交点
 * @param line
 * @param polygon
 * @returns {boolean|Array}
 */
let getPointInPolygon = (line, polygon) => {
  let normal = getNormal(polygon[0], polygon[1], polygon[2]);
  let lineX = line[0].x - line[1].x;
  let lineY = line[0].y - line[1].y;
  let lineZ = line[0].z - line[1].z;
  let lineNormal = {"x": lineX, "y": lineY, "z": lineZ};
  let result = countIntersectionOfLineAndPlane(normal, polygon[1], lineNormal, line[0]);
  if (result) {
    return result;
  }
  return false;
};

/**
 * 判断点是否在四边形内部(只针对凸多边形)
 * @param point
 * @param quadrilateral
 */
let isPointInQuadrilateral = (point, quadrilateral) => {
  let s1, s2, s3, s4, s5, s6;//s1 s2是将四边形分为两个三角形的面积  s3 s4 s5 s6代表四边形四个顶点到目标点组成的四个三角形的面积
  let ab, bc, ac, cd, da;//四边形的边长和对角线ac的长度
  ab = getDistance(quadrilateral[0], quadrilateral[1]);
  bc = getDistance(quadrilateral[1], quadrilateral[2]);
  ac = getDistance(quadrilateral[0], quadrilateral[2]);
  cd = getDistance(quadrilateral[2], quadrilateral[3]);
  da = getDistance(quadrilateral[3], quadrilateral[4]);
  //海伦公式 计算出四边形中两个三角形的面积
  let p_abc = (ab + bc + ac) / 2;
  let p_acd = (ac + cd + da) / 2;
  s1 = Math.sqrt(p_abc * (p_abc - ab) * (p_abc - bc) * (p_abc - ac));
  s2 = Math.sqrt(p_acd * (p_acd - ac) * (p_acd - cd) * (p_acd - da));
  let ap, bp, cp, dp;//四边形到目标点之间的距离
  ap = getDistance(point, quadrilateral[0]);
  bp = getDistance(point, quadrilateral[1]);
  cp = getDistance(point, quadrilateral[2]);
  dp = getDistance(point, quadrilateral[3]);
  let p_abp = (ab + ap + bp) / 2;
  let p_bcp = (bc + bp + cp) / 2;
  let p_cdp = (cd + cp + dp) / 2;
  let p_dap = (da + dp + ap) / 2;
  s3 = Math.sqrt(p_abp * (p_abp - ab) * (p_abp - ap) * (p_abp - bp));
  s4 = Math.sqrt(p_bcp * (p_bcp - bc) * (p_bcp - bp) * (p_bcp - cp));
  s5 = Math.sqrt(p_cdp * (p_cdp - cd) * (p_cdp - cp) * (p_cdp - dp));
  s6 = Math.sqrt(p_dap * (p_dap - da) * (p_dap - dp) * (p_dap - ap));
  if (Math.abs((s3 + s4 + s5 + s6) - (s1 + s2)) > 0.0001) {
    return false
  }
  return true;
};

/**
 * 判断点是否在平面内部
 * @param point
 * @param polygon
 * @returns {boolean}
 * @constructor
 */
let JudgePointInPolygon = (point, polygon) => {
  /**
   * 两个向量的叉积和
   * @param n
   * @param m
   * @returns {number}
   * @constructor
   */
  let VectorMultiplication = (n, m) => {
    return (n.y * m.z - m.y * n.z) + (n.z * m.x - n.x * m.z) + (n.x * m.y - n.y * m.x);
  };

  let p1 = polygon[0];
  let p2 = polygon[1];
  let p3 = polygon[2];
  let p4 = polygon[3];
  let n1, n2, n3, n4, n5, n6, n7, n8;
  n1 = {"x": p2.x - p1.x, "y": p2.y - p1.y, "z": p2.z - p1.z};
  n2 = {"x": point.x - p1.x, "y": point.y - p1.y, "z": point.z - p1.z};
  n3 = {"x": p4.x - p3.x, "y": p4.y - p3.y, "z": p4.z - p3.z};
  n4 = {"x": point.x - p3.x, "y": point.y - p3.y, "z": point.z - p3.z};
  n5 = {"x": p3.x - p2.x, "y": p3.y - p2.y, "z": p3.z - p2.z};
  n6 = {"x": point.x - p2.x, "y": point.y - p2.y, "z": point.z - p2.z};
  n7 = {"x": p4.x - p1.x, "y": p4.y - p1.y, "z": p4.z - p1.z};
  n8 = {"x": point.x - p4.x, "y": point.y - p4.y, "z": point.z - p4.z};
  return !(VectorMultiplication(n1, n2) * VectorMultiplication(n3, n4) >= 0 && VectorMultiplication(n5, n6) * VectorMultiplication(n7, n8) >= 0);
};

/**
 * 盘算点是否在线段上
 * @param point
 * @param polyline
 * @returns {boolean}
 * @constructor
 */
let JudgePointInPolyline = (point, polyline) => {
  let lineLength = Math.sqrt(Math.pow((polyline[0].x - polyline[1].x), 2) + Math.pow((polyline[0].y - polyline[1].y), 2) + Math.pow((polyline[0].z - polyline[1].z), 2));
  let one = Math.sqrt(Math.pow((point.x - polyline[1].x), 2) + Math.pow((point.y - polyline[1].y), 2) + Math.pow((point.z - polyline[1].z), 2));
  let two = Math.sqrt(Math.pow((point.x - polyline[0].x), 2) + Math.pow((point.y - polyline[0].y), 2) + Math.pow((point.z - polyline[0].z), 2));
  let di = one + two - lineLength;
  if (di * 10000 < 1) {
    return true;
  }
  return false;

};

/**
 * 根据3个点,计算空间平面的方程
 * Ax+By+Cz+D=0
 * 输入参数:point3fArray---空间中3个点的坐标,大小为3;输入点>3时,只取前3个点
 * 输出参数A,B,C,D
 * 返回值:true---计算成功;false----计算失败
 * @param point3fArray
 * @returns {{A: number, B: number, C: number, D: number}}
 * @constructor
 */
let GetPanelEquation = (point3fArray) => {
  if (point3fArray.length < 3) {
    return undefined;
  }
  let A, B, C, D;
  A = point3fArray[0].y * (point3fArray[1].z - point3fArray[2].z) +
    point3fArray[1].y * (point3fArray[2].z - point3fArray[0].z) +
    point3fArray[2].y * (point3fArray[0].z - point3fArray[1].z);
  B = point3fArray[0].z * (point3fArray[1].x - point3fArray[2].x) +
    point3fArray[1].z * (point3fArray[2].x - point3fArray[0].x) +
    point3fArray[2].z * (point3fArray[0].x - point3fArray[1].x);
  C = point3fArray[0].x * (point3fArray[1].y - point3fArray[2].y) +
    point3fArray[1].x * (point3fArray[2].y - point3fArray[0].y) +
    point3fArray[2].x * (point3fArray[0].y - point3fArray[1].y);
  D = -point3fArray[0].x * (point3fArray[1].y * point3fArray[2].z - point3fArray[2].y * point3fArray[1].z) -
    point3fArray[1].x * (point3fArray[2].y * point3fArray[0].z - point3fArray[0].y * point3fArray[2].z) -
    point3fArray[2].x * (point3fArray[0].y * point3fArray[1].z - point3fArray[1].y * point3fArray[0].z);
  return {A: A, B: B, C: C, D: D};
};



export default {
  getCameraInfo,
  meter2Lat,
  meter2Lng,
  isDegreesOrCartesian,
  toDegrees,
  toCartesian,
  toWindowCoordinates,
  point2LineDistance,
  countArea,
  countAreaByThreePoints,
  getDistance,
  getNormal,
  countIntersectionOfLineAndPlane,
  getPointInPolygon,
  isPointInQuadrilateral,
  JudgePointInPolygon,
  JudgePointInPolyline,
  GetPanelEquation,
}

你可能感兴趣的:(Cesium中空间计算 、坐标转换、相机姿态)