从中心 Lat/Long 以特定半径在圆内生成点

Producing Points in a Circle with Specific Radius from Central Lat/Long

我需要能够获取一组 lat/lon 坐标并在地图上围绕这些坐标绘制圆形“栅栏”,其中圆形中的点数和圆的半径都是可配置的。我的目标是能够输入点、形状的半径和我希望它拥有的点数,并接收包含 lat/long 坐标列表的输出,这些坐标将围绕中心点(如果已连接)。

我已经能够想出一个可行的解决方案来完成大部分我需要它做的事情,但是有一些事情并不像我期望的那样工作,以及我对底层的理解数学太弱了,我不知道怎么解决。

我参考了 this post from CSharpHelper to produce the points in a circle around my starting point, and math from this Whosebug answer 来尝试将我的英里半径转换为纬度和经度。 这是我到目前为止的代码:

public readonly struct LocationPoint
{
    public LocationPoint(double lat, double lon)
    {
        Lat = lat;
        Lon = lon;
    }
        
    public double Lat { get; }
    public double Lon { get; }
}

private static List<LocationPoint> GetZoneCoordinates(LocationPoint center,
                                                      double radiusInMiles,
                                                      int numberOfPoints)
{
    // Convert input miles to degrees latitude and longitude.
    var radiusKm = radiusInMiles * 0.621371;
    var radiusLon = 1 / (111.319 * Math.Cos(center.Lat)) * radiusKm;
    var radiusLat = 1 / 110.574 * radiusKm;
            
    // Calculate amount to increment angle for number of points.
    var dTheta = 2 * Math.PI / numberOfPoints;
    double theta = 0;

    // Produce points.
    var points = new List<LocationPoint>();
    for (var i = 0; i < numberOfPoints; i++)
    {
        points.Add(new LocationPoint
        (
            center.Lat + radiusLat * Math.Sin(theta),
            center.Lon + radiusLon * Math.Cos(theta)
        ));
        theta += dTheta;
    }
            
    return points;
}

上面的代码可以很好地创建一个带有我给它的点数的圆形。我将以肯尼迪国际机场的中心点为例。如果我 运行 以上半径为 0.5 英里和 8 个点,我得到一组映射到以下点的坐标:

这个形状有两个问题:

  1. 这是一个垂直的椭圆(虽然我不确定这是否真的有问题,可能只是在球体上映射并在二维地图上显示的结果)
  2. 实际半径比我输入的很多。我为上述点集设置了 .5 英里的半径,直径 从右到左实际上更接近 .25 英里。

我做错了什么?

你换算成千米是错误的。 var radiusKm = radiusInMiles * 0.621371; 应该是 var radiusKm = radiusInMiles / 0.621371;.

一种检测此类错误的直观方法:一公里比一英里更短,因此对于任何英里数,您应该比英里多 公里。但是你的公式显然 原始数字减少到更小的值,所以这是不对的。

如果您的原始换算系数带有单位(应该如此),另一种检测此类错误的方法是确保您在数学中包含了单位。 IE。 0.621371 实际上是 0.621371 mile/kilometer。当您在原始公式中包含单位时,您最终得到的单位是 mile * mile/kilometer,而您真正想要的只是 kilometer.

至于边界不是一个完美的圆圈,我会说是的,这可能至少部分是因为在该纬度使用的映射投影。请注意,地图上包含的网格线也不会形成完美的正方形,但会像您的边界一样被拉伸。但也要记住,您使用的公式假设地球是一个完美的球体,但事实并非如此。与从两极到另一极的直径相比,它在赤道处凸起,因此将其视为完美球体会导致扭曲,如您在此处看到的那样。

如果这对其他人有帮助,我能够使用上面代码中的一些数学以及@PeterDuniho 的指导,非常接近真正的“大圆”距离计算,现在该方法输出坐标,创建一个更均匀的圆形:

private static List<LocationPoint> GetZoneCoordinates(LocationPoint center,
                                                      double radiusInMiles,
                                                      int numberOfPoints)
{
    // Convert input miles to degrees latitude and longitude.
    var radiusKm = radiusInMiles / 0.621371;
    var radiusLon = 1 / (111.319 * Math.Cos(center.Lat * (Math.PI / 180))) * radiusKm;
    var radiusLat = 1 / 110.574 * radiusKm;
            
    // Calculate amount to increment angle for number of points.
    var dTheta = 2 * Math.PI / numberOfPoints;
    double theta = 0;

    // Produce points.
    var points = new List<LocationPoint>();
    for (var i = 0; i < numberOfPoints; i++)
    {
        points.Add(new LocationPoint
        (
            center.Lat + radiusLat * Math.Sin(theta),
            center.Lon + radiusLon * Math.Cos(theta)
        ));
        theta += dTheta;
    }
            
    return points;
}

区别在于计算 radiusLon 的线上 - 使用中心点纬度的 转换弧度 的余弦值而不是原始度数。我希望我能说我明白为什么这会有所不同,但它在我迄今为止尝试过的所有纬度上都运行良好。

考虑到彼得在我的原始代码中发现了最严重的错误,我将保留他的回答作为已接受的答案。再次感谢您的帮助!