ตรวจสอบว่าจุดหนึ่งอยู่ในหลายรูปหลายเหลี่ยมด้วย Python หรือไม่


13

ฉันได้ลองหลายตัวอย่างของรหัสโดยใช้ไลบรารีเช่น shapefile, fiona และ ogr เพื่อพยายามตรวจสอบว่ามีจุด (x, y) อยู่ในขอบเขตของรูปหลายเหลี่ยมที่สร้างด้วย ArcMap (และในรูปแบบ shapefile) อย่างไรก็ตามไม่มีตัวอย่างใดที่ทำงานได้ดีกับมัลติโพลิกอนถึงแม้ว่ามันจะใช้ได้ดีกับรูปร่างหลายเหลี่ยมแบบปกติ ตัวอย่างบางส่วนที่ฉันพยายามอยู่ด้านล่าง:

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

ตัวอย่างแรกทำงานได้ดีกับรูปหลายเหลี่ยมครั้งเดียว แต่เมื่อฉันป้อนจุดภายในรูปร่างใดรูปหนึ่งในรูปร่างหลายรูปแบบของฉันมันจะส่งกลับ "ออก" แม้ว่ามันจะตกอยู่ในหนึ่งในหลาย ๆ ส่วน

สำหรับตัวอย่างที่สองฉันได้รับข้อผิดพลาด "วัตถุชนิด 'เรขาคณิต' ไม่มีเลน ()" ซึ่งฉันถือว่าเป็นเพราะฟิลด์เรขาคณิตไม่สามารถอ่านได้ตามปกติรายการ / อาร์เรย์ที่จัดทำดัชนี

ฉันพยายามแทนที่จุดที่เกิดขึ้นจริงในรหัสรูปหลายเหลี่ยมตามที่แนะนำที่นี่เพื่อให้แน่ใจว่าไม่ใช่ส่วนหนึ่งของรหัสที่ไม่ทำงาน และในขณะที่ตัวอย่างของลิงค์นั้นทำงานได้ดีกับรูปหลายเหลี่ยมแบบง่ายฉันไม่สามารถรับรูปหลายเหลี่ยมที่ซับซ้อนเพื่อทดสอบได้อย่างถูกต้อง

ดังนั้นฉันจึงไม่สามารถคิดวิธีอื่นในการทดสอบว่ามีจุดตกอยู่ในไฟล์รูปร่างหลายรูปแบบผ่านหลาม ... บางทีอาจมีห้องสมุดอื่น ๆ ที่ฉันขาดหายไปหรือ


ตัวอย่างที่สองของคุณดูเหมือนว่าอาจมีการข่มขู่หลายรูปแบบกับรูปหลายเหลี่ยม? มันอาจจะตรวจสอบจุดกับส่วนแรกของรูปหลายเหลี่ยมเท่านั้น ลองย้ายจุดไปยังส่วนต่างๆและดูว่าการตรวจสอบนั้นประสบความสำเร็จหรือไม่
obrl_soil

@obrl_soil ขอบคุณสำหรับคำแนะนำของคุณ อย่างไรก็ตามตัวอย่างที่สองไม่ทำงานเนื่องจากข้อความแสดงข้อผิดพลาดที่ฉันอธิบายไว้ข้างต้น (วัตถุประเภท 'เรขาคณิต' ไม่มี len ()) "ไม่ว่าฉันจะลอง MultiPolygon (เรขาคณิต) หรือเพียงแค่รูปหลายเหลี่ยม (เรขาคณิต) ฉันลองหลายจุดใน . ตัวอย่างแรกและคนเดียวที่อยู่ในการทำงานรูปหลายเหลี่ยมหลักความหวังนี้จะช่วยให้ความกระจ่าง.
spartmar

ใช่ฉันคิดว่าคุณต้องแทนที่polygon = Polygon(geometry)ด้วยลองวนรอบที่มันจะเปลี่ยนไปpolygon = MultiPolygon(geometry)ถ้าเกิดข้อผิดพลาด
obrl_soil

ปัญหาในตัวอย่างแรกของคุณอยู่ในลูปแรก
xunilk

คำตอบ:


24

Shapefiles ไม่มีประเภท MultiPolygon (type = Polygon) แต่ก็รองรับได้อยู่ดี (วงแหวนทั้งหมดจะถูกเก็บไว้ในหนึ่ง Feature = list ของรูปหลายเหลี่ยมดูที่การแปลงรูปหลายเหลี่ยมขนาดใหญ่เป็นรูปหลายเหลี่ยม )

ปัญหา

ป้อนคำอธิบายรูปภาพที่นี่

ถ้าฉันเปิดไฟล์รูปร่าง MultiPolygon เรขาคณิตคือ 'รูปหลายเหลี่ยม'

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

โซลูชันที่ 1 กับFiona

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 ตีความคุณสมบัติเป็น MultiPolygon และคุณสามารถใช้วิธีแก้ปัญหาที่แสดงในMore Efficient Spatial ร่วมใน Python โดยไม่ต้องใช้ QGIS, ArcGIS, PostGIS, ฯลฯ (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)

โซลูชันที่ 2 พร้อมกับpyshp (shapefile)และโปรโตคอล geo_interface (GeoJSON like)

นี่เป็นส่วนเสริมของคำตอบของ 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)

โซลูชัน 3 ที่มีogr และโปรโตคอลgeo_interface ( แอปพลิเคชัน 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)

โซลูชัน 4 ที่มีGeoPandasเช่นเดียวกับในSpatial More Efficient Spatial เข้าร่วมใน Python โดยไม่มี QGIS, ArcGIS, PostGIS, ฯลฯ (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 อยู่ในขอบเขตของ MultiPolygon


เธรดเก่าที่นี่เล็กน้อย แต่คุณจะโทรmulti = shape(polys.shapeRecords()[0].shape.__geo_interface__)ในโซลูชัน 2 ได้อย่างไร ฉันไม่สามารถรับวิธีเรียกรูปร่าง () shapefile.pyได้ ฉันได้ลองshapefile.Shape()แล้ว มีคลาสสำหรับมัน แต่มันใช้งานไม่ได้
pstatix

นอกจากนี้คุณจะได้รับwithin()วิธีการจากที่ไหน?
pstatix

1
จาก Shapely ( 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'
Aaron Bramson

6

ปัญหาในตัวอย่างแรกของคุณอยู่ในลูปนี้:

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

มันจะต่อท้ายฟีเจอร์สุดท้ายเท่านั้น ฉันลองวิธีของฉันกับ shapefile นี้:

ป้อนคำอธิบายรูปภาพที่นี่

ฉันแก้ไขรหัสของคุณเป็น:

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

โค้ดข้างต้นถูกรันที่ Python Console ของ QGIS และผลลัพธ์คือ:

ป้อนคำอธิบายรูปภาพที่นี่

มันทำงานได้อย่างสมบูรณ์แบบและตอนนี้คุณสามารถตรวจสอบว่าจุด (x, y) อยู่ในขอบเขตของแต่ละคุณสมบัติหรือไม่


0

หากคุณพยายามตรวจสอบละติจูดจุดลองจิจูดภายในรูปหลายเหลี่ยมตรวจสอบให้แน่ใจว่าคุณมีวัตถุจุดถูกสร้างขึ้นโดยต่อไปนี้:

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

Point ใช้ลองจิจูดแล้วละติจูดในการโต้แย้ง ไม่ใช่ละติจูดก่อน คุณสามารถเรียกpolygon_object.withinใช้ฟังก์ชันเพื่อตรวจสอบว่าจุดนั้นอยู่ในรูปร่างหรือไม่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.