Pythonでポイントがマルチポリゴン内にあるかどうかを確認する


13

シェープファイル、フィオナ、ogrなどのライブラリを使用してコードのいくつかの例を試して、ポイント(x、y)がArcMapで作成された(したがって、シェープファイル形式で)マルチポリゴンの境界内にあるかどうかを確認しました。ただし、通常の単一ポリゴンシェープファイルでは問題なく機能しますが、マルチポリゴンではうまく機能しない例もあります。私が試したスニペットの一部を以下に示します。

# First example using shapefile and shapely:
from shapely.geometry import Polygon, Point, MultiPolygon
import shapefile

polygon = shapefile.Reader('shapefile.shp') 
polygon = polygon.shapes()  
shpfilePoints = []
for shape in polygon:
    shpfilePoints = shape.points 
polygon = shpfilePoints 
poly = Polygon(poly)

point = Point(x, y)
# point in polygon test
if polygon.contains(point):
    print 'inside'
else:
    print 'OUT'


# Second example using ogr and shapely:
from shapely.geometry import Polygon, Point, MultiPolygon
from osgeo import ogr, gdal

driver = ogr.GetDriverByName('ESRI Shapefile')
dataset = driver.Open("shapefile.shp", 0)

layer = dataset.GetLayer()
for index in xrange(layer.GetFeatureCount()):
    feature = layer.GetFeature(index)
    geometry = feature.GetGeometryRef()

polygon = Polygon(geometry)
print 'polygon points =', polygon  # this prints 'multipoint' + all the points fine

point = Point(x, y)
# point in polygon test
if polygon.contains(point):
    print 'inside'
else:
    print 'OUT'

最初の例は一度に1つのポリゴンで正常に動作しますが、マルチポリゴンシェープファイルのいずれかのシェイプ内のポイントを入力すると、多数のパーツの1つに含まれていても「out」を返します。

2番目の例の場合、「タイプ「Geometry」のオブジェクトにはlen()がありません」というエラーが表示されます。これは、geometryフィールドが通常のインデックス付きリスト/配列として読み取れないためと考えられます。

さらに、ここで提案さているように、機能していないコードの一部ではないことを確認するために、ポリゴンコードの実際のポイントを置き換えようとしました。そして、そのリンクの例は単純なポリゴンシェープファイルで正常に動作しますが、複雑なマルチポリゴンを適切にテストすることができません。

だから、ポイントがpythonを介してマルチポリゴンシェープファイル内にあるかどうかをテストする他の方法は考えられません...たぶん他のライブラリがないのでしょうか?


2番目の例は、マルチポリゴンをポリゴンに強制変換しているように見えますか?それはマルチポリゴンの最初の部分に対してポイントをチェックするだけかもしれません。ポイントを別の部分に移動して、チェックが成功するかどうかを確認してください。
obrl_soil

@obrl_soilご提案ありがとうございます。ただし、2番目の例は、上で説明したエラーメッセージ(タイプ 'Geometry'のオブジェクトにはlen()がありません) "のために機能しません。MultiPolygon(geometry)または単にPolygon(geometry)を試しても、最初の例で、メインポリゴン内のものだけが機能します。この説明が役立つことを願っています
。–スパルマー

ええ、私はあなたがそのエラーが発生polygon = Polygon(geometry)したpolygon = MultiPolygon(geometry)場合に切り替わるある種のtryループに置き換える必要があると思います。
obrl_soil

最初の例の問題は最初のループにあります。
xunilk

回答:


24

シェープファイルにはタイプMultiPolygon(type = Polygon)はありませんが、サポートされます(すべてのリングは1つの機能に格納されます=ポリゴンのリスト、巨大なマルチポリゴンのポリゴンへの変換を参照)

問題

ここに画像の説明を入力してください

MultiPolygonシェープファイルを開くと、ジオメトリは「Polygon」になります

multipolys = fiona.open("multipol.shp")
multipolys.schema
{'geometry': 'Polygon', 'properties': OrderedDict([(u'id', 'int:10')])}
len(multipolys)
1

Fionaによるソリューション1

import fiona
from shapely.geometry import shape,mapping, Point, Polygon, MultiPolygon
multipol = fiona.open("multipol.shp")
multi= multipol.next() # only one feature in the shapefile
print multi
{'geometry': {'type': 'MultiPolygon', 'coordinates': [[[(-0.5275288092189501, 0.5569782330345711), (-0.11779769526248396, 0.29065300896286816), (-0.25608194622279135, 0.01920614596670933), (-0.709346991037132, -0.08834827144686286), (-0.8629961587708066, 0.18309859154929575), (-0.734955185659411, 0.39820742637644047), (-0.5275288092189501, 0.5569782330345711)]], [[(0.19974391805377723, 0.060179257362355965), (0.5480153649167734, 0.1293213828425096), (0.729833546734955, 0.03969270166453265), (0.8143405889884763, -0.13956466069142115), (0.701664532650448, -0.38540332906530095), (0.4763124199743918, -0.5006402048655569), (0.26888604353393086, -0.4238156209987196), (0.18950064020486557, -0.2291933418693981), (0.19974391805377723, 0.060179257362355965)]], [[(-0.3764404609475033, -0.295774647887324), (-0.11523687580025621, -0.3597951344430217), (-0.033290653008962945, -0.5800256081946222), (-0.11523687580025621, -0.7413572343149808), (-0.3072983354673495, -0.8591549295774648), (-0.58898847631242, -0.6927016645326505), (-0.6555697823303457, -0.4750320102432779), (-0.3764404609475033, -0.295774647887324)]]]}, 'type': 'Feature', 'id': '0', 'properties': OrderedDict([(u'id', 1)])}

フィオナは機能をマルチポリゴンとして解釈し、QGIS、ArcGIS、PostGISなどを使用せずにPythonより効率的な空間結合に示されたソリューションを適用できます(1)

points= ([pt for pt  in fiona.open("points.shp")])
for i, pt in enumerate(points):
    point = shape(pt['geometry'])
    if point.within(shape(multi['geometry'])):
         print i, shape(points[i]['geometry'])
1 POINT (-0.58898847631242 0.17797695262484)
3 POINT (0.4993597951344431 -0.06017925736235585)
5 POINT (-0.3764404609475033 -0.4750320102432779)
6 POINT (-0.3098591549295775 -0.6312419974391805)

pyshp(シェープファイル)およびgeo_interface(GeoJSONのような)プロトコルを使用したソリューション2

これはxulnikの回答の補足です。

import shapefile
pts = shapefile.Reader("points.shp")
polys = shapefile.Reader("multipol.shp")
points = [pt.shape.__geo_interface__ for pt in pts.shapeRecords()]
multi = shape(polys.shapeRecords()[0].shape.__geo_interface__) # 1 polygon
print multi
MULTIPOLYGON (((-0.5275288092189501 0.5569782330345711, -0.117797695262484 0.2906530089628682, -0.2560819462227913 0.01920614596670933, -0.7093469910371319 -0.08834827144686286, -0.8629961587708066 0.1830985915492958, -0.734955185659411 0.3982074263764405, -0.5275288092189501 0.5569782330345711)), ((0.1997439180537772 0.06017925736235596, 0.5480153649167734 0.1293213828425096, 0.729833546734955 0.03969270166453265, 0.8143405889884763 -0.1395646606914211, 0.701664532650448 -0.3854033290653009, 0.4763124199743918 -0.5006402048655569, 0.2688860435339309 -0.4238156209987196, 0.1895006402048656 -0.2291933418693981, 0.1997439180537772 0.06017925736235596)), ((-0.3764404609475033 -0.295774647887324, -0.1152368758002562 -0.3597951344430217, -0.03329065300896294 -0.5800256081946222, -0.1152368758002562 -0.7413572343149808, -0.3072983354673495 -0.8591549295774648, -0.58898847631242 -0.6927016645326505, -0.6555697823303457 -0.4750320102432779, -0.3764404609475033 -0.295774647887324)))
for i, pt in enumerate(points):
    point = shape(pt)
    if point.within(multi): 
        print i, shape(points[i])
1 POINT (-0.58898847631242 0.17797695262484)
3 POINT (0.4993597951344431 -0.06017925736235585)
5 POINT (-0.3764404609475033 -0.4750320102432779)
6 POINT (-0.3098591549295775 -0.6312419974391805)

ogrとgeo_interfaceプロトコルを使用したソリューション3 (Python Geo_interfaceアプリケーション

from osgeo import ogr
import json
def records(file):  
    # generator 
    reader = ogr.Open(file)
    layer = reader.GetLayer(0)
    for i in range(layer.GetFeatureCount()):
        feature = layer.GetFeature(i)
        yield json.loads(feature.ExportToJson())

points  = [pt for pt in records("point_multi_contains.shp")]
multipol = records("multipol.shp")
multi = multipol.next() # 1 feature
for i, pt in enumerate(points):
     point = shape(pt['geometry'])
     if point.within(shape(multi['geometry'])):
          print i, shape(points[i]['geometry'])

1 POINT (-0.58898847631242 0.17797695262484)
3 POINT (0.499359795134443 -0.060179257362356)
5 POINT (-0.376440460947503 -0.475032010243278)
6 POINT (-0.309859154929577 -0.631241997439181)

QGIS、ArcGIS、PostGISなどを使用せずにPythonより効率的な空間結合行うようなGeoPandasを使用したソリューション4 (2)

import geopandas
point = geopandas.GeoDataFrame.from_file('points.shp') 
poly  = geopandas.GeoDataFrame.from_file('multipol.shp')
from geopandas.tools import sjoin
pointInPolys = sjoin(point, poly, how='left')
grouped = pointInPolys.groupby('index_right')
list(grouped)
[(0.0,      geometry                               id_left  index_right id_right  

1  POINT (-0.58898847631242 0.17797695262484)       None      0.0        1.0 
3  POINT (0.4993597951344431 -0.06017925736235585)  None      0.0        1.0
5  POINT (-0.3764404609475033 -0.4750320102432779)  None      0.0        1.0 
6  POINT (-0.3098591549295775 -0.6312419974391805)  None      0.0        1.0 ]
print grouped.groups
{0.0: [1, 3, 5, 6]} 

ポイント1、3、5、6はマルチポリゴンの境界内にあります


ここでは少し古いスレッドですがmulti = shape(polys.shapeRecords()[0].shape.__geo_interface__)、ソリューション2 ではどのように呼び出しますか?からshape()メソッドの呼び出しを取得できませんshapefile.py。私も試しましたshapefile.Shape()。そのためのクラスがありますが、機能しません。
pstatix 2017

さらに、within()メソッドはどこから入手しますか?
pstatix 2017

1
シェイプリー(from shapely.geometry import shape,mapping, Point, Polygon, MultiPolygon)から
遺伝子

私は解決策4を使用して、このエラーを取得:File "C:\WinPython\python-3.6.5.amd64\lib\site-packages\geopandas\tools\sjoin.py", line 43, in sjoin if left_df.crs != right_df.crs: AttributeError: 'MultiPolygon' object has no attribute 'crs'
アーロンBramson

6

最初の例の問題は次のループにあります。

...
shpfilePoints = []
for shape in polygon:
    shpfilePoints = shape.points
...

最後の特徴点を追加するだけです。私はこのシェープファイルで自分のアプローチを試しました:

ここに画像の説明を入力してください

私はあなたのコードを次のように変更しました:

from shapely.geometry import Polygon, Point, MultiPolygon
import shapefile 

path = '/home/zeito/pyqgis_data/polygon8.shp'

polygon = shapefile.Reader(path) 

polygon = polygon.shapes() 

shpfilePoints = [ shape.points for shape in polygon ]

print shpfilePoints

polygons = shpfilePoints

for polygon in polygons:
    poly = Polygon(polygon)
    print poly

上記のコードはQGISのPythonコンソールで実行され、結果は次のとおりです。

ここに画像の説明を入力してください

これは完全に機能し、今、ポイント(x、y)が各フィーチャの境界内にあるかどうかを確認できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.