如何使用随机模块生成随机地理坐标

How to generate random geocoordinates using random module

如何使用Python3random模块生成随机经纬度?我已经用谷歌搜索并阅读了文档,但没有找到执行此操作的方法。

之间生成一个random number

Latitude: -85 to +85 (actually -85.05115 for some reason)

Longitude: -180 to +180

正如@tandem 在他的回答中所写,纬度的范围几乎是 -90 to +90(它在地图上被切割),而经度的范围是 -180 to +180。要生成此范围内的随机浮点数,请使用 random.uniform 函数:

import random

# returns (lat, lon)
def randlatlon():
    return (round(random.uniform( -90,  90), 5),
            round(random.uniform(-180, 180), 5))

它被四舍五入到逗号后的 5 位数字,因为不需要额外的准确性。

同时纬度和经度使用均匀分布时的问题 是物理上,纬度不是均匀分布的。

因此,如果您打算将这些随机点用于某些统计平均计算之类的事情, 或物理蒙特卡洛模拟,结果可能不正确。

如果绘制“均匀”随机点的图形表示,它们似乎会聚集在极地地区。

为了描绘这一点,请考虑地球上位于纬度 89 到 90 度(北)之间的区域。 一个纬度的长度是 10,000/90 = 111 公里。该区域是一个半径为 111 公里的圆圈, 以北极为中心。其面积约为3.14 * 111 * 111 ≈ 39,000 km2

另一方面,考虑位于纬度 0 到 1 度之间的区域。 这是一条长 40,000 公里(赤道)、宽 111 公里的地带, 所以它的面积是444万平方公里2。比极地大很多

一个简单的算法:

一种可能性是使用 Python 库提供的高斯分布随机变量。 如果我们构建一个 3D 向量,其 3 个分量具有高斯分布,则整体 概率分布就像 exp(-x2) * exp(-y2) * exp(-z2) 但这与 exp(-(x2 + y2 + z2) 是一样的) 或 exp(-r2),其中 r 是距原点的距离。

所以这些向量没有优先方向。一旦归一化为单位长度,它们均匀 分布在单位球面上。他们用纬度分布解决了我们的问题。

想法通过以下Python代码实现:

import  math
import  random

def randlatlon1():
    pi = math.pi
    cf = 180.0 / pi  # radians to degrees Correction Factor

    # get a random Gaussian 3D vector:
    gx = random.gauss(0.0, 1.0)
    gy = random.gauss(0.0, 1.0)
    gz = random.gauss(0.0, 1.0)

    # normalize to an equidistributed (x,y,z) point on the unit sphere:
    norm2 = gx*gx + gy*gy + gz*gz
    norm1 = 1.0 / math.sqrt(norm2)
    x = gx * norm1
    y = gy * norm1
    z = gz * norm1

    radLat = math.asin(z)      # latitude  in radians
    radLon = math.atan2(y,x)   # longitude in radians

    return (round(cf*radLat, 5), round(cf*radLon, 5))

完整性检查:

欧氏几何提供了球形区域概率的公式 由 minimal/maximal 纬度和经度定义。对应的Python代码是这样的:

def computeProbaG(minLat, maxLat, minLon, maxLon):
    pi  = math.pi
    rcf = pi / 180.0  # degrees to radians Correction Factor
    lonProba = (maxLon - minLon) / 360.0
    minLatR  = rcf * minLat
    maxLatR  = rcf * maxLat
    latProba = (1.0/2.0) * (math.sin(maxLatR) - math.sin(minLatR))
    return (lonProba * latProba)

我们还可以通过随机抽样计算相同概率的近似值,使用 randlatlon1 等函数提供的随机点数 他们中的百分比恰好落在所选区域内:

def computeProbaR(randlatlon, ranCount, minLat, maxLat, minLon, maxLon):
    norm  = 1.0 / ranCount
    pairs = [randlatlon()  for i in range(ranCount)]
    acceptor = lambda p: ( (p[0] > minLat)  and  (p[0] < maxLat)     and
                           (p[1] > minLon)  and   (p[1] < maxLon) )
    selCount = sum(1 for p in filter(acceptor, pairs))
    return (norm * selCount)

有了这两个功能,我们就可以查询各种几何参数集了 几何和概率结果非常吻合,ranCount 设置为一百万个随机点:

ranCount = 1000*1000
print (" ")
probaG1 = computeProbaG(                       30, 60, 45, 90)
probaR1 = computeProbaR(randlatlon1, ranCount, 30, 60, 45, 90)
print ("probaG1 = %f" % probaG1)
print ("probaR1 = %f" % probaR1)
print (" ")
probaG2 = computeProbaG(                        10, 55, -40, 160)
probaR2 = computeProbaR(randlatlon1, ranCount,  10, 55, -40, 160)
print ("probaG2 = %f" % probaG2)
print ("probaR2 = %f" % probaR2)
print (" ")

执行输出:

$ python3 georandom.py

probaG1 = 0.022877
probaR1 = 0.022852
 
probaG2 = 0.179307
probaR2 = 0.179644

$

所以这两种数字在这里似乎是一致的。

附录:

为了完整起见,我们可以添加第二个算法,它不太直观,但源自更广泛的统计原理。

要解决纬度分布问题,我们可以使用Inverse Transform Sampling定理。为此,我们需要一些公式来计算纬度小于任意指定值的概率,φ.

纬度小于给定 φ 的单位 3D 球体区域称为 球冠。它的面积可以通过初等微积分得到,例如here

球冠面积由公式给出:A = 2π * (1 + sin(φ)) 将这个面积除以单位3D球体的总面积即4π即可得到相应的概率,对应φ=φmax=π/2。因此:

p = Proba{latitude < φ} = (1/2) * (1 + sin(φ))

或者,反过来:

φ = 反正弦 (2*p - 1)

根据逆变换采样定理,通过将概率p替换为在0和1之间均匀分布的随机变量,可以获得纬度(以弧度为单位)的公平采样。在 Python 中,这给出:

lat = math.asin(2*random.uniform(0.0, 1.0) - 1.0)

至于经度,这是一个独立的随机变量,仍然均匀分布在-π和+π之间(以弧度为单位)。所以整体 Python 采样器代码是:

def randlatlon2r():
    pi = math.pi
    cf = 180.0 / pi  # radians to degrees Correction Factor
    u0 = random.uniform(0.0, 1.0)
    u1 = random.uniform(0.0, 1.0)
    radLat = math.asin(2*u0 - 1.0)  # angle with Equator   - from +pi/2 to -pi/2
    radLon = (2*u1 - 1) * pi        # longitude in radians - from -pi to +pi 
    return (round(radLat*cf,5), round(radLon*cf,5))

已发现此代码已成功通过上述完整性检查。