QGIS, ArcGIS, PostGIS 등을 사용하지 않고도 Python에서보다 효율적인 공간 조인


31

여기 예제와 같이 공간 조인을 시도하고 있습니다. "위치별로 속성에 가입"하는 파이썬 옵션이 있습니까? . 그러나 그 접근 방식은 실제로 비효율적이며 느립니다. 적당한 250 포인트로 이것을 실행하더라도 거의 2 분이 걸리고 1,000 포인트가 넘는 쉐이프 파일에서는 완전히 실패합니다. 더 나은 접근 방법이 있습니까? ArcGIS, QGIS 등을 사용하지 않고 Python에서 완전히 수행하고 싶습니다.

또한 다각형 내에있는 모든 점의 속성 (예 : 모집단)을 합산하고 해당 수량을 다각형 모양 파일에 결합 할 수 있는지 알고 싶습니다.

변환하려는 코드는 다음과 같습니다. 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'])함께 사용할 수 없습니다 .

Mike T가 제공 한 참조를 사용하여 속성을 합하는 예 :

여기에 이미지 설명을 입력하십시오

# 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-tree 인덱스 사용 ( 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

차이점은 무엇입니까?

  • 인덱스가 없으면 모든 형상 (다각형 및 점)을 반복해야합니다.
  • 경계 공간 인덱스 ( Spatial Index 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)

더 나아가려면 OGR사용한 Rtree 공간 인덱스 사용, Shapely, Fiona를 참조하십시오.


15

또한-geopandas는 이제 선택적 rtree으로 종속성 으로 포함 됩니다 .github 저장소를 참조하십시오.

따라서 위의 모든 (매우 훌륭한) 코드를 따르는 대신 간단히 다음과 같은 작업을 수행 할 수 있습니다.

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 를 설치하십시오

편집 : 수정 된 패키지 가져 오기


나는 rtree선택 의 여지 가 있었다는 인상을 받았다. 그것은 C 라이브러리 rtree뿐만 아니라 설치해야한다는 것을 의미하지 libspatialindex않습니까?
kuanb

내가 자동으로 추가 GitHub의에서이 설치 geopandas했을 때하지만 내가 생각하는 동안이 지났지 rtreeI 처음 설치했을 때 libspatialindex나는 확실히 상황이 조금 변경되었습니다있어 ... 그들은 상당히 주요 릴리스 한 적이 가입일 있도록
claytonrsh

9

사용 RTREE 인덱스로는 포인트가 다각형 내에서 실제로 있는지 확인하기 위해 공간 관련 술어를 할 매끈한 다음, 조인 빨리 많이 수행 할 수 있습니다. 올바르게 수행하면 대부분의 다른 GIS보다 빠를 수 있습니다.

여기 또는 여기에 예를 참조 하십시오 .

'SUM'에 관한 질문의 두 번째 부분은 dict객체를 사용하여 다각형 ID를 키로 사용하여 모집단을 축적합니다. 이 유형의 작업은 PostGIS에서 훨씬 더 잘 수행됩니다.


@Mike T에게 감사합니다 ... dict 객체 또는 PostGIS를 사용하는 것이 좋습니다. 그러나 여전히 Rtree를 내 코드에 통합 할 수있는 곳에서 약간 혼란 스럽습니다 (위의 코드 포함).
jburrfischer 2016 년

1

이 웹 페이지는 Shapely의 공간 내 쿼리가 더 비싸기 전에 Bounding Box Point-in-polygon 검색을 사용하는 방법을 보여줍니다.

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


감사합니다 @klewis ... 두 번째 부분을 도울 수있는 기회가 있습니까? 다각형 내에 포함되는 점 속성 (예 : 모집단)을 합산하기 위해 아래 코드와 비슷한 것을 시도했지만 오류가 발생했습니다. if shape (school [ 'geometry']). within (shape (neighborhood [ 'geometry'])) : neighbourhood [ 'properties'] [ 'population'] + = schools [ 'properties'] [ 'population']
jburrfischer

'r'모드에서 이웃을 열면 읽기 전용 일 수 있습니다. 두 shapefile 모두 필드 모집단이 있습니까? 어떤 줄 번호에서 오류가 발생합니까? 행운을 빕니다.
klewis

다시 @klewis 감사합니다 ... 위의 코드를 추가하고 오류를 설명했습니다. 또한 rtree로 놀고 있었고 위의 코드에 이것을 추가 할 위치가 여전히 혼란 스럽습니다. 귀찮게해서 죄송합니다.
jburrfischer 2016 년

이것을 시도하십시오, int에 None을 추가하면 오류가 발생합니다. poly_score = poly [ 'properties'] [ 'score']) point_score = point [ 'properties'] [ 'score']) point_score 인 경우 : poly_score poly [ 'properties'] [ 'score']) + = point_score 기타 : poly [ 'properties'] [ 'score']) = point_score
klewis
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.