支持Great Circle Distance和多边形的快速python GIS库

时间:2013-06-10 10:43:33

标签: python performance gis polygon great-circle

我在寻找python的地理库。 我需要能够做到以下几点:

  1. 使用Great-circle distance(不是线性距离计算)
  2. 获取2点(以米为单位)之间的距离
  3. 检查点是否在多边形内
  4. 每秒执行1到2次数
  5. 一开始我看过这篇文章:Python module for storing and querying geographical coordinates并开始使用geopy。 我遇到了两个问题:

    1. Geopy不支持多边形
    2. geoPy的高CPU使用率(计算点与相对5000点之间的距离需要大约140ms的CPU)
    3. 我一直在寻找Best Python GIS library?https://gis.stackexchange.com/。它看起来很有希望,因为地理位置使用的是编译的C代码,它应该更快,并且更好地支持多边形。 问题是geos / OGR执行线性距离计算而不是球体。这消除了所有其他基于地理的模块(如GEODjango和形状)。 我在这里错过了什么吗?我不认为我是第一个使用python进行GIS计算并希望获得准确结果的人。

2 个答案:

答案 0 :(得分:1)

<强>更新

现在继续完成该库中的其他576个函数,不包括已完成的两个多边形函数,完成的三个球面距离算法,以及两个新的算法,即angle_box_2d和angle_contains_ray_2d。此外,我切换到C版本,因此不需要外部,简化了工作。将旧的C ++版本放在old_c ++目录中,所以它仍然存在。

经过测试的表现,与答案底部列出的相同。


更新2

所以只是一个快速的更新,我还没有完成整个库(我只有大约15%的通过),但是我已经添加了这些未经测试的函数,以防你需要它们, github,添加到多边形和球体距离算法中的旧点。

angle_box_2d
angle_contains_ray_2d
angle_deg_2d
angle_half_2d # MLM: double *
angle_rad_2d
angle_rad_3d
angle_rad_nd
angle_turn_2d
anglei_deg_2d
anglei_rad_2d
annulus_area_2d
annulus_sector_area_2d
annulus_sector_centroid_2d # MLM: double *
ball_unit_sample_2d # MLM: double *
ball_unit_sample_3d # MLM: double *
ball_unit_sample_nd # MLM; double *
basis_map_3d #double *
box_01_contains_point_2d
box_01_contains_point_nd
box_contains_point_2d
box_contains_point_nd
box_ray_int_2d
box_segment_clip_2d
circle_arc_point_near_2d
circle_area_2d
circle_dia2imp_2d
circle_exp_contains_point_2d
circle_exp2imp_2d
circle_imp_contains_point_2d
circle_imp_line_par_int_2d
circle_imp_point_dist_2d
circle_imp_point_dist_signed_2d
circle_imp_point_near_2d
circle_imp_points_2d # MlM: double *
circle_imp_points_3d # MLM: double *
circle_imp_points_arc_2d
circle_imp_print_2d
circle_imp_print_3d
circle_imp2exp_2d
circle_llr2imp_2d # MLM: double *
circle_lune_area_2d
circle_lune_centroid_2d # MLM; double *
circle_pppr2imp_3d

我上面评论的那些可能不会起作用,其他人可能会这样,但是再次 - 多边形&amp;球体距离绝对可以。你可以指定米,公里,英里,海里,它在球形距离上并不重要,输出与输入的单位相同 - 算法与单位无关。


我今天早上将它放在一起所以它目前只提供多边形点,凸多边形点和三种不同类型的球形距离算法,但至少那些你请求的那些现在供你使用。我不知道是否存在与其他任何python库存在名称冲突的问题,这些天我只是在外围参与python,所以如果有一个更好的名称,我愿意接受建议。

在github上:https://github.com/hoonto/pygeometry

它只是这里描述和实现的功能的python桥梁:

http://people.sc.fsu.edu/~jburkardt/cpp_src/geometry/geometry.html

实际上GEOMETRY库非常好,所以我认为为python桥接所有这些函数会很有用,我今晚可能会这样做。

编辑:其他一些事情

  1. 因为数学函数实际上是编译C ++,所以当然需要确保共享库在路径中。您可以修改geometry.py以指向您希望放置该共享库的位置。
  2. 仅针对linux编译,.o和.so是在x86_64 fedora上编译的。
  3. 球面距离算法需要弧度,因此您需要将十进制纬度/经度转换为弧度,如geometry.py所示。
  4. 如果你在Windows上确实需要这个,请告诉我,它应该只需要几分钟就可以在Visual Studio中实现。但除非有人问我现在可能只是不管它。

    希望这有帮助!

    RGDS .... Hoonto /马特

    (新提交:SHA:4fa2dbbe849c09252c7bd931edfe8db478de28e6 - 修复了一些内容,例如radian转换以及py函数的返回类型。还添加了一些基本的性能测试,以确保库正常运行。)

    测试结果 在每次迭代中,一次调用sphere_distance1,一次调用polygon_contains_point_2d,因此调用库总数。

    • ~0.062s:2000次迭代,4000次调用
    • ~0.603s:20000次迭代,40000次调用
    • ~0.905s:30000次迭代,60000次呼叫
    • ~1.198s:40000次迭代,80000次通话

答案 1 :(得分:0)

如果球形计算足够,我只需使用numpy作为距离,使用matplotlib进行多边形检查(当你在stackoverflow中找到类似的建议时)。

from math import asin, cos, radians, sin, sqrt
import numpy as np

def great_circle_distance_py(pnt1, pnt2, radius):
    """ Returns distance on sphere between points given as (latitude, longitude) in degrees. """
    lat1 = radians(pnt1[0])
    lat2 = radians(pnt2[0])
    dLat = lat2 - lat1
    dLon = radians(pnt2[1]) - radians(pnt1[1])
    a = sin(dLat / 2.0) ** 2 + cos(lat1) * cos(lat2) * sin(dLon / 2.0) ** 2
    return 2 * asin(min(1, sqrt(a))) * radius

def great_circle_distance_numpy(pnt1, l_pnt2, radius):
    """ Similar to great_circle_distance_py(), but working on list of pnt2 and returning minimum. """
    dLat = np.radians(l_pnt2[:, 0]) - radians(pnt1[0])   # slice latitude from list of (lat, lon) points
    dLon = np.radians(l_pnt2[:, 1]) - radians(pnt1[1])
    a = np.square(np.sin(dLat / 2.0)) + np.cos(radians(pnt1[0])) * np.cos(np.radians(l_pnt2[:, 0])) * np.square(np.sin(dLon / 2.0))
    return np.min(2 * np.arcsin(np.minimum(np.sqrt(a), len(a)))) * radius

def aux_generateLatLon():
    import random
    while 1:
        yield (90.0 - 180.0 * random.random(), 180.0 - 360.0 * random.random())

if __name__ == "__main__":
    ## 1. Great-circle distance
    earth_radius_m = 6371000.785   # sphere of same volume
    nPoints = 1000
    nRep    = 100   # just to measure time

    # generate a point and a list of to check against
    pnt1 = next(aux_generateLatLon())
    l_pnt2 = np.array([next(aux_generateLatLon()) for i in range(nPoints)])

    dMin1 = min([great_circle_distance_py(pnt1, pnt2, earth_radius_m) for pnt2 in l_pnt2])
    dMin2 = great_circle_distance_numpy(pnt1, l_pnt2, earth_radius_m)

    # check performance
    import timeit
    print "random points: %7i" % nPoints
    print "repetitions  : %7i" % nRep
    print "function 1   : %14.6f s" % (timeit.timeit('min([great_circle_distance_py(pnt1, pnt2, earth_radius_m) for pnt2 in l_pnt2])', 'from __main__ import great_circle_distance_py   , pnt1, l_pnt2, earth_radius_m', number=nRep))
    print "function 2   : %14.6f s" % (timeit.timeit('great_circle_distance_numpy(pnt1, l_pnt2, earth_radius_m)'                     , 'from __main__ import great_circle_distance_numpy, pnt1, l_pnt2, earth_radius_m', number=nRep))

    # tell distance
    assert(abs(dMin1 - dMin2) < 0.0001)
    print
    print "min. distance: %14.6f m" % dMin1

    ## 2. Inside polygon?
    # Note, not handled:
    #   - the "pathological case" mentioned on http://paulbourke.net/geometry/polygonmesh/
    #   - special situations on a sphere: polygons covering "180 degrees longitude edge" or the Poles
    from matplotlib.path import Path
    x = y = 1.0
    l_pnt2 = [(-x, -y), (x, -y), (x, y), (-x, y), (-x, -y)]
    path = Path(l_pnt2)
    print "isInside ?"
    for pnt in [(0.9, -1.9), (0.9, -0.9)]:
        print "   ", pnt, bool(path.contains_point(pnt))

如果您想要做更多,Quantum GIS工具集可能值得一看:PyQGIS Developer Cookbook (docs.qgis.org)