Không gian hiệu quả hơn tham gia vào Python mà không cần QGIS, ArcGIS, PostGIS, v.v.


31

Tôi đang cố gắng thực hiện một phép nối không gian giống như ví dụ ở đây: Có tùy chọn python để "nối các thuộc tính theo vị trí" không? . Tuy nhiên, cách tiếp cận đó có vẻ thực sự không hiệu quả / chậm. Ngay cả việc chạy này với 250 điểm khiêm tốn cũng mất gần 2 phút và nó hoàn toàn thất bại trên các shapefile với> 1.000 điểm. Có một cách tiếp cận tốt hơn? Tôi muốn làm điều này hoàn toàn bằng Python mà không cần sử dụng ArcGIS, QGIS, v.v.

Tôi cũng muốn biết liệu có thể thuộc tính SUM (tức là dân số) của tất cả các điểm nằm trong một đa giác và nối số lượng đó với shapefile đa giác không.

Đây là mã tôi đang cố gắng chuyển đổi. Tôi gặp lỗi trên dòng 9:

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

mà nói:

TypeError: loại toán hạng không được hỗ trợ cho + =: 'noneType' và 'float'.

Nếu tôi thay thế "+ =" bằng "=" thì nó sẽ chạy tốt nhưng điều đó không tổng hợp các trường. Tôi cũng đã thử biến chúng thành số nguyên nhưng điều đó cũng thất bại.

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']})

Tôi nghĩ bạn nên chỉnh sửa câu hỏi thứ hai của mình ra khỏi đây để câu hỏi này vẫn tập trung vào những gì tôi cho là câu hỏi quan trọng hơn với bạn. Cái khác có thể được nghiên cứu / yêu cầu riêng.
PolyGeo

Câu trả lời:


37

Fiona trả về từ điển Python và bạn không thể sử dụng poly['properties']['score']) += point['properties']['score'])với từ điển.

Ví dụ về tính tổng của các thuộc tính bằng cách sử dụng các tham chiếu được đưa ra bởi Mike T:

nhập mô tả hình ảnh ở đây

# 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)

Bây giờ, chúng ta có thể sử dụng hai phương thức, có hoặc không có chỉ mục không gian:

1) không có

# 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) với chỉ mục cây R (bạn có thể sử dụng pyrtree hoặc 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']

Kết quả với hai giải pháp:

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

Sự khác biệt là gì ?

  • Không có chỉ mục, bạn phải lặp qua tất cả các hình học (đa giác và điểm).
  • Với chỉ số không gian giới hạn ( Chỉ số không gian RTree ), bạn chỉ lặp qua các hình học có cơ hội giao với hình học hiện tại của bạn ('bộ lọc' có thể tiết kiệm một lượng tính toán và thời gian đáng kể ...)
  • nhưng Chỉ số không gian không phải là cây đũa thần. Khi phải lấy một phần rất lớn của bộ dữ liệu, Chỉ số không gian không thể mang lại bất kỳ lợi ích tốc độ nào.

Sau:

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

Để đi xa hơn, hãy xem Sử dụng lập chỉ mục không gian Rtree với OGR, Shapely, Fiona


15

Ngoài ra - giờ đây, geopandas tùy chọn bao gồm rtreenhư một phụ thuộc, xem repo github

Vì vậy, thay vì làm theo tất cả các mã (rất đẹp) ở trên, bạn có thể chỉ cần làm một cái gì đó như:

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ó được chức năng hấp dẫn này, trước tiên hãy chắc chắn cài đặt thư viện C- libspatialindex

EDIT: nhập gói chính xác


Tôi đã có ấn tượng rtreelà tùy chọn. Điều đó không có nghĩa là bạn cần cài đặt rtreecũng như libspatialindexthư viện C?
kuanb

đã được một lúc nhưng tôi nghĩ khi tôi thực hiện cài đặt geopandas này từ github sẽ tự động thêm vào rtreekhi tôi cài đặt lần đầu tiên libspatialindex... họ đã thực hiện một bản phát hành khá lớn kể từ đó vì vậy tôi chắc chắn mọi thứ đã thay đổi một chút
claytonrsh

9

Sử dụng Rtree làm chỉ mục để thực hiện các phép nối nhanh hơn nhiều, sau đó Shapely thực hiện các vị từ không gian để xác định xem một điểm có thực sự nằm trong đa giác hay không. Nếu được thực hiện đúng cách, điều này có thể nhanh hơn hầu hết các GIS khác.

Xem ví dụ ở đây hoặc ở đây .

Phần thứ hai của câu hỏi của bạn liên quan đến 'SUM', sử dụng một dictđối tượng để tích lũy dân số bằng cách sử dụng id đa giác làm khóa. Mặc dù, loại điều này được thực hiện độc đáo hơn nhiều với PostGIS.


Cảm ơn bạn @Mike T ... sử dụng đối tượng dict hoặc PostGIS là những gợi ý tuyệt vời. Tuy nhiên, tôi vẫn hơi bối rối khi có thể kết hợp Rtree trong mã của mình (bao gồm cả mã ở trên).
jburrfischer

1

Trang web này cho biết cách sử dụng tìm kiếm điểm đa giác Bounding Box trước khi truy vấn không gian đắt hơn trong Shapely.

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


Cảm ơn @klewis ... bất kỳ cơ hội nào bạn có thể giúp với phần thứ hai? Để tính tổng các thuộc tính điểm (ví dụ: dân số) nằm trong đa giác tôi đã thử một cái gì đó tương tự như mã bên dưới nhưng nó đã gây ra lỗi. nếu hình dạng (trường [ 'hình học']) trong vòng (hình dạng (khu phố [ 'hình học'])):. xóm [ 'properties'] [ 'dân số'] + = trường [ 'properties'] [ 'dân số']
jburrfischer

Nếu bạn mở vùng lân cận ở chế độ 'r', nó có thể chỉ đọc. Cả hai shapefile có dân số lĩnh vực? Dòng # nào đang ném lỗi? Chúc may mắn.
klewis

Cảm ơn bạn một lần nữa @klewis ... Tôi đã thêm mã của mình ở trên và giải thích lỗi. Ngoài ra, tôi đã chơi xung quanh với rtree và tôi vẫn hơi bối rối khi tôi sẽ thêm mã này vào mã ở trên. Xin lỗi để làm phiền như vậy.
jburrfischer

Hãy thử điều này, dường như việc thêm Không vào một int đang gây ra lỗi. poly_score = poly ['property'] ['points']) point_score = point ['property'] ['points']) if point_score: if poly_score poly ['property'] ['points']) + = point_score other: poly ['property'] ['points']) = point_score
klewis
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.