Більш ефективне просторове приєднання до Python без QGIS, ArcGIS, PostGIS тощо


31

Я намагаюся зробити просторове з'єднання подібно до прикладу тут: Чи є варіант python "приєднати атрибути за місцезнаходженням"? . Однак такий підхід здається дійсно неефективним / повільним. Навіть запуск цього моменту зі скромними 250 балами займає майже 2 хвилини, і він повністю провалюється на форматі з> 1000 очок. Чи є кращий підхід? Я б хотів це зробити повністю на Python, не використовуючи ArcGIS, QGIS тощо.

Мені б також цікаво дізнатись, чи можливо атрибути SUM (тобто сукупність) усіх точок, які потрапляють у полігон, і приєднати цю кількість до формі профілю багатокутника.

Ось код, який я намагаюся перетворити. Я отримую помилку в рядку 9:

poly['properties']['score'] += point['properties']['score']

що говорить:

TypeError: непідтримувані типи операндів для + =: 'NoneType' і 'float'.

Якщо я заміню "+ =" на "=", він працює нормально, але це не підсумовує поля. Я також намагався зробити їх як цілі числа, але це також не вдається.

with fiona.open(poly_shp, 'r') as n: 
  with fiona.open(point_shp,'r') as s:
    outSchema = {'geometry': 'Polygon','properties':{'region':'str','score':'float'}}
    with fiona.open (out_shp, 'w', 'ESRI Shapefile', outSchema, crs) as output:
        for point in s:
            for poly in n:
                if shape(point['geometry']).within(shape(poly['geometry'])):  
                    poly['properties']['score']) += point['properties']['score'])
                    output.write({
                        'properties':{
                            'region':poly['properties']['NAME'],
                            'score':poly['properties']['score']},
                        'geometry':poly['geometry']})

Я думаю, ви повинні відредагувати своє друге питання звідси, щоб це залишалося зосередженим на тому, що, на мою думку, є для вас більш важливим питанням. Інше можна дослідити / запитати окремо.
PolyGeo

Відповіді:


37

Fiona повертає словники Python, і ви не можете користуватися poly['properties']['score']) += point['properties']['score'])зі словником.

Приклад підсумовування атрибутів за допомогою посилань, наведених Майком Т:

введіть тут опис зображення

# read the shapefiles 
import fiona
from shapely.geometry import shape
polygons = [pol for pol in fiona.open('poly.shp')]
points = [pt for pt in fiona.open('point.shp')]
# attributes of the polygons
for poly in polygons:
   print poly['properties'] 
OrderedDict([(u'score', 0)])
OrderedDict([(u'score', 0)])
OrderedDict([(u'score', 0)])

# attributes of the points
for pt in points:
    print i['properties']
 OrderedDict([(u'score', 1)]) 
 .... # (same for the 8 points)

Тепер ми можемо використовувати два методи з просторовим індексом або без нього:

1) без

# iterate through points 
for i, pt in enumerate(points):
     point = shape(pt['geometry'])
     #iterate through polygons
     for j, poly in enumerate(polygons):
        if point.within(shape(poly['geometry'])):
             # sum of attributes values
             polygons[j]['properties']['score'] = polygons[j]['properties']['score'] + points[i]['properties']['score']

2) з індексом R-дерева (можна використовувати pyrtree або rtree )

# Create the R-tree index and store the features in it (bounding box)
 from rtree import index
 idx = index.Index()
 for pos, poly in enumerate(polygons):
       idx.insert(pos, shape(poly['geometry']).bounds)

#iterate through points
for i,pt in enumerate(points):
  point = shape(pt['geometry'])
  # iterate through spatial index
  for j in idx.intersection(point.coords[0]):
      if point.within(shape(multi[j]['geometry'])):
            polygons[j]['properties']['score'] = polygons[j]['properties']['score'] + points[i]['properties']['score']

Результат з двома рішеннями:

for poly in polygons:
   print poly['properties']    
 OrderedDict([(u'score', 2)]) # 2 points in the polygon
 OrderedDict([(u'score', 1)]) # 1 point in the polygon
 OrderedDict([(u'score', 1)]) # 1 point in the polygon

Яка різниця ?

  • Без індексу потрібно повторити всі геометрії (багатокутники та точки).
  • За допомогою обмежувального просторового індексу ( просторовий індекс RTree ) ви повторюєте лише геометрії, які мають шанс перетинатися з вашою поточною геометрією ("фільтр", що може заощадити значну кількість обчислень та часу ...)
  • але просторовий індекс - це не чарівна паличка. Коли потрібно отримати дуже велику частину набору даних, просторовий індекс не може дати переваги швидкості.

Після:

schema = fiona.open('poly.shp').schema
with fiona.open ('output.shp', 'w', 'ESRI Shapefile', schema) as output:
    for poly in polygons:
        output.write(poly)

Щоб піти далі, подивіться на використання просторової індексації Rtree за допомогою OGR, Shapely, Fiona


15

Додатково - геопанда тепер необов'язково включає rtreeзалежність, див. Github repo

Отже, замість того, щоб дотримуватися всіх (дуже приємних) код вище, ви можете просто зробити щось на кшталт:

import geopandas
from geopandas.tools import sjoin
point = geopandas.GeoDataFrame.from_file('point.shp') # or geojson etc
poly = geopandas.GeoDataFrame.from_file('poly.shp')
pointInPolys = sjoin(point, poly, how='left')
pointSumByPoly = pointInPolys.groupby('PolyGroupByField')['fields', 'in', 'grouped', 'output'].agg(['sum'])

Для того, щоб отримати цю функцію привабливою обов'язково встановити C-бібліотеку libspatialindex першим

EDIT: виправлений пакет імпорту


У мене під враженням rtreeбуло необов’язково. Чи це не означає, що вам потрібно встановити rtreeтакож libspatialindexC-бібліотеку?
kuanb

минуло певний час, але я думаю, що коли я робив це, встановивши геопанди від github, автоматично додав, rtreeколи я вперше встановив libspatialindex... вони зробили досить великий реліз, тому я впевнений, що справи трохи змінилися
claytonrsh

9

Використовуйте Rtree як індекс для виконання набагато швидших приєднань, тоді Shapely робити просторові предикати, щоб визначити, чи точка є насправді в полігоні. Якщо це зробити правильно, це може бути швидше, ніж більшість інших ГІС.

Дивіться приклади тут або тут .

У другій частині вашого питання, що стосується "SUM", використовуйте dictоб'єкт для накопичення популяцій, використовуючи ідентифікатор полігона як ключ. Хоча, цей спосіб робиться набагато приємніше з PostGIS.


Дякую @Mike T ... використання об'єкта dict або PostGIS - чудові пропозиції. Я все ще трохи розгублений, де я можу включити Rtree у свій код, однак (включений код вище).
jburrfischer

1

На цій веб-сторінці показано, як використовувати пошук Bounding Box «в полігоні» перед дорожчим в просторовому запиті Shapely.

http://rexdouglass.com/fast-spatial-joins-in-python-with-a-spatial-index/


Дякую @klewis ... будь-який шанс допомогти вам у другій частині? Підсумовуючи точкові атрибути (наприклад, сукупність), які потрапляють у полігони, я спробував щось подібне до коду нижче, але це призвело до помилки. якщо форма (школа [ 'геометрія']) в межах (форми (околиць [ 'геометрія'])) :. околиці [ 'властивість'] [ 'населення'] + = школа [ 'властивість'] [ 'населення']
jburrfischer

Якщо ви відкриєте сусідство в режимі 'r', воно може бути лише для читання. Чи мають обидва типи файлів чисельність польових? У якому рядку № викидається помилка? Удачі.
klewis

Дякую ще раз @klewis ... Я додав свій код вище та пояснив помилку. Крім того, я бавився з rtree, і я все ще мало розгублений, куди б додати це до коду вище. Вибачте, що такий набрид.
jburrfischer

Спробуйте це, схоже, додавання None до int викликає помилку. poly_score = poly ['властивості'] ['оцінка']) point_score = точка ['властивості'] ['оцінка']), якщо point_score: якщо poly_score poly ['властивості'] ['оцінка']) + = point_score інше: poly ['властивості'] ['оцінка']) = point_score
klewis
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.