获取经纬度给定的当前点,距离和方位


77

给定一个现有的经度/纬度点,距离(以KM为单位)和方位角(以度为单位转换成弧度),我想计算新的经度/纬度。这个站点一遍又一遍地出现,但我只是无法找到适合我的公式。

上述链接采用的公式为:

lat2 = asin(sin(lat1)*cos(d/R) + cos(lat1)*sin(d/R)*cos(θ))

lon2 = lon1 + atan2(sin(θ)*sin(d/R)*cos(lat1), cos(d/R)−sin(lat1)*sin(lat2))

上面的公式适用于MSExcel,其中-

asin          = arc sin()   
d             = distance (in any unit)   
R             = Radius of the earth (in the same unit as above)  
and hence d/r = is the angular distance (in radians)  
atan2(a,b)    = arc tan(b/a)  
θ is the bearing (in radians, clockwise from north);  

这是我在Python中获得的代码。

import math

R = 6378.1 #Radius of the Earth
brng = 1.57 #Bearing is 90 degrees converted to radians.
d = 15 #Distance in km

#lat2  52.20444 - the lat result I'm hoping for
#lon2  0.36056 - the long result I'm hoping for.

lat1 = 52.20472 * (math.pi * 180) #Current lat point converted to radians
lon1 = 0.14056 * (math.pi * 180) #Current long point converted to radians

lat2 = math.asin( math.sin(lat1)*math.cos(d/R) +
             math.cos(lat1)*math.sin(d/R)*math.cos(brng))

lon2 = lon1 + math.atan2(math.sin(brng)*math.sin(d/R)*math.cos(lat1),
                     math.cos(d/R)-math.sin(lat1)*math.sin(lat2))

print(lat2)
print(lon2)

我懂了

lat2 = 0.472492248844 
lon2 = 79.4821662373

1
@GWW我得到的答案没有意义。之所以没有意义,是因为我没有将答案转换回学位。代码已更改,并包含在原始帖子中作为编辑。
David M

6
您只需将您的编辑作为答案提交,然后接受该答案,就可以更清楚地说明您已解决了自己的问题。否则,SO将对您留下未解决的问题进行处罚,从而使将来的用户不太愿意回答您的问题。
塞林

如果使用numpy对象,则将获得更好的精度和结果。
Mike Pennington

@Cerin-感谢您的建议。
David M

不应该是“ lat1 = 52.20472 *(math.pi * / 180)”吗?
Tushar Seth,

Answers:


90

需要将答案从弧度转换回度。下面的工作代码:

import math

R = 6378.1 #Radius of the Earth
brng = 1.57 #Bearing is 90 degrees converted to radians.
d = 15 #Distance in km

#lat2  52.20444 - the lat result I'm hoping for
#lon2  0.36056 - the long result I'm hoping for.

lat1 = math.radians(52.20472) #Current lat point converted to radians
lon1 = math.radians(0.14056) #Current long point converted to radians

lat2 = math.asin( math.sin(lat1)*math.cos(d/R) +
     math.cos(lat1)*math.sin(d/R)*math.cos(brng))

lon2 = lon1 + math.atan2(math.sin(brng)*math.sin(d/R)*math.cos(lat1),
             math.cos(d/R)-math.sin(lat1)*math.sin(lat2))

lat2 = math.degrees(lat2)
lon2 = math.degrees(lon2)

print(lat2)
print(lon2)

对我来说也一样
Boden


我注意到,如果原始纬度为0,原始经度为-179,方位角为270度(1.5pi弧度),并且距离为1500km,则结果经度为-192.4,这在地图上不存在。
JVE999'5


我使用以下代码验证了代码输出:fcc.gov/media/radio/find-terminal-coordinates
Michael Behrens


14

这个问题大地测量学中被称为直接问题

这确实是一个非常受欢迎的问题,并且一直是造成混乱的原因。原因是大多数人都在寻找简单直接的答案。但是没有,因为大多数人问这个问题并没有提供足够的信息,仅仅是因为他们不知道:

  1. 地球不是一个完美的球体,因为它被两极弄平/压缩
  2. 由于(1),地球没有恒定的半径,R。看这里
  3. 地球不是十分光滑(高度变化)等。
  4. 由于构造板块的运动,某个地理位置的纬度/经度位置每年可能会变化几毫米(至少)。

因此,根据所需的精度,在各种几何模型中使用了许多不同的假设,这些假设的适用范围有所不同。因此,要回答这个问题,您需要考虑您想要得到什么精度的结果。

一些例子:

  • 我只是在寻找N | S之间小的(< 100 km)距离的最近位置的近似位置。(地球是平坦的模型。)latitudes0-70 deg
  • 我想要一个在全球任何地方都很好的答案,但只能精确到几米
  • 我想要一个超精确的定位,该定位有效至nanometers[nm]的原子级。
  • 我想要的答案是非常快速,易于计算且不占用大量计算资源。

因此,您可以选择使用哪种算法。此外,每种编程语言都有自己的实现或“包”,乘以模型数量以及模型开发人员的特定需求。出于所有实际目的,不理会其他任何语言都是javascript值得的,因为它的性质与伪代码非常相似。因此,只需很少的更改即可轻松将其转换为任何其他语言。

那么主要模型是:

  • Euclidian/Flat earth model:适用于约10 km以下的短距离
  • Spherical model:适用于较大的纵向距离,但横向差异较小。流行型号:
    • Haversine米制精度[km],非常简单的代码。
  • Ellipsoidal models:在任何纬度/经度和距离上都最精确,但是仍然是数值近似值,具体取决于所需的精度。一些流行的模型是:

参考文献:


13

回答可能有点晚,但是在测试了其他答案后,看来它们无法正常工作。这是我们用于系统的PHP代码。四面八方工作。

PHP代码:

lat1 =起点的纬度(度)

long1 =起点经度(以度为单位)

d =以KM为单位的距离

角度=方位角

function get_gps_distance($lat1,$long1,$d,$angle)
{
    # Earth Radious in KM
    $R = 6378.14;

    # Degree to Radian
    $latitude1 = $lat1 * (M_PI/180);
    $longitude1 = $long1 * (M_PI/180);
    $brng = $angle * (M_PI/180);

    $latitude2 = asin(sin($latitude1)*cos($d/$R) + cos($latitude1)*sin($d/$R)*cos($brng));
    $longitude2 = $longitude1 + atan2(sin($brng)*sin($d/$R)*cos($latitude1),cos($d/$R)-sin($latitude1)*sin($latitude2));

    # back to degrees
    $latitude2 = $latitude2 * (180/M_PI);
    $longitude2 = $longitude2 * (180/M_PI);

    # 6 decimal for Leaflet and other system compatibility
   $lat2 = round ($latitude2,6);
   $long2 = round ($longitude2,6);

   // Push in array and get back
   $tab[0] = $lat2;
   $tab[1] = $long2;
   return $tab;
 }

1
看起来不错,但是我认为请求者希望在python中有一些东西。错误?
user2360915

可能会更好地命名get_gps_coord或类似。您没有得到距离,而是将其提供给功能。但是,为此,这正是我想要的。许多搜索返回坐标之间的计算距离(误报)。谢谢!
Dev Null

太棒了!感谢您的贡献!
jkamizato

1
6,378.14 km似乎是地球的最大半径。平均值约为6,371.0 km,这可能允许更准确的计算。
JVE999 '20

7

我将Brad的答案移植到了香草JS答案,没有Bing地图的依赖

https://jsfiddle.net/kodisha/8a3hcjtd/

    // ----------------------------------------
    // Calculate new Lat/Lng from original points
    // on a distance and bearing (angle)
    // ----------------------------------------
    let llFromDistance = function(latitude, longitude, distance, bearing) {
      // taken from: https://stackoverflow.com/a/46410871/13549 
      // distance in KM, bearing in degrees
    
      const R = 6378.1; // Radius of the Earth
      const brng = bearing * Math.PI / 180; // Convert bearing to radian
      let lat = latitude * Math.PI / 180; // Current coords to radians
      let lon = longitude * Math.PI / 180;
    
      // Do the math magic
      lat = Math.asin(Math.sin(lat) * Math.cos(distance / R) + Math.cos(lat) * Math.sin(distance / R) * Math.cos(brng));
      lon += Math.atan2(Math.sin(brng) * Math.sin(distance / R) * Math.cos(lat), Math.cos(distance / R) - Math.sin(lat) * Math.sin(lat));
    
      // Coords back to degrees and return
      return [(lat * 180 / Math.PI), (lon * 180 / Math.PI)];
    
    }
    
    let pointsOnMapCircle = function(latitude, longitude, distance, numPoints) {
      const points = [];
      for (let i = 0; i <= numPoints - 1; i++) {
        const bearing = Math.round((360 / numPoints) * i);
        console.log(bearing, i);
        const newPoints = llFromDistance(latitude, longitude, distance, bearing);
        points.push(newPoints);
      }
      return points;
    }
    
    const points = pointsOnMapCircle(41.890242042122836, 12.492358982563019, 0.2, 8);
    let geoJSON = {
      "type": "FeatureCollection",
      "features": []
    };
    points.forEach((p) => {
      geoJSON.features.push({
        "type": "Feature",
        "properties": {},
        "geometry": {
          "type": "Point",
          "coordinates": [
            p[1],
            p[0]
          ]
        }
      });
    });
    
    document.getElementById('res').innerHTML = JSON.stringify(geoJSON, true, 2);

此外,我添加了geoJSON导出功能,因此您只需将生成的geoJSON粘贴到:http://geojson.io/#map=17/41.89017/12.49171即可立即查看结果。

结果: geoJSON屏幕截图


以GeoJSON的地图是非常有帮助的,我的目标在地图中的位置
cloudscomputes

谢谢@kodisha,您的小提琴对我有很大帮助!
道格拉斯·施密特

5

使用Geopy的快速方法

from geopy import distance
#distance.distance(unit=15).destination((lat,lon),bering) 
#Exemples
distance.distance(nautical=15).destination((-24,-42),90) 
distance.distance(miles=15).destination((-24,-42),90)
distance.distance(kilometers=15).destination((-24,-42),90) 

2
没有说明您用于计算的方法,答案基本上是没有用的。
not2qubit

1
@ not2qubit无论@ plinio-bueno-andrade-silva是否意识到,geopy.distance.distance currently uses geodesic. geopy更具体地说,默认情况下使用的椭球模型是WGS-84椭球,“在全球范围内最精确”。
hlongmore '19

3

lon1和lat1(以度为单位)

brng =弧度轴承

d =距离(公里)

R =以km为单位的地球半径

lat2 = math.degrees((d/R) * math.cos(brng)) + lat1
long2 = math.degrees((d/(R*math.sin(math.radians(lat2)))) * math.sin(brng)) + long1

我实现了您的算法,并在PHP中进行了基准测试。此版本的运行时间约为50%。生成的结果是相同的,因此在数学上似乎是等效的。

我没有测试上面的python代码,因此可能存在语法错误。


不工作 从北到南,结果是正确的,但在“东西向”方向上是错误的。
彼得

1

同样晚了,但对于那些可能会发现此问题的人,您将使用geoliblib库获得更准确的结果。查看测地线问题描述和JavaScript示例,以轻松地介绍如何使用答案以及其他许多问题。用多种语言实现,包括Python。如果您关心准确性,则比编码自己的代码好得多;比之前的“使用图书馆”建议中的VincentyDistance更好。正如文档所述:“重点在于返回误差接近四舍五入(约5至15纳米)的准确结果。”


1

只需交换atan2(y,x)函数中的值即可。不是atan2(x,y)!


1

我将Python移植到Javascript。这将返回一个Bing MapsLocation对象,您可以更改为任何您喜欢的对象。

getLocationXDistanceFromLocation: function(latitude, longitude, distance, bearing) {
    // distance in KM, bearing in degrees

    var R = 6378.1,                         // Radius of the Earth
        brng = Math.radians(bearing)       // Convert bearing to radian
        lat = Math.radians(latitude),       // Current coords to radians
        lon = Math.radians(longitude);

    // Do the math magic
    lat = Math.asin(Math.sin(lat) * Math.cos(distance / R) + Math.cos(lat) * Math.sin(distance / R) * Math.cos(brng));
    lon += Math.atan2(Math.sin(brng) * Math.sin(distance / R) * Math.cos(lat), Math.cos(distance/R)-Math.sin(lat)*Math.sin(lat));

    // Coords back to degrees and return
    return new Microsoft.Maps.Location(Math.degrees(lat), Math.degrees(lon));

},

请发布功能代码,包括需要运行的代码。即,这似乎取决于Microsoft.Maps。在哪里找到/如何安装?
not2qubit

如果您的程序使用Bing地图,则只能使用Bing(Microsoft)地图。只需采用Math.degrees(lat)Math.degrees(lon)值,并根据需要对它们进行处理即可。
布拉德·马修斯

0

这是一个基于Ed Williams Aviation Formulary的PHP版本。在PHP中,Modulus的处理方式略有不同。这对我有用。

function get_new_waypoint ( $lat, $lon, $radial, $magvar, $range )
{

   // $range in nm.
   // $radial is heading to or bearing from    
   // $magvar for local area.

   $range = $range * pi() /(180*60);
   $radial = $radial - $magvar ;

   if ( $radial < 1 )
     {
        $radial = 360 + $radial - $magvar; 
     }
   $radial =  deg2rad($radial);
   $tmp_lat = deg2rad($lat);
   $tmp_lon = deg2rad($lon);
   $new_lat = asin(sin($tmp_lat)* cos($range) + cos($tmp_lat) * sin($range) * cos($radial));
   $new_lat = rad2deg($new_lat);
   $new_lon = $tmp_lon - asin(sin($radial) * sin($range)/cos($new_lat))+ pi() % 2 * pi() -  pi();
   $new_lon = rad2deg($new_lon);

   return $new_lat." ".$new_lon;

}

您能解释几个变量吗?$ range和$ magvar对于(me :)等新手读者可以使用更多的说明
Tommie

请查看我的答案,并链接到它使用的公式以及我们可以期望的准确性。
not2qubit

0

如果有人需要,我将答案从@David M移植到java ...我确实得到52.20462299620793,0.360433887489931的稍微不同的结果

    double R = 6378.1;  //Radius of the Earth
    double brng = 1.57;  //Bearing is 90 degrees converted to radians.
    double d = 15;  //Distance in km

    double lat2 = 52.20444; // - the lat result I'm hoping for
    double lon2 = 0.36056; // - the long result I'm hoping for.

    double lat1 = Math.toRadians(52.20472); //Current lat point converted to radians
    double lon1 = Math.toRadians(0.14056); //Current long point converted to radians

    lat2 = Math.asin( Math.sin(lat1)*Math.cos(d/R) +
            Math.cos(lat1)*Math.sin(d/R)*Math.cos(brng));

    lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1),
            Math.cos(d/R)-Math.sin(lat1)*Math.sin(lat2));

    lat2 = Math.toDegrees(lat2);
    lon2 = Math.toDegrees(lon2);

    System.out.println(lat2 + ", " + lon2);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.