Python ile bir noktanın çokgen içine girip girmediğini kontrol edin


13

Bir nokta (x, y) ArcMap (ve böylece şekil dosyası biçiminde) oluşturulan bir çokgen sınırları içinde olup olmadığını kontrol etmek için şekil dosyası, fiona ve ogr gibi kitaplıkları kullanarak kod birkaç örnek denedim. Bununla birlikte, örneklerin hiçbiri düzenli, tek poligon şekil dosyalarıyla iyi sonuç vermesine rağmen, çokgenlerle iyi çalışmaz. Denediğim bazı snippet'ler aşağıdadır:

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

İlk örnek bir seferde tek bir çokgenle iyi çalışır, ancak çokgenli şekil dosyamdaki şekillerden birine bir nokta girdiğimde, birçok parçadan birine düşse bile "dışarı" döner.

İkinci örnek için bir hata alıyorum "Geometri türü nesnenin len () yok" varsayalım çünkü geometri alanı normal, dizinlenmiş liste / dizi olarak okunamıyor.

Ayrıca burada işe yaramadı kodun bir parçası değildi emin olmak için burada önerildiği gibi çokgen kodu gerçek noktasını değiştirmek için çalıştı . Ve bu bağlantının örnekleri basit çokgen şekil dosyaları ile iyi çalışsa da, karmaşık çokgenimi düzgün bir şekilde test edemiyorum.

Bu yüzden, bir noktanın python aracılığıyla çokgenli bir şekil dosyasına girip girmediğini test etmenin başka bir yolunu düşünemiyorum ... Belki de orada eksik olduğum başka kütüphaneler var mı?


ikinci örneğiniz, çokgeni çokgene zorlamak gibi görünüyor mu? Sadece çokgenigonun ilk kısmına karşı noktayı kontrol ediyor olabilir. Noktayı farklı parçalara taşımayı deneyin ve kontrolün başarılı olup olmadığını görün.
obrl_soil

@obrl_soil Öneriniz için teşekkür ederiz. Ancak, ikinci örnek asla yukarıda açıklanan hata iletisi ('Geometri' türünün nesnesi len ()) yok "nedeniyle çalışmıyor" MultiPolygon (geometri) veya basitçe Polygon (geometri) deneyin. ilk örnek ve sadece ana çokgen çalışması olanlar .. Umarım bu açıklama yardımcı olur
spartmar

Evet, sanırım bu hata oluşursa polygon = Polygon(geometry)geçtiği bir çeşit deneme döngüsüyle değiştirmeniz gerekiyor polygon = MultiPolygon(geometry).
obrl_soil

İlk örneğinizdeki sorun ilk döngüdedir.
xunilk

Yanıtlar:


24

Şekil dosyalarının türü MultiPolygon yoktur (type = Polygon), ancak yine de onları desteklerler (tüm halkalar bir özellikte depolanır = çokgenler listesi, Büyük çokgeni çokgenlere dönüştürme bölümüne bakın )

Sorun

resim açıklamasını buraya girin

Bir MultiPolygon şekil dosyasını açarsam, geometri 'Çokgen' olur

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

Fiona ile Çözüm 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)])}

Fiona bu özelliği bir MultiPolygon olarak yorumlar ve QGIS, ArcGIS, PostGIS, vb olmadan Python'da Daha Verimli Mekansal birleşimde sunulan çözümü uygulayabilirsiniz (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 (şekil dosyası) ve geo_interface (GeoJSON benzeri) protokolü ile çözüm 2

Bu, xulnik'in cevabına bir ektir.

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 ve geo_interface protokolü ile çözüm 3 ( Python Geo_interface uygulamaları )

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)

İle çözüm 4 GeoPandas olarak daha etkili olarak Yer vb QGIS, ArcGIS, PostGIS olmadan Python birleştirme (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 puanları MultiPolygon sınırları içine girer


Biraz eski iş parçacığı burada, ama multi = shape(polys.shapeRecords()[0].shape.__geo_interface__)Çözüm 2'de nasıl ararsınız? 'Den bir shape () yöntemine çağrı alamıyorum shapefile.py. Denedim bile shapefile.Shape(); Bunun için bir sınıf var ama çalışmıyor.
pstatix

Ayrıca, within()yöntemi nereden alıyorsunuz ?
pstatix

1
Shapely ( from shapely.geometry import shape,mapping, Point, Polygon, MultiPolygon)
gen

Çözüm 4'ü kullanarak bu hatayı alıyorum: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'
Aaron Bramson

6

İlk örneğinizdeki sorun şu döngüdedir:

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

Yalnızca son özellik noktalarını ekler. Bu şekil dosyasıyla yaklaşımımı denedim:

resim açıklamasını buraya girin

Kodunuzu şu şekilde değiştirdim:

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

Yukarıdaki kod QGIS Python Konsolunda çalıştırıldı ve sonuç:

resim açıklamasını buraya girin

Mükemmel çalışıyor ve şimdi, bir noktanın (x, y) her özelliğin sınırları içinde olup olmadığını kontrol edebilirsiniz.


0

Çokgen içindeki bir enlem, boylam noktasını kontrol etmeye çalışıyorsanız, aşağıdakiler tarafından oluşturulmuş nokta nesneniz olduğundan emin olun:

from shapely.geometry.point import Point
Point(LONGITUDE, LATITUDE)
..
poly.within(point) # Returns true if the point within the 

Nokta boylam, sonra argümanda enlem alır. Önce enlem değil. polygon_object.withinNoktanın şeklin içinde olup olmadığını kontrol etmek için işlevi çağırabilirsiniz .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.