Быстрая ГИС-библиотека python, поддерживающая большое расстояние круга и полигон

Я искал географическую библиотеку для python. Мне нужно уметь делать следующее:

  1. получить расстояние между 2 точками (в метрах) с помощью Great-circle расстояние (не расчет расстояния лайнера)
  2. проверьте, находится ли точка внутри многоугольника
  3. Выполните 1 и 2 пару тысяч раз в секунду

в начале я посмотрел на этот пост: модуль Python для хранения и запросов географические координаты и geopy. Я столкнулся с 2 проблемами:

  1. Geopy не поддерживает полигоны
  2. высокая загрузка процессора geoPy (для расчета расстояния между точкой и относительными 5000 точками требуется около 140ms процессора)

Я продолжал искать и нашел Лучшая ГИС-библиотека Python? и https://gis.stackexchange.com/ . Это выглядело многообещающе, поскольку geos использует C код, который должен быть быстрее и shapely поддерживает полигоны. Проблема в том, что geos/OGR выполняет вычисления линейных расстояний вместо сферы. Это исключает все другие основанные geos модули (как GEODjango и shapely). Я что-то упускаю? Я не думаю, что я первый человек, который использует python для выполнения вычислений ГИС и хочет получить точные результаты.

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

те, которые я прокомментировал выше, вероятно, не будут работать, другие могут, но снова-polygon & расстояния сферы определенно делают. И вы можете указать метры, километры, мили, морские мили, это действительно не имеет значения на сферических расстояниях, выход находится в тех же единицах, что и вход - алгоритмы агностичны для единиц.


Я собрал это сегодня утром, поэтому в настоящее время он предоставляет только точку в многоугольнике, точку в выпуклом многоугольнике и три разных типа алгоритмов сферического расстояния, но, по крайней мере, те, которые вы запросили тебя использовать сейчас. Я не знаю, есть ли конфликт имен с любой другой библиотекой python, я только периферийно связан с python в эти дни, поэтому, если есть лучшее имя для него, я открыт для предложений.

на github:https://github.com/hoonto/pygeometry

это просто мост python для функций, описанных и реализованных здесь:

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

библиотека геометрии на самом деле довольно хороша, поэтому я думаю, что будет полезно объединить все эти функции для python, что я сделаю, вероятно, сегодня вечером.

Edit: пара других вещей

  1. поскольку математические функции фактически скомпилированы на C++, вам, конечно, нужно убедиться, что общая библиотека находится в пути. Вы смогите доработать geometry.py чтобы указать, куда вы хотите поместить эту общую библиотеку.
  2. компилируется только для linux, the .o и .так были скомпилированы на x86_64 fedora.
  3. алгоритмы сферического расстояния ожидают радианы, поэтому вам нужно преобразовать десятичные лат / Лон градусов, например, в радианы, как показано в geometry.py.

Если вам это нужно в Windows, дайте мне знать, это займет всего пару минут, чтобы получить его в Visual Studio. Но если никто не спросит, я, наверное, пока оставлю его в покое.

надеюсь, что это помогает!

Rgds....Hoonto / Matt

(new commit: SHA: 4fa2dbbe849c09252c7bd931edfe8db478de28e6 - исправлены некоторые вещи, такие как преобразования Радиана, а также возвращаемые типы для функций py. Также добавлены некоторые базовые тесты производительности, чтобы убедиться, что библиотека работает должным образом.)

Результаты Теста В каждой итерации один вызов sphere_distance1 и один вызовите polygon_contains_point_2d, чтобы 2 вызова общей библиотеки.

  • ~0.062 s: 2000 итераций, 4000 вызовов
  • ~0.603 s: 20000 итераций, 40000 вызовов
  • ~0.905 s : 30000 итераций, 60000 вызовов
  • ~1.198 s : 40000 итераций, 80000 вызовов

Если сферического вычисления достаточно, я бы просто использовал 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 (docs.qgis.org).