You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C#中计算切线角度变化后圆的新坐标与方位(EPSG:4326)

问题分析与C#实现方案

几何核心逻辑

  1. 初始状态:圆与两条垂直切线(右侧、底部)相切,圆心到两条切线的距离均等于半径r。
  2. 目标状态:右侧切线保持不变,两条切线夹角变为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}米)");
    }
}

代码说明

  1. 方位角适配:采用GIS领域标准方位角定义(正北0°,顺时针递增),若实际场景方位角定义不同,可调整targetBisectorAzimuth的计算逻辑。
  2. 方向调整:若夹角增大方向与示例相反,将targetBisectorAzimuth的计算改为rightTangentAzimuth - targetAngle / 2即可。
  3. 验证机制:VerifyDistanceToTangent方法可验证计算结果的正确性,确保圆心到右侧切线的距离等于半径。

内容的提问来源于stack exchange,提问作者Sergio Lobo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 21:00:28