我正在尝试生成位于半径为5千米的圆内的随机坐标(纬度,长度),其中心点位于某个坐标(x,y)处。我试图用红宝石编码,我正在使用该方法,但不知怎的,我得到的结果不在指定的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
答案 0 :(得分:11)
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
要在半径为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.2km,但1°经度在赤道为111.2km但在极点为0km:
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大猩猩。
几点:
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,这是结果图:
耶!我刚刚彻底改造了Tissot's indicatrix,已经晚了150年:
答案 1 :(得分:3)
def location(x_origin, y_origin, radius)
x_offset, y_offset = nil, nil
rad = radius.to_f
begin
x_offset = rand(-rad..rad)
y_offset = rand(-rad..rad)
end until Math.hypot(x_offset, y_offset) < radius
[x_origin + x_offset, y_origin + y_offset]
end
答案 2 :(得分:1)
我建议生成随机半径和随机角度。然后,您可以使用这些来生成Math.sin
和Math.cos
的坐标。
def location(max_radius)
# 0 to max radius.
# Using a range ensures max_radius is included.
radius = Random.rand(0.0..max_radius)
# 0 to 2 Pi excluding 2 Pi because that's just 0.
# Using Random.rand() because Kernel#rand() does not honor floats.
radians = Random.rand(2 * Math::PI)
# Math.cos/sin work in radians, not degrees.
x = radius * Math.cos(radians)
y = radius * Math.sin(radians)
return [x, y]
end
我会将此转换为lat / long并为您添加中心。
我真正建议的是找到一个支持&#34等操作的几何库;在这个形状内给我一个随机点&#34;,&#34;这个点就在这个形状内#34;并且会为你做转/长转换,因为这些东西很容易出错。
您可以在Ruby geometry gem之上构建,它为您提供基本形状的类。或者,如果您的数据位于数据库中,则许多数据支持几何类型,例如PostgreSQL's geometry types,功能更强大的PostGIS add-on,甚至MySQL has spatial data types。
答案 3 :(得分:1)
有一颗宝石可以满足您的需求。
https://github.com/sauloperez/random-location
只需安装gem并在代码中使用它:
gem install random-location
require 'random-location'
RandomLocation.near_by(41.38506, 2.17340, 10000)