如何在C#中计算切线角度变化后圆的新坐标与方位(EPSG:4326)
问题分析与C#实现方案
几何核心逻辑
- 初始状态:圆与两条垂直切线(右侧、底部)相切,圆心到两条切线的距离均等于半径
r。 - 目标状态:右侧切线保持不变,两条切线夹角变为127°,需找到新的圆心位置,使得圆仍与两条切线相切。
关键结论:
- 圆心始终位于两条切线的角平分线上(因到两条切线距离相等)。
- 初始夹角90°时,角平分线与右侧切线夹角为45°;夹角变为127°时,角平分线与右侧切线夹角为
127°/2 = 63.5°。 - 圆心到右侧切线的距离固定为
r,因此可通过方位角偏移和球面距离计算得到新坐标。
注意事项(EPSG:4326 经纬度处理)
EPSG:4326是球面坐标系,代码采用球面三角公式实现坐标计算,适配任意范围场景,避免局部平面近似带来的误差。
C# 实现代码
using System; public class CirclePositionCalculator { private const double EarthRadius = 6378137; // WGS84地球半径(米) private const double DegToRad = Math.PI / 180; private const double RadToDeg = 180 / Math.PI; /// <summary> /// 计算圆的新圆心坐标 /// </summary> /// <param name="rightTangentStart">右侧切线起点的经纬度</param> /// <param name="rightTangentAzimuth">右侧切线的方位角(正北为0°,顺时针递增)</param> /// <param name="circleRadius">圆的半径(米)</param> /// <param name="targetAngle">目标切线夹角(默认127°)</param> /// <returns>新圆心的经纬度(纬度,经度)</returns> public (double Latitude, double Longitude) CalculateNewCircleCenter( (double Lat, double Lon) rightTangentStart, double rightTangentAzimuth, double circleRadius, double targetAngle = 127) { // 计算目标状态下角平分线的方位角(假设夹角向初始底部方向增大,反向则改为减号) var targetBisectorAzimuth = rightTangentAzimuth + targetAngle / 2; // 计算圆心到右侧切线起点的直线距离:r / cos(θ/2),θ为目标夹角 var distanceToTangentStart = circleRadius / Math.Cos((targetAngle / 2) * DegToRad); // 基于方位角和距离计算新圆心坐标 return CalculatePointFromAzimuthDistance(rightTangentStart, targetBisectorAzimuth, distanceToTangentStart); } /// <summary> /// 根据起点、方位角、距离计算目标点经纬度 /// </summary> private (double Lat, double Lon) CalculatePointFromAzimuthDistance( (double Lat, double Lon) start, double azimuth, double distance) { var lat1 = start.Lat * DegToRad; var lon1 = start.Lon * DegToRad; var azimuthRad = azimuth * DegToRad; var angularDistance = distance / EarthRadius; // 球面三角公式计算纬度 var lat2 = Math.Asin(Math.Sin(lat1) * Math.Cos(angularDistance) + Math.Cos(lat1) * Math.Sin(angularDistance) * Math.Cos(azimuthRad)); // 球面三角公式计算经度,并修正到[-180°, 180°]范围 var lon2 = lon1 + Math.Atan2(Math.Sin(azimuthRad) * Math.Sin(angularDistance) * Math.Cos(lat1), Math.Cos(angularDistance) - Math.Sin(lat1) * Math.Sin(lat2)); lon2 = (lon2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI; return (lat2 * RadToDeg, lon2 * RadToDeg); } /// <summary> /// 验证圆心到右侧切线的距离是否等于半径 /// </summary> public double VerifyDistanceToTangent((double Lat, double Lon) center, (double Lat, double Lon) tangentStart, double tangentAzimuth) { var delta = GetDeltaMeters(tangentStart, center); var azimuthRad = tangentAzimuth * DegToRad; // 计算点到直线的垂直距离 return Math.Abs(delta.Y * Math.Cos(azimuthRad) - delta.X * Math.Sin(azimuthRad)); } /// <summary> /// 计算两点间的平面坐标差(米) /// </summary> private (double X, double Y) GetDeltaMeters((double Lat, double Lon) start, (double Lat, double Lon) end) { var lat1 = start.Lat * DegToRad; var lon1 = start.Lon * DegToRad; var lat2 = end.Lat * DegToRad; var lon2 = end.Lon * DegToRad; var dLat = lat2 - lat1; var dLon = lon2 - lon1; // Haversine公式计算直线距离 var a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(lat1) * Math.Cos(lat2) * Math.Sin(dLon / 2) * Math.Sin(dLon / 2); var distance = EarthRadius * 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a)); // 计算两点间的方位角 var azimuth = Math.Atan2(Math.Sin(dLon) * Math.Cos(lat2), Math.Cos(lat1) * Math.Sin(lat2) - Math.Sin(lat1) * Math.Cos(lat2) * Math.Cos(dLon)); return (distance * Math.Cos(azimuth), distance * Math.Sin(azimuth)); } } // 用法示例 public class Program { public static void Main() { var calculator = new CirclePositionCalculator(); // 示例参数:右侧切线起点(北京天安门附近),方位角90°(正东),半径100米 var tangentStart = (Lat: 39.9042, Lon: 116.4074); var tangentAzimuth = 90.0; var radius = 100.0; var newCenter = calculator.CalculateNewCircleCenter(tangentStart, tangentAzimuth, radius); Console.WriteLine($"新圆心坐标:纬度 {newCenter.Latitude:F6}, 经度 {newCenter.Longitude:F6}"); // 验证距离是否符合要求 var distance = calculator.VerifyDistanceToTangent(newCenter, tangentStart, tangentAzimuth); Console.WriteLine($"圆心到右侧切线的距离:{distance:F2} 米(理论值:{radius}米)"); } }
代码说明
- 方位角适配:采用GIS领域标准方位角定义(正北0°,顺时针递增),若实际场景方位角定义不同,可调整
targetBisectorAzimuth的计算逻辑。 - 方向调整:若夹角增大方向与示例相反,将
targetBisectorAzimuth的计算改为rightTangentAzimuth - targetAngle / 2即可。 - 验证机制:
VerifyDistanceToTangent方法可验证计算结果的正确性,确保圆心到右侧切线的距离等于半径。
内容的提问来源于stack exchange,提问作者Sergio Lobo
相关产品推荐
相关产品推荐

