你的代码
max_radius = Math.sqrt((max_dist_meters ** 2) / 2.0)
这只是max_dist_meters.abs / Math.sqrt(2)
or 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() https://ruby-doc.org/core-2.2.3/Kernel.html#method-i-rand如果 x 介于-1
and 1
. So if max_dist_meters
小于1/6.370325E−6 ~ 156977.86
,你的前 3 行所做的就是:
lat_offset = rand()
lng_offset = rand()
So for max_dist_meters = 5000
,您的方法将返回一个随机点,该点可能位于 1° 经度和 1° 纬度之外。最多也就157公里多一点。
更糟糕的是,如果x
在。。。之间156978
and 313955
,您的代码相当于:
lat_offset = lng_offset = 0
从 Ruby 2.4 开始
[[-90, lat].max, 90].min
可以写成lat.clamp(-90, 90) https://ruby-doc.org/core-2.4.0/Comparable.html#method-i-clamp
可能的解决方案
得到半径圆盘上均匀分布的随机点max_radius
, 你需要随机半径的非均匀分布 http://mathworld.wolfram.com/DiskPointPicking.html :
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 大猩猩。
几点:
- 我们通常先讨论纬度,然后讨论经度,但在 GIS 中,通常是
lon
首先因为x
出现在之前y
.
-
cos(lat)
被认为是常数,所以max_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,结果图如下:
耶!我刚刚重新发明天梭指标 https://en.wikipedia.org/wiki/Tissot%27s_indicatrix,晚了 150 年: