ruby - 如何在指定半径的圆内生成随机坐标?

标签 ruby

我正在尝试生成随机坐标 (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

这是一个包含一百万个随机点的图:

Uniform distribution

这是与@Schwern 的代码相同的情节:

Non-uniform distribution

一旦掌握了这种方法,您就可以应用一些基本的数学运算将米转换为纬度和经度。请记住,纬度 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首先是因为xy 之前.
  • 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,结果图如下:

enter image description here

耶!我刚刚 reshape 了 Tissot's indicatrix ,晚了 150 年:

enter image description here

关于ruby - 如何在指定半径的圆内生成随机坐标?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43195899/

相关文章:

ruby - NoMethodError : undefined method `split' for #<Proc: . ..> 法拉第

mysql - Rails 异常 Mysql::Error: query: not connected: SHOW FULL FIELDS FROM `publications`

ruby-on-rails - File.read 返回 nil

ruby - 工厂女孩 - 目的是什么?

mysql - Ruby:Rails:MRI 和 JRuby

ruby - 如何使用 Mongoid 删除特定数据库?

ruby - compass 路径需要更正

css - 在 Rails 中添加 CSS 自定义样式失败

ruby-on-rails - ActiveRecord 如何将现有记录添加到 has_many :through relationship in rails? 中的关联

python - 为什么负数在求平方时常常需要括号才能得到预期的结果?