从旧的经纬度加上n米计算新的经纬度

153
我希望基于一个坐标和距离(单位为米),创建两个新的经度和两个新的纬度,以便在特定点周围创建一个漂亮的边界框。这是城市的一部分,最大范围为±1500米,因此我认为不需要考虑地球曲率。
所以,给定 50.0452345 (x) 和 4.3242234 (y),我想知道 x + 500 米,x - 500 米,y - 500 米,y + 500 米。
我找到了许多算法,但几乎所有算法似乎都处理点之间的距离。

http://gis.stackexchange.com/questions/25877/how-to-generate-random-locations-nearby-my-location - MilapTank
13个回答

180

每经过一度经度所对应的公里数大约为

(pi/180) * r_earth * cos(theta*pi/180)

其中theta为纬度(以度为单位),r_earth约为6378公里。

每度纬度对应的公里数在所有位置大致相同,约为:

(pi/180) * r_earth = 111 km / degree 

所以你可以这样做:

new_latitude  = latitude  + (dy / r_earth) * (180 / pi);
new_longitude = longitude + (dx / r_earth) * (180 / pi) / cos(latitude * pi/180);
只要dxdy与地球半径相比较小,并且不靠近极地,就可以。

5
将角度转换为弧度,需要用 π 乘以角度并除以180。但在计算 cos(latitude*180/pi) 时使用的是角度制。 - josch
14
@josch: 好发现。下次尝试纠正答案而不只是提出更正建议。许多人仅仅从StackOverflow复制粘贴代码,认为它是正确的并且可以直接使用。 - Alex Essilfie
8
好的,方向将是什么?我的意思是如果我想添加50米,它会被添加在哪里?右边,左边,上面还是下面? - Tushar Monirul
3
地球并非完美的球形,因此使用单一数值表示“半径”是一种近似值。维基百科称:“从地表某点到中心的距离范围为6,353公里至6,384公里”。它还说:“将地球建模为一个球体有几种不同的方法,每种方法都会得出平均半径为6,371公里”的结果,这也证实了你的数值。如果这种修正在你的应用程序中具有重要意义,那么你应该使用更好的算法。 - nibot
6
如果有人不确定,r_earth 变量应该是以米为单位,并且大约等于 6371000.0。 - Amit Assaraf
显示剩余4条评论

46

接受的答案是完全正确并且可行的。我进行了一些微调并将其转换为以下内容:

double meters = 50;

// number of km per degree = ~111km (111.32 in google maps, but range varies
// between 110.567km at the equator and 111.699km at the poles)
//
// 111.32km = 111320.0m (".0" is used to make sure the result of division is
// double even if the "meters" variable can't be explicitly declared as double)
double coef = meters / 111320.0;

double new_lat = my_lat + coef;

// pi / 180 ~= 0.01745
double new_long = my_long + coef / Math.cos(my_lat * 0.01745);

希望这也能有所帮助。

33
"0.0000089"?请避免使用神秘数字,这会让读者感到困惑。 - scai
3
这是关于地球直径和圆周率的代码的简短版本,不是什么魔法。 - Numan Karaaslan
20
如果没有人知道如何重现这个数字,那么它仍然是一种魔法。为什么不将完整的计算放入你的代码中? - scai
15
在Google地图中,1度等于111.32千米。 1度 = 111.32千米。 1千米在度数上的表示 = 1/111.32 = 0.008983。 1米在度数上的表示 = 0.000008983。 - Muhammad Azeem
8
你应该在回答中发表评论,将其放在评论中并不有帮助。 - chutsu
显示剩余8条评论

40

对于纬度,请执行以下操作:

var earth = 6378.137,  //radius of the earth in kilometer
    pi = Math.PI,
    m = (1 / ((2 * pi / 360) * earth)) / 1000;  //1 meter in degree

var new_latitude = latitude + (your_meters * m);

对于经度执行以下操作:

var earth = 6378.137,  //radius of the earth in kilometer
    pi = Math.PI,
    cos = Math.cos,
    m = (1 / ((2 * pi / 360) * earth)) / 1000;  //1 meter in degree

var new_longitude = longitude + (your_meters * m) / cos(latitude * (pi / 180));

变量your_meters可以包含正值或负值。


非常感谢,解决了我的问题。但我认为如果您能添加更多关于位的说明信息,特别是m以及其中发生的情况,那将会更好。 - Hossein

15

我花了大约两个小时来解决@nibot提供的问题,我只需要一个方法来根据中心点和宽度/高度(或半径)以公里为单位创建边界框:

数学/地理上,我没有完全理解解决方案。通过试错,我调整了解决方案,以获得四个坐标。在当前位置和移动到四个坐标的新位置的距离(以公里为单位):

北:

private static Position ToNorthPosition(Position center, double northDistance)
{
    double r_earth = 6378;
    var pi = Math.PI;
    var new_latitude = center.Lat + (northDistance / r_earth) * (180 / pi);
    return new Position(new_latitude, center.Long);
}

东:

private static Position ToEastPosition(Position center, double eastDistance)
{
    double r_earth = 6378;
    var pi = Math.PI;
    var new_longitude = center.Long + (eastDistance / r_earth) * (180 / pi) / Math.Cos(center.Lat * pi / 180);
    return new Position(center.Lat, new_longitude);
}

南方:

private static Position ToSouthPosition(Position center, double southDistance)
{
    double r_earth = 6378;
    var pi = Math.PI;
    var new_latitude = center.Lat - (southDistance / r_earth) * (180 / pi);
    return new Position(new_latitude, center.Long);
}

西方:

private static Position ToWestPosition(Position center, double westDistance)
{
    double r_earth = 6378;
    var pi = Math.PI;
    var new_longitude = center.Long - (westDistance / r_earth) * (180 / pi) / Math.Cos(center.Lat * pi / 180);
    return new Position(center.Lat, new_longitude);
}

10

如果您的工作范围相对较小,仅使用纬度-0.09和经度-0.0148来获取大约1平方公里的区域是否真的很糟糕? - Ben
我认为这并不是真的很糟糕。在那个级别上,平方公里不会因地球曲率而扭曲 - 只要你处理的纬度/经度是十进制的。 - Ryan Ternier
1
@BenjaminUdinktenCate 这在阿姆斯特丹可能有效,但在世界其他地方将不准确。在赤道上执行“经度-0.0148”只会让您获得约0.16公里的距离。 - nibot

5

出于完整性考虑发布此方法。

直接使用此方法来:

  • 将任意(纬度、经度)点在任一轴上移动给定的米数。

Python方法,可将任意点移动指定米数。

def translate_latlong(lat,long,lat_translation_meters,long_translation_meters):
    ''' method to move any lat,long point by provided meters in lat and long direction.
    params :
        lat,long: lattitude and longitude in degrees as decimal values, e.g. 37.43609517497065, -122.17226450150885
        lat_translation_meters: movement of point in meters in lattitude direction.
                                positive value: up move, negative value: down move
        long_translation_meters: movement of point in meters in longitude direction.
                                positive value: left move, negative value: right move
        '''
    earth_radius = 6378.137

    #Calculate top, which is lat_translation_meters above
    m_lat = (1 / ((2 * math.pi / 360) * earth_radius)) / 1000;  
    lat_new = lat + (lat_translation_meters * m_lat)

    #Calculate right, which is long_translation_meters right
    m_long = (1 / ((2 * math.pi / 360) * earth_radius)) / 1000;  # 1 meter in degree
    long_new = long + (long_translation_meters * m_long) / math.cos(lat * (math.pi / 180));
    
    return lat_new,long_new

2

将坐标偏移10米的Python工作代码。

def add_blur(lat, long):
meters = 10
blur_factor = meters * 0.000006279
new_lat = lat + blur_factor
new_long = long + blur_factor / math.cos(lat * 0.018)
return new_lat, new_long

你使用的神奇数字 0.00006279 可能会导致巨大的偏移。请将其替换为以下值:```earth_radius_in_km = 6378.137
coeff = (1 / ((2 * math.pi / 360) * earth_radius_in_km)) / 1000
blur_factor = meters * coeff # 根据南北方向,在米数上使用 - 或 + 通过应用此更改,我的偏移量从36米缩小到了约10厘米!
- Hossein

1
public double MeterToDegree(double meters, double latitude)
{
    return meters / (111.32 * 1000 * Math.Cos(latitude * (Math.PI / 180)));
}

1
如果您不需要非常精确,那么每10000米约为0.1纬度和经度。例如,我想从我的数据库中加载距离点A周围3000米的位置。
double newMeter =  3000 * 0.1 / 10000;
double lat1 = point_A.latitude - newMeter;
double lat2 = point_A.latitude + newMeter;
double lon1 = point_A.longitude - newMeter;
double lon1 = point_A.longitude + newMeter;
Cursor c = mDb.rawQuery("select * from TABLE1  where lat >= " + lat1 + " and lat <= " + lat2 + " and lon >= " + lon1 + " and lon <= " + lon2 + " order by id", null);

0

原帖作者说: “我有50.0452345 (x) 和 4.3242234 (y),我想知道 x + 500 米…”

我假设他给出的 x 和 y 值的单位是米(而不是经度、纬度)。如果是这样,那么他陈述了到 0.1 微米的测量值,因此我假设他对翻译输出需要类似的精度。我还假设他所指的“+500米”等意味着朝南北方向和朝东西方向前进。 他提到了参考点:“基于一个坐标的两个新纬度”,但他没有给出经度和纬度,因此为了具体解释该过程,我将给出他请求的围绕[30 度经度,30 度纬度]点的 500 米方框角落的纬度和经度。

GRS80 椭球面上的精确解可使用以下函数集给出 (我为名为“PARI”的免费开源数学程序编写了这些函数,它可以设置任意位数的精度):

\\=======Arc lengths along Latitude and Longitude and the respective scales:
dms(u)=[truncate(u),truncate((u-truncate(u))*60),((u-truncate(u))*60-truncate((u-truncate(u))*60))*60];
SpinEarthRadiansPerSec=7.292115e-5;\
GMearth=3986005e8;\
J2earth=108263e-8;\
re=6378137;\
ecc=solve(ecc=.0001,.9999,eccp=ecc/sqrt(1-ecc^2);qecc=(1+3/eccp^2)*atan(eccp)-3/eccp;ecc^2-(3*J2earth+4/15*SpinEarthRadiansPerSec^2*re^3/GMearth*ecc^3/qecc));\
e2=ecc^2;\
b2=1-e2;\
b=sqrt(b2);\
fl=1-b;\
rfl=1/fl;\
U0=GMearth/ecc/re*atan(eccp)+1/3*SpinEarthRadiansPerSec^2*re^2;\
HeightAboveEllipsoid=0;\
reh=re+HeightAboveEllipsoid;\
longscale(lat)=reh*Pi/648000/sqrt(1+b2*(tan(lat))^2);
latscale(lat)=reh*b*Pi/648000/(1-e2*(sin(lat))^2)^(3/2);
longarc(lat,long1,long2)=longscale(lat)*648000/Pi*(long2-long1);
latarc(lat1,lat2)=(intnum(th=lat1,lat2,sqrt(1-e2*(sin(th))^2))+e2/2*sin(2*lat1)/sqrt(1-e2*(sin(lat1))^2)-e2/2*sin(2*lat2)/sqrt(1-e2*(sin(lat2))^2))*reh;
\\=======

然后我在PARI命令提示符中将参考点[30,30]输入这些函数,并让PARI解决距离该点+/- 500米的点,给出了原始帖子所要求的两个新经度和两个新纬度。以下是输入和输出示例:

? dms(solve(x=29,31,longarc(30*Pi/180,30*Pi/180,x*Pi/180)+500))
cpu time = 1 ms, real time = 1 ms.
%1172 = [29, 59, 41.3444979398934670450280297216509190843055]
? dms(solve(x=29,31,longarc(30*Pi/180,30*Pi/180,x*Pi/180)-500))
cpu time = 1 ms, real time = 1 ms.
%1173 = [30, 0, 18.6555020601065329549719702783490809156945]
? dms(solve(x=29,31,latarc(30*Pi/180,x*Pi/180)+500))
cpu time = 1,357 ms, real time = 1,358 ms.
%1174 = [29, 59, 43.7621925447500548285775757329518579545513]
? dms(solve(x=29,31,latarc(30*Pi/180,x*Pi/180)-500))
cpu time = 1,365 ms, real time = 1,368 ms.
%1175 = [30, 0, 16.2377963202802863245716034907838199823349]
? 

理想情况下,这个程序应该能够在全球范围内运行,使用任何给定的参数集。 - user11717481

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接