我正在尝试生成随机坐标 (lat,long),该坐标位于半径为 5 公里的圆内,圆心位于某些坐标 (x, y)。我正在尝试用 ruby 对此进行编码,并且我正在使用该方法,但不知何故我得到的结果不在指定的 5 公里半径内。
def location(lat, lng, max_dist_meters)
max_radius = Math.sqrt((max_dist_meters ** 2) / 2.0)
lat_offset = rand(10 ** (Math.log10(max_radius / 1.11)-5))
lng_offset = rand(10 ** (Math.log10(max_radius / 1.11)-5))
lat += [1,-1].sample * lat_offset
lng += [1,-1].sample * lng_offset
lat = [[-90, lat].max, 90].min
lng = [[-180, lng].max, 180].min
[lat, lng]
end
最佳答案
你的代码
max_radius = Math.sqrt((max_dist_meters ** 2) / 2.0)
这只是 max_dist_meters.abs / Math.sqrt(2)
或 max_dist_meters * 0.7071067811865475
.
10 ** (Math.log10(max_radius / 1.11)-5)
这可以写成9.00901E-6 * max_radius
, 所以它是 6.370325E−6 * max_dist_meters
.
rand(10 ** (Math.log10(max_radius / 1.11)-5))
现在是有趣的部分:rand(x)
只是 rand()
如果 x 在 -1
之间和 1
.所以如果max_dist_meters
小于 1/6.370325E−6 ~ 156977.86
,你所有的前 3 行都是:
lat_offset = rand()
lng_offset = rand()
所以对于 max_dist_meters = 5000
,您的方法将返回一个随机点,该点可能相距 1° 经度和 1° 纬度。最多也就157公里多一点。
更糟的是,如果x
介于 156978
之间和 313955
,您的代码相当于:
lat_offset = lng_offset = 0
从 Ruby 2.4 开始
[[-90, lat].max, 90].min
可以写成 lat.clamp(-90, 90)
可能的解决方案
在半径为max_radius
的圆盘上得到均匀分布的随机点, 你需要 a non-uniform distribution of random radii :
def random_point_in_disk(max_radius)
r = max_radius * rand ** 0.5
theta = rand * 2 * Math::PI
[r * Math.cos(theta), r * Math.sin(theta)]
end
这是一个包含一百万个随机点的图:
这是与@Schwern 的代码相同的情节:
一旦掌握了这种方法,您就可以应用一些基本的数学运算将米转换为纬度和经度。请记住,纬度 1° 始终为 111.2 公里,而经度 1° 在赤道处始终为 111.2 公里,而在两极处为 0 公里:
def random_point_in_disk(max_radius)
r = max_radius * rand**0.5
theta = rand * 2 * Math::PI
[r * Math.cos(theta), r * Math.sin(theta)]
end
EarthRadius = 6371 # km
OneDegree = EarthRadius * 2 * Math::PI / 360 * 1000 # 1° latitude in meters
def random_location(lon, lat, max_radius)
dx, dy = random_point_in_disk(max_radius)
random_lat = lat + dy / OneDegree
random_lon = lon + dx / ( OneDegree * Math::cos(lat * Math::PI / 180) )
[random_lon, random_lat]
end
对于这种计算,不需要安装 800 磅重的 GIS gorilla 。
几点:
- 我们通常先谈纬度再谈经度,但在 GIS 中,通常是
lon
首先是因为x
在y
之前. -
cos(lat)
被认为是常数,所以max_radius
不应该太大。几十公里应该没有问题。圆盘在球体上的形状变为 weird with a large radius . - 不要在太靠近两极的地方使用此方法,否则会得到任意大的坐标。
为了测试它,让我们在不同位置的 200 公里圆盘上创建随机点:
10_000.times do
[-120, -60, 0, 60, 120].each do |lon|
[-85, -45, 0, 45, 85].each do |lat|
puts random_location(lon, lat, 200_000).join(' ')
end
end
end
使用 gnuplot,结果图如下:
耶!我刚刚 reshape 了 Tissot's indicatrix ,晚了 150 年:
关于ruby - 如何在指定半径的圆内生成随机坐标?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43195899/