简体   繁体   English

从中央纬度/经度在具有特定半径的圆中生成点

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

I need to be able to take a set of lat/lon coordinates and draw a circular "fence" around those coordinates on a map, where the number of points in the circular shape and the radius of the circle are both configurable.我需要能够获取一组纬度/经度坐标并在 map 上围绕这些坐标绘制一个圆形“围栏”,其中圆形中的点数和圆的半径都是可配置的。 My goal is to be able to input the point, the radius of the shape and the number of points I would like it to have and to receive an output containing a List of lat/long coordinates that would complete a shape around the center point if connected.我的目标是能够输入点、形状的半径和我希望它具有的点数,并接收一个 output 包含一个纬度/经度坐标列表,如果它可以在中心点周围完成一个形状连接的。

I've been able to come up with a workable solution that does most of what I need it to, but there are a few things about it that don't work how I expect, and my understanding of the underlying mathematics is weak enough that I'm not sure how to resolve them.我已经能够提出一个可行的解决方案来完成我需要的大部分工作,但是有一些事情并没有按照我的预期工作,而且我对基础数学的理解很弱,以至于我不确定如何解决它们。

I referenced this post from CSharpHelper to produce the points in a circle around my starting point, and math from this StackOverflow answer to try and convert my miles radius to degrees Lat and Long.我引用了 CSharpHelper 的这篇文章,以在我的起点周围生成一个圆圈中的点,并从这个 StackOverflow 答案中进行数学运算,以尝试将我的英里半径转换为纬度和经度。 Here is the code that I have so far:这是我到目前为止的代码:

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;
}

The code above works well to create a circular shape with the number of points that I give it.上面的代码可以很好地使用我给它的点数创建一个圆形。 I'll use a center point at JFK International as an example.我将以肯尼迪国际机场的中心点为例。 If I run the above with a radius of.5 miles and 8 points, I get a set of coordinates that map out to the following points:如果我以 5 英里和 8 个点的半径运行上述内容,我会得到一组坐标 map 到以下点:

在此处输入图像描述

There are two things wrong with this shape:这个形状有两个问题:

  1. It's a vertical ellipse (though I'm not sure if this is actually a problem, might just be the result of mapping on a sphere and displaying on a 2D map)这是一个垂直椭圆(虽然我不确定这是否真的是一个问题,可能只是在球体上映射并在 2D 地图上显示的结果)
  2. The actual radius is much smaller than my input.实际半径远小于我的输入。 I in put a radius of.5 miles for the above set of points, and the diameter from right to left is actually closer to.25 miles.我为上面的一组点输入了0.5英里的半径,从右到左的直径实际上更接近0.25英里。

What am I doing wrong here?我在这里做错了什么?

Your conversion to km is wrong.您转换为公里是错误的。 var radiusKm = radiusInMiles * 0.621371; should be var radiusKm = radiusInMiles / 0.621371;应该是var radiusKm = radiusInMiles / 0.621371; . .

An intuitive way to detect this sort of mistake: one kilometer is a shorter distance than one mile, so for any number of miles, you should have more kilometers than miles.一种检测此类错误的直观方法:一公里比一英里短,因此对于任意数量的英里,您应该有比英里的公里。 But your formula obviously reduces the original number to something smaller, so that can't be right.但是您的公式显然原始数字减少到较小的数字,因此这是不对的。

If your original conversion factor comes with units (as it should), another way to detect that sort of mistake is to make sure you include the units in the math.如果您的原始转换因子带有单位(应该如此),那么检测这种错误的另一种方法是确保在数学中包含单位。 Ie 0.621371 is actually 0.621371 mile/kilometer .0.621371实际上是0.621371 mile/kilometer When you include the units in your original formula, you wind up with units of mile * mile/kilometer , when what you actually want is just kilometer .当您在原始公式中包含单位时,您会得到mile * mile/kilometer的单位,而您真正想要的只是kilometer

As far as the boundary not being a perfect circle, I would say that yes, that's could be at least partially because of the mapping projection being used at that latitude.至于边界不是一个完美的圆,我会说是的,这可能至少部分是因为在那个纬度使用了映射投影。 Note that the grid lines included on the map also don't form perfect squares, but are stretched out the same way your boundary is.请注意,map 中包含的网格线也不会形成完美的正方形,但会以与您的边界相同的方式延伸。 But also keep in mind that the formulae you're using assumes the Earth is a perfect sphere, and it's definitely not.但请记住,您使用的公式假设地球是一个完美的球体,而且绝对不是。 It bulges at the equator as compared to the diameter from pole to pole, and so treating it as a perfect sphere will result in distortions such as you're seeing here.与从两极到另一极的直径相比,它在赤道处凸出,因此将其视为完美球体将导致您在此处看到的扭曲。

In case this is helpful to anyone else, I was able to get a very close approximation of a true "great-circle" distance calculation using some of the math in my code above, along with guidance from @PeterDuniho, and now the method outputs coordinates that create a much more uniform circle shape:如果这对其他人有帮助,我可以使用上面代码中的一些数学运算以及@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;
}

The difference is on the line where radiusLon is calculated - the cosine of the converted radians of the center point's latitude is being used instead of the raw degrees.不同之处在于计算radiusLon的行 - 使用中心点纬度的转换弧度的余弦而不是原始度数。 I wish I could say that I understand why this makes the difference, but it works well at all of the latitudes I've tried so far.我希望我可以说我理解为什么这会有所作为,但它在我迄今为止尝试过的所有纬度上都运作良好。

I will leave Peter's response as the Accepted answer considering that he spotted the most grievous error in my original code.考虑到他在我的原始代码中发现了最严重的错误,我会将 Peter 的回复保留为已接受的答案。 Thanks again for the help!再次感谢您的帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM