Cesium 方位角方法和原理
完整代码:
/*** 计算2点的角度值,角度已正北为0度,顺时针为正方向* @param startPosition - 需要计算的点* @param endPosition - 目标点,以该点为参考中心。* @param [isNorthZero = false] - 是否正东为0时的角度(如方位角)* @returns 返回角度值,0-360度*/
export function getAzimuthAngle(startPosition, endPosition, adjustForOrientation) {// 获取参考点的局部东-北-上坐标系变换矩阵let localFrame = Cesium.Transforms.eastNorthUpToFixedFrame(endPosition);// 从 4x4 变换矩阵中提取 3x3 旋转矩阵localFrame = Cesium.Matrix4.getMatrix3(localFrame, new Cesium.Matrix3());// 提取局部坐标系的东(X)、北(Y)、上(Z)方向向量const eastDirection = Cesium.Matrix3.getColumn(localFrame, 0, new Cesium.Cartesian3());const northDirection = Cesium.Matrix3.getColumn(localFrame, 1, new Cesium.Cartesian3());const upDirection = Cesium.Matrix3.getColumn(localFrame, 2, new Cesium.Cartesian3());// 计算目标点相对于参考点的方向向量let directionVector = Cesium.Cartesian3.subtract(startPosition, endPosition, new Cesium.Cartesian3());// 检查方向向量是否有效(非 NaN 且不为零向量)if (isNaN(directionVector.x) ||isNaN(directionVector.y) ||isNaN(directionVector.z) ||directionVector.equals(Cesium.Cartesian3.ZERO)) {return 0;}// 将方向向量投影到局部水平面(通过两次叉乘实现)directionVector = Cesium.Cartesian3.cross(directionVector, upDirection, directionVector);directionVector = Cesium.Cartesian3.cross(upDirection, directionVector, directionVector);// 归一化方向向量directionVector = Cesium.Cartesian3.normalize(directionVector, directionVector);// 计算方向向量与东方向的夹角(弧度)let angleFromEast = Cesium.Cartesian3.angleBetween(eastDirection, directionVector);// 计算方向向量与北方向的夹角,用于确定角度方向const angleFromNorth = Cesium.Cartesian3.angleBetween(northDirection, directionVector);// 如果方向向量更接近南方向(与北向夹角 > 90°),调整角度if (angleFromNorth > Math.PI * 0.5) {angleFromEast = 2 * Math.PI - angleFromEast;}// 将角度转换为度,并调整为方位角(0° 在北,逆时针旋转)let azimuthAngle = 360 - Cesium.Math.toDegrees(angleFromEast) - 180;// 根据调整标志,增加 90 度(用于特定方向校正)if (adjustForOrientation) {azimuthAngle += 90;}// 确保角度在 0-360 度范围内if (azimuthAngle < 0) {azimuthAngle += 360;} else if (azimuthAngle > 360) {azimuthAngle -= 360;}return azimuthAngle;
}// https://jercky.top/2021/03/26/Cesium%E5%85%A5%E9%97%A8-7/
// https://github.com/JerckyLY/cesium-demo-view/blob/master/Utils/CesiumAngelUtil.js/*** 计算两个点的方位角度* @param lng_a* @param lat_a* @param lng_b* @param lat_b* @return {number}*/
export function courseAngle(startPosition, endPosition) {const carto_a = Cesium.Cartographic.fromCartesian(startPosition);const carto_b = Cesium.Cartographic.fromCartesian(endPosition);const lng_a = Cesium.Math.toDegrees(carto_a.longitude);const lat_a = Cesium.Math.toDegrees(carto_a.latitude);const lng_b = Cesium.Math.toDegrees(carto_b.longitude);const lat_b = Cesium.Math.toDegrees(carto_b.latitude);//以a点为原点建立局部坐标系(东方向为x轴,北方向为y轴,垂直于地面为z轴),得到一个局部坐标到世界坐标转换的变换矩阵const localToWorld_Matrix = Cesium.Transforms.eastNorthUpToFixedFrame(new Cesium.Cartesian3.fromDegrees(lng_a, lat_a));//求世界坐标到局部坐标的变换矩阵const worldToLocal_Matrix = Cesium.Matrix4.inverse(localToWorld_Matrix,new Cesium.Matrix4());//a点在局部坐标的位置,其实就是局部坐标原点const localPosition_A = Cesium.Matrix4.multiplyByPoint(worldToLocal_Matrix,new Cesium.Cartesian3.fromDegrees(lng_a, lat_a),new Cesium.Cartesian3());//B点在以A点为原点的局部的坐标位置const localPosition_B = Cesium.Matrix4.multiplyByPoint(worldToLocal_Matrix,new Cesium.Cartesian3.fromDegrees(lng_b, lat_b),new Cesium.Cartesian3());//弧度const angle = Math.atan2(localPosition_B.x - localPosition_A.x,localPosition_B.y - localPosition_A.y);//角度let theta = angle * (180 / Math.PI);if (theta < 0) {theta = theta + 360;}return theta;
}export function turfBearing(startPosition, endPosition) {const carto_a = Cesium.Cartographic.fromCartesian(startPosition);const carto_b = Cesium.Cartographic.fromCartesian(endPosition);const lng_a = Cesium.Math.toDegrees(carto_a.longitude);const lat_a = Cesium.Math.toDegrees(carto_a.latitude);const lng_b = Cesium.Math.toDegrees(carto_b.longitude);const lat_b = Cesium.Math.toDegrees(carto_b.latitude);const point1 = turf.point([lng_a, lat_a]); // Start: Philadelphia City Hallconst point2 = turf.point([lng_b, lat_b]); // End: Philadelphia International Airportreturn turf.bearing(point1, point2);
}