C# 如何找到给定纬度/长度以北x km的纬度/长度?

C# 如何找到给定纬度/长度以北x km的纬度/长度?,c#,geometry,gis,C#,Geometry,Gis,我有一些生成谷歌地图的C代码。这些代码查看我需要在地图上绘制的所有点,然后计算出矩形的边界以包含这些点。然后,它将此边界传递给GoogleMapsAPI,以适当地设置缩放级别以显示地图上的所有点 此代码运行良好,但我有一个新的要求 其中一个点可能具有与其相关联的精度。如果是这种情况,那么我在点周围画一个圆,半径设置为精度值。同样,这很好,但是我的边界检查现在并没有执行我希望它执行的操作。我想让边界框包含完整的圆 这需要一个算法来获取点x并计算点y,该点y位于x以北z米处,也位于x以南z米处 有没

我有一些生成谷歌地图的C代码。这些代码查看我需要在地图上绘制的所有点,然后计算出矩形的边界以包含这些点。然后,它将此边界传递给GoogleMapsAPI,以适当地设置缩放级别以显示地图上的所有点

此代码运行良好,但我有一个新的要求

其中一个点可能具有与其相关联的精度。如果是这种情况,那么我在点周围画一个圆,半径设置为精度值。同样,这很好,但是我的边界检查现在并没有执行我希望它执行的操作。我想让边界框包含完整的圆

这需要一个算法来获取点x并计算点y,该点y位于x以北z米处,也位于x以南z米处

有没有人有这个算法,最好是用C#。我确实找到了一个通用算法,但我似乎没有正确地实现这一点,因为我得到的答案是1000公里的漂流

这是一个通用的例子

Lat/lon given radial and distance

A point {lat,lon} is a distance d out on the tc radial from point 1 if:

     lat=asin(sin(lat1)*cos(d)+cos(lat1)*sin(d)*cos(tc))
     IF (cos(lat)=0)
        lon=lon1      // endpoint a pole
     ELSE
        lon=mod(lon1-asin(sin(tc)*sin(d)/cos(lat))+pi,2*pi)-pi
     ENDIF
这是我的C#翻译

  // Extend a Point North/South by the specified distance
    public static Point ExtendPoint(Point _pt, int _distance, int _bearing )
    {
        Decimal lat = 0.0;
        Decimal lng = 0.0;

        lat = Math.Asin(Math.Sin(_pt.Lat) * Math.Cos(_distance) + Math.Cos(_pt.Lat) * 
            Math.Sin(_distance) * Math.Cos(_bearing));

         if (Math.Cos(lat) == 0)
         {
            lng = _pt.Lng;      // endpoint a pole
         }
         else 
         {
             lng = (
                 (_pt.Lng - Math.Asin(Math.Sin(_bearing) * Math.Sin(_distance) / Math.Cos(lat)) 
                 + Math.PI) % (2 * Math.PI)) - Math.PI;
         }

         ret = new Point(lat,lng);
         return ret;
    }
我用0的方位角调用这个函数来计算新的北风位置,用180的值来计算新的南风位置


有人能看到我做错了什么,或者提供了一个已知的工作算法吗?

如果你有一个给定的纬度和经度,你可以计算出x-km纬度变化的正确纬度和经度,如下所示:

new-lat = ((old-km-north + x-km-change)/40,075) * 360)
           ^ is the ratio of the                  ^ times the ratio of the circle
           earth the change                       by 360 to get the total ratio 
           covers.                                covered in degrees.
这同样适用于经度。如果有总距离加上更改,则可以以类似方式计算总度数

new-long = ((old-km-east + x-km-change)/40,075) * 360)
           ^ is the ratio of the                  ^ times the ratio of the circle
           earth the change                       by 360 to get the total ratio 
           covers.                                covered in degrees.
同样,这些计算应该是可行的,但我在这里脱离了纯粹的直觉,但逻辑似乎是正确的


编辑:正如Skizz 40075所指出的,需要使用2.pi.r.cos(lat)或40074.cos(lat)将其调整到任何给定纬度的地球周长。

如果您先将其重新投影,然后检查距离,则更准确


希望这有帮助

我有一段非常类似的代码。与另一个实现相比,它的结果非常接近

我认为你的问题是,你用“距离”作为线性距离(米),而不是角距离(弧度)

/// <summary>
/// Calculates the end-point from a given source at a given range (meters) and bearing (degrees).
/// This methods uses simple geometry equations to calculate the end-point.
/// </summary>
/// <param name="source">Point of origin</param>
/// <param name="range">Range in meters</param>
/// <param name="bearing">Bearing in degrees</param>
/// <returns>End-point from the source given the desired range and bearing.</returns>
public static LatLonAlt CalculateDerivedPosition(LatLonAlt source, double range, double bearing)
{
    double latA = source.Latitude * UnitConstants.DegreesToRadians;
    double lonA = source.Longitude * UnitConstants.DegreesToRadians;
    double angularDistance = range / GeospatialConstants.EarthRadius;
    double trueCourse = bearing * UnitConstants.DegreesToRadians;

    double lat = Math.Asin(
        Math.Sin(latA) * Math.Cos(angularDistance) + 
        Math.Cos(latA) * Math.Sin(angularDistance) * Math.Cos(trueCourse));

    double dlon = Math.Atan2(
        Math.Sin(trueCourse) * Math.Sin(angularDistance) * Math.Cos(latA), 
        Math.Cos(angularDistance) - Math.Sin(latA) * Math.Sin(lat));

    double lon = ((lonA + dlon + Math.PI) % UnitConstants.TwoPi) - Math.PI;

    return new LatLonAlt(
        lat * UnitConstants.RadiansToDegrees, 
        lon * UnitConstants.RadiansToDegrees, 
        source.Altitude);
}
LatLonAlt以度/米为单位(转换在内部进行)。 根据需要进行调整


我想你可以计算出单位常数.度数的值:)

我不确定这里是否遗漏了什么,但我想这个问题可以重新表述为,“我有一个纬度点,我想找到该点以北x米和以南x米的点。”

如果这是一个问题,那么你不需要找到一个新的经度(这使事情更简单),你只需要一个新的纬度。地球上任何地方的纬度约为60海里,1海里为1852米。因此,对于新纬度x米的南北方向:

north_lat = lat + x / (1852 * 60)
north_lat = min(north_lat, 90)

south_lat = lat - x / (1852 * 60)
south_lat = max(south_lat, -90)
这并不完全准确,因为地球并不是一个完美的球体,每个纬度之间正好有60海里。然而,其他的答案假设纬度线是等距的,所以我假设你不在乎这个。如果您对可能引入的误差感兴趣,Wikipedia上有一个很好的表格,其中显示了此链接中不同纬度的“每1°纬度变化的表面距离”:


这两个方程在……上有问题。。。但我并没有分析它们来找出原因

第三个方程式似乎给出了正确的结果

下面是php中的测试用例。。。第三个方程式是正确的,前两个方程式给出的经度值非常不正确

<?php
            $lon1 = -108.553412; $lat1 = 35.467155; $linDistance = .5; $bearing = 170;
            $lon1 = deg2rad($lon1); $lat1 = deg2rad($lat1);
            $distance = $linDistance/6371;  // convert dist to angular distance in radians
            $bearing = deg2rad($bearing);

            echo "lon1: " . rad2deg($lon1) . " lat1: " . rad2deg($lat1) . "<br>\n";

// doesn't work
            $lat2 = asin(sin($lat1) * cos($distance) + cos($lat1) * sin($distance) * cos($bearing) );
            $dlon = atan2(sin($bearing) * sin($distance) * cos($lat1), cos($distance) - sin($lat1) * sin($lat2));
            $lon2 = (($lon1 - $dlon + M_PI) % (2 * M_PI)) - M_PI;  // normalise to -180...+180

            echo "lon2: " . rad2deg($lon2) . " lat2: " . rad2deg($lat2) . "<br>\n";

// same results as above
            $lat3 = asin( (sin($lat1) * cos($distance)) + (cos($lat1) * sin($distance) * cos($bearing)));
            $lon3 = (($lon1 - (asin(sin($bearing) * sin($distance) / cos($lat3))) + M_PI) % (2 * M_PI)) - M_PI;

            echo "lon3: " . rad2deg($lon3) . " lat3: " . rad2deg($lat3) . "<br>\n";

// gives correct answer... go figure
            $lat4 = asin(sin($lat1) * cos($linDistance/6371) + cos($lat1) * sin($linDistance/6371) * cos($bearing) );
            $lon4 = $lon1 + atan2( (sin($bearing) * sin($linDistance/6371) * cos($lat1) ), (cos($linDistance/6371) - sin($lat1) * sin($lat2)));

            echo "lon4: " . rad2deg($lon4) . " lat4: " . rad2deg($lat4) . "<br>\n";
?>
在没有楼层功能的情况下,以下各项应起作用-无论 “int”是截断还是向下舍入:

mod=y-x*int(y/x)
如果(mod<0)mod=mod+x
php就像C中的fmod,出于我的目的,它是“错误的”

对于懒惰的人,(比如我;)一个拷贝粘贴解决方案,Erich Mirabal的版本有非常小的改动:

using System.Device.Location; // add reference to System.Device.dll
public static class GeoUtils
{
    /// <summary>
    /// Calculates the end-point from a given source at a given range (meters) and bearing (degrees).
    /// This methods uses simple geometry equations to calculate the end-point.
    /// </summary>
    /// <param name="source">Point of origin</param>
    /// <param name="range">Range in meters</param>
    /// <param name="bearing">Bearing in degrees</param>
    /// <returns>End-point from the source given the desired range and bearing.</returns>
    public static GeoCoordinate CalculateDerivedPosition(this GeoCoordinate source, double range, double bearing)
    {
        var latA = source.Latitude * DegreesToRadians;
        var lonA = source.Longitude * DegreesToRadians;
        var angularDistance = range / EarthRadius;
        var trueCourse = bearing * DegreesToRadians;

        var lat = Math.Asin(
            Math.Sin(latA) * Math.Cos(angularDistance) +
            Math.Cos(latA) * Math.Sin(angularDistance) * Math.Cos(trueCourse));

        var dlon = Math.Atan2(
            Math.Sin(trueCourse) * Math.Sin(angularDistance) * Math.Cos(latA),
            Math.Cos(angularDistance) - Math.Sin(latA) * Math.Sin(lat));

        var lon = ((lonA + dlon + Math.PI) % (Math.PI*2)) - Math.PI;

        return new GeoCoordinate(
            lat * RadiansToDegrees,
            lon * RadiansToDegrees,
            source.Altitude);
    }

    private const double DegreesToRadians = Math.PI/180.0;
    private const double RadiansToDegrees = 180.0/ Math.PI;
    private const double EarthRadius = 6378137.0;
}

对于想要java版本Eirch代码的人

/**
 * move latlng point by rang and bearing
 *
 * @param latLng  point
 * @param range   range in meters
 * @param bearing bearing in degrees
 * @return new LatLng
 */
public static LatLng moveLatLng(LatLng latLng, double range, double bearing) {
    double EarthRadius = 6378137.0;
    double DegreesToRadians = Math.PI / 180.0;
    double RadiansToDegrees = 180.0 / Math.PI;

    final double latA = latLng.latitude * DegreesToRadians;
    final double lonA = latLng.longitude * DegreesToRadians;
    final double angularDistance = range / EarthRadius;
    final double trueCourse = bearing * DegreesToRadians;

    final double lat = Math.asin(
            Math.sin(latA) * Math.cos(angularDistance) +
                    Math.cos(latA) * Math.sin(angularDistance) * Math.cos(trueCourse));

    final double dlon = Math.atan2(
            Math.sin(trueCourse) * Math.sin(angularDistance) * Math.cos(latA),
            Math.cos(angularDistance) - Math.sin(latA) * Math.sin(lat));

    final double lon = ((lonA + dlon + Math.PI) % (Math.PI * 2)) - Math.PI;

    return new LatLng(lat * RadiansToDegrees, lon * RadiansToDegrees);
}

嗯,这看起来容易多了:-)谢谢。我现在就试试。40.075的幻数是多少。一公里所覆盖的秒数(度)是多少?那是地球的周长,尽管试一下。这与直觉不符,因此可能也需要调整。KevDog的意思是,40075随着纬度的增加而减小-靠近极点以东1公里处的经度将增加到赤道以东1公里以上。因此,您需要用给定纬度的周长替换40075,即:2.pi.rs.cos(lat),其中rs是地球的半径,lat是纬度。它确实假设地球是球形的。但是,如果你向东走1公里然后向北走1公里,而不是向北走1公里然后向东走1公里,这种方法将产生不同的结果。如果你只是将度添加到原始位置,为什么不将经度保持不变(因为他只是向北走,经度不应该改变),只将km change/40075添加到lat?这个方程式对我来说似乎很脆弱,而且缺少很多数据。当有非常全面的方程描述如何计算时,为什么要偏离直觉?有谁能给我一个老km north/old km east指的是什么线索吗?我正在java中尝试这种方法,但它不起作用。我得到的是dlon=0的值。这是我的dlon代码:double dlon=Math.atan2(Math.sin(brng)*Math.sin(dist)*Math.cos(lat1),Math.cos(dist)-Math.sin(lat1)*Math.sin(lat2));我已经将纬度和经度的值转换为弧度。每个代码都是按照您所说的那样编写的。我无法计算出UnitConstants.DegreesToRadians、GeospatialConstants.EarthRadius、UnitConstants.TwoPi和UnitConstants.RadiansToDegrees是什么?:(有人能帮忙吗?EarthRadius=6378137.0,TwoPi=Math.PI*2,DegreesToradius=0.0174532925,Rad
mod=y - x * int(y/x)
if ( mod < 0) mod = mod + x
using System.Device.Location; // add reference to System.Device.dll
public static class GeoUtils
{
    /// <summary>
    /// Calculates the end-point from a given source at a given range (meters) and bearing (degrees).
    /// This methods uses simple geometry equations to calculate the end-point.
    /// </summary>
    /// <param name="source">Point of origin</param>
    /// <param name="range">Range in meters</param>
    /// <param name="bearing">Bearing in degrees</param>
    /// <returns>End-point from the source given the desired range and bearing.</returns>
    public static GeoCoordinate CalculateDerivedPosition(this GeoCoordinate source, double range, double bearing)
    {
        var latA = source.Latitude * DegreesToRadians;
        var lonA = source.Longitude * DegreesToRadians;
        var angularDistance = range / EarthRadius;
        var trueCourse = bearing * DegreesToRadians;

        var lat = Math.Asin(
            Math.Sin(latA) * Math.Cos(angularDistance) +
            Math.Cos(latA) * Math.Sin(angularDistance) * Math.Cos(trueCourse));

        var dlon = Math.Atan2(
            Math.Sin(trueCourse) * Math.Sin(angularDistance) * Math.Cos(latA),
            Math.Cos(angularDistance) - Math.Sin(latA) * Math.Sin(lat));

        var lon = ((lonA + dlon + Math.PI) % (Math.PI*2)) - Math.PI;

        return new GeoCoordinate(
            lat * RadiansToDegrees,
            lon * RadiansToDegrees,
            source.Altitude);
    }

    private const double DegreesToRadians = Math.PI/180.0;
    private const double RadiansToDegrees = 180.0/ Math.PI;
    private const double EarthRadius = 6378137.0;
}
[TestClass]
public class CalculateDerivedPositionUnitTest
{
    [TestMethod]
    public void OneDegreeSquareAtEquator()
    {
        var center = new GeoCoordinate(0, 0);
        var radius = 111320;
        var southBound = center.CalculateDerivedPosition(radius, -180);
        var westBound = center.CalculateDerivedPosition(radius, -90);
        var eastBound = center.CalculateDerivedPosition(radius, 90);
        var northBound = center.CalculateDerivedPosition(radius, 0);

        Console.Write($"leftBottom: {southBound.Latitude} , {westBound.Longitude} rightTop: {northBound.Latitude} , {eastBound.Longitude}");
    }
}
/**
 * move latlng point by rang and bearing
 *
 * @param latLng  point
 * @param range   range in meters
 * @param bearing bearing in degrees
 * @return new LatLng
 */
public static LatLng moveLatLng(LatLng latLng, double range, double bearing) {
    double EarthRadius = 6378137.0;
    double DegreesToRadians = Math.PI / 180.0;
    double RadiansToDegrees = 180.0 / Math.PI;

    final double latA = latLng.latitude * DegreesToRadians;
    final double lonA = latLng.longitude * DegreesToRadians;
    final double angularDistance = range / EarthRadius;
    final double trueCourse = bearing * DegreesToRadians;

    final double lat = Math.asin(
            Math.sin(latA) * Math.cos(angularDistance) +
                    Math.cos(latA) * Math.sin(angularDistance) * Math.cos(trueCourse));

    final double dlon = Math.atan2(
            Math.sin(trueCourse) * Math.sin(angularDistance) * Math.cos(latA),
            Math.cos(angularDistance) - Math.sin(latA) * Math.sin(lat));

    final double lon = ((lonA + dlon + Math.PI) % (Math.PI * 2)) - Math.PI;

    return new LatLng(lat * RadiansToDegrees, lon * RadiansToDegrees);
}