【问题标题】:Fast python GIS library that supports Great Circle Distance and polygon支持大圆距离和多边形的快速 python GIS 库
【发布时间】:2013-06-05 23:57:18
【问题描述】:

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

  1. 使用Great-circle distance 获取两点之间的距离(以米为单位)(不是线性距离计算)
  2. 检查点是否在多边形内
  3. 每秒执行 1 和 2 几千次

一开始我看过这篇文章:Python module for storing and querying geographical coordinates 并开始使用geopy。 我遇到了两个问题:

  1. Geopy 不支持多边形
  2. geoPy 的 CPU 使用率高(计算一个点与相对 5000 个点之间的距离大约需要 140 毫秒的 CPU)

我继续寻找并找到了 Best Python GIS library? 和 https://gis.stackexchange.com/ 。它看起来很有希望,因为 geos 正在使用经过编译的 C 代码,它应该更快并且支持多边形。 问题是 geos/OGR 执行线性距离计算而不是球体。这消除了所有其他基于地理的模块(如 GEODjango 和 shapely)。 我在这里错过了什么吗?我不认为我是第一个使用 python 进行 GIS 计算并希望得到准确结果的人。

【问题讨论】:

  • 你看过Shapely吗?它可以检查一个点是否在多边形内。使用Haversine 公式计算大圆距离很容易,请使用本指南here。不过我不确定性能。
  • Shapely 基于地理,因此只执行线性距离计算。我可以将 Shapely 用于多边形并编写 Haversine 公式的 C 植入(以替换 geopy),但这正是我想要避免的。
  • 你有没有看过 this question 的想法?
  • 是的,我在帖子里写的。
  • 您只想在球体上计算或考虑 DEM:en.wikipedia.org/wiki/Digital_elevation_model?

标签: python performance gis polygon great-circle


【解决方案1】:

如果球面计算足够了,我会使用 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)。

【讨论】:

  • 我们计算great_circle_distance_py和great_circle_distance_numpy的方式可能会出现近距离/远距离浮点误差的问题。
【解决方案2】:

更新

现在继续完成该库中的其他 576 个函数,不包括已完成的两个多边形函数、已完成的三个球体距离算法,以及两个新的,一个 angle_box_2d 和 angle_contains_ray_2d。另外,我切换到 C 版本,这样就不需要 externs,简化了工作。将旧的 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

我在上面评论过的那些可能不起作用,其他可能,但同样 - 多边形和球体距离肯定会起作用。您可以指定米、公里、英里、海里,球面距离并不重要,输出的单位与输入的单位相同——算法与单位无关。


我今天早上把它放在一起,所以它目前只提供多边形中的点、凸多边形中的点和三种不同类型的球面距离算法,但至少你要求的那些现在在那里供你使用。我不知道是否与那里的任何其他 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 中所示。

如果您在 Windows 上确实需要此功能,请告诉我,只需几分钟即可在 Visual Studio 中完成。但除非有人问,否则我可能会暂时搁置它。

希望这会有所帮助!

Rgds....Hoonto/马特

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

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

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

【讨论】:

  • 谢谢!我已经用 C 而不是 python 实现了这两个函数。顺便说一句,在这里使用几何的 C 是一个巨大的缺点,因为您需要执行对象实例化,而不仅仅是调用带有浮点数组的函数。但这实际上是我所做的。谢谢!
  • 很高兴听到,是的,我实际上想知道将整个库从 C 转到 LLVM 到 python 是否是一种更好的方法,并以这种方式引入它,但我没有使用 LLVM使用 python,只有从 C 转到 Javascript,我知道有一些缺点。不管怎样,很高兴听到你解决了:)
  • 很抱歉这个新问题,但是,我如何将它安装在 /usr/local/lib/python2.7/dist-packages 以在我的项目中使用它?没有 install.py。
猜你喜欢
  • 2017-02-21
  • 2016-05-19
  • 2017-09-12
  • 2016-01-23
  • 1970-01-01
  • 2017-01-08
  • 1970-01-01
  • 2021-11-17
  • 2010-10-28
相关资源
最近更新 更多