QGIS、ArcGIS、PostGISなどを使用しないPythonでのより効率的な空間結合


31

私はここの例のように空間結合をしようとしています:「場所によって属性を結合する」Pythonオプションはありますか?。しかし、そのアプローチは本当に非効率的/遅いようです。わずかな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']})

ここで2番目の質問を編集して、この質問が私にとってあなたにとってより重要な質問であると思われるものに焦点を合わせ続けるようにする必要があると思います。もう一方は、個別に調査/依頼することができます。
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)

これで、空間インデックスの有無にかかわらず、2つのメソッドを使用できます。

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

2つのソリューションの結果:

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)を使用すると、現在のジオメトリと交差する可能性があるジオメトリ(かなりの量の計算と時間を節約できる「フィルタ」...)のみを反復処理します
  • しかし、空間インデックスは魔法の杖ではありません。データセットの非常に大きな部分を取得する必要がある場合、Spatial Indexを使用しても速度を向上できません。

後:

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、Shapely、FionaでのRtree空間インデックスの使用をご覧ください。


15

さらに-オプションでrtree依存関係としてgeopandasが含まれるようになりました。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オプションであった印象を受けていました。これはrtreelibspatialindexCライブラリと同様にインストールする必要があるという意味ではありませんか?
クアンブ

それはしばらくして私は、私は自動的に追加githubのから、このインストールgeopandasがなかったときだと思いますrtree私が最初にインストールしていたときlibspatialindex、私は物事が少し変わってきたと確信しているので、以来...彼らはかなりメジャーリリースを行ってきた
claytonrsh

9

Rtreeをインデックスとして使用してより高速な結合を実行し、次にShapelyを使用して空間述語を実行し、ポイントが実際にポリゴン内にあるかどうかを判断します。適切に行われれば、これは他のほとんどのGISよりも高速になります。

こちらまたはこちらの例をご覧ください

「SUM」に関する質問の2番目の部分では、dictオブジェクトを使用して、ポリゴンIDをキーとして人口を累積します。ただし、このタイプのことはPostGISを使用してより適切に行われます。


@Mike Tに感謝します... dictオブジェクトを使用するか、PostGISは素晴らしい提案です。ただし、Rtreeをコードのどこに組み込むことができるのか、まだ少し混乱しています(上記のコードを含む)。
jburrfischer 14年

1

このWebページでは、Shapelyの空間クエリ内でより高価な検索を行う前に、境界ボックスポイントインポリゴン検索を使用する方法を示しています。

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


@klewisに感謝します...第二の部分を手伝うことができますか?ポリゴン内にあるポイント属性(人口など)を合計するために、以下のコードに似たものを試しましたが、エラーがスローされました。if shape(school ['geometry'])。within(shape(neighborhood ['geometry'])):
Neighborhood

近所を「r」モードで開くと、読み取り専用になる可能性があります。両方のシェープファイルにフィールドがありますか?エラーをスローしている行番号は何ですか?がんばろう。
klewis

再びありがとう@klewis ...上記のコードを追加し、エラーを説明しました。また、私はrtreeをいじくり回してきましたが、上記のコードのどこにこれを追加するのか、まだ少し混乱しています。そんな気になってすみません。
jburrfischer 14年

これを試してください。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.