Kiểm tra xem một điểm có nằm trong đa tuyến với Python không


13

Tôi đã thử một vài ví dụ về mã bằng các thư viện như shapefile, fiona và ogr để kiểm tra xem một điểm (x, y) có nằm trong ranh giới của một đa đường được tạo bằng ArcMap (và do đó ở định dạng shapefile). Tuy nhiên, không có ví dụ nào hoạt động tốt với đa chuỗi, mặc dù chúng hoạt động tốt với các shapefile đa giác thông thường, đơn. Một số đoạn tôi đã thử dưới đây:

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

Ví dụ đầu tiên hoạt động tốt với một đa giác duy nhất tại một thời điểm, nhưng khi tôi nhập một điểm trong một trong các hình dạng trong hình đa giác của mình, nó sẽ trả về "ngoài" mặc dù nó nằm trong một trong nhiều phần.

Đối với ví dụ thứ hai, tôi gặp lỗi "đối tượng loại 'Hình học' không có len ()" mà tôi giả sử là do trường hình học không thể được đọc dưới dạng danh sách / mảng được lập chỉ mục, bình thường.

Tôi cũng đã cố gắng thay thế điểm thực tế trong mã đa giác như được đề xuất ở đây để đảm bảo rằng đó không phải là một phần của mã không hoạt động. Và trong khi các ví dụ của liên kết đó hoạt động tốt với các shapefile đa giác đơn giản, tôi không thể kiểm tra đa giác phức tạp của mình để kiểm tra chính xác.

Vì vậy, tôi không thể nghĩ ra bất kỳ cách nào khác để kiểm tra xem một điểm có nằm trong một shapefile đa đường thông qua python không ... Có thể có những thư viện khác ngoài đó tôi đang thiếu?


ví dụ thứ hai của bạn có vẻ như nó có thể ép đa giác thành đa giác? Nó chỉ có thể được kiểm tra điểm so với phần đầu tiên của đa giác. Hãy thử di chuyển điểm xung quanh đến các phần khác nhau và xem kiểm tra có bao giờ thành công không.
obrl_soil

@obrl_soil Cảm ơn bạn đã gợi ý. Tuy nhiên, ví dụ thứ hai không bao giờ hoạt động do thông báo lỗi tôi đã mô tả ở trên (đối tượng loại 'Hình học' không có len ()) "cho dù tôi thử MultiPolygon (hình học) hay đơn giản là Đa giác (hình học). Tôi đã thử nhiều điểm trong ví dụ đầu tiên và chỉ những người trong đa giác chính hoạt động. Hy vọng việc làm rõ này sẽ giúp.
spartmar

Vâng, tôi nghĩ rằng bạn cần phải thay thế polygon = Polygon(geometry)bằng một số loại vòng lặp thử nơi nó chuyển sang polygon = MultiPolygon(geometry)nếu lỗi đó xảy ra.
obrl_soil

Vấn đề trong ví dụ đầu tiên của bạn là trong vòng lặp đầu tiên.
xunilk

Câu trả lời:


24

Shapefiles không có loại MultiPolygon (type = Polygon), nhưng dù sao chúng cũng hỗ trợ chúng (tất cả các vòng được lưu trữ trong một tính năng = danh sách đa giác, hãy xem Chuyển đổi đa giác khổng lồ thành đa giác )

Vấn đề

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

Nếu tôi mở một shapefile MultiPolygon, hình học là 'Đa giác'

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

Giải pháp 1 với 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 diễn giải tính năng này dưới dạng MultiPolygon và bạn có thể áp dụng giải pháp được trình bày trong Tham gia không gian hiệu quả hơn trong Python mà không cần QGIS, ArcGIS, PostGIS, v.v. (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)

Giải pháp 2 với pyshp (shapefile) và giao thức Geo_interface (GeoJSON like)

Đây là phần bổ sung cho câu trả lời của 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)

Giải pháp 3 với ogr và giao thức Geo_interface ( ứng dụng 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)

Giải pháp 4 với GeoPandas như trong Không gian hiệu quả hơn tham gia vào Python mà không cần QGIS, ArcGIS, PostGIS, v.v (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]} 

Các điểm 1,3,5,6 nằm trong ranh giới của MultiPolygon


Chủ đề hơi cũ ở đây, nhưng làm thế nào để bạn gọi multi = shape(polys.shapeRecords()[0].shape.__geo_interface__)trong Giải pháp 2? Tôi không thể nhận được một cuộc gọi đến một phương thức shape () từ shapefile.py. Tôi thậm chí đã thử shapefile.Shape(); Có một lớp cho nó nhưng nó không hoạt động.
pstatix

Hơn nữa, bạn lấy within()phương pháp từ đâu?
pstatix

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

Tôi gặp lỗi này khi sử dụng Giải pháp 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

Vấn đề trong ví dụ đầu tiên của bạn là trong vòng lặp này:

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

Nó chỉ nối các điểm tính năng cuối cùng. Tôi đã thử cách tiếp cận của mình với shapefile này:

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

Tôi đã sửa đổi mã của bạn thành:

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

Mã ở trên đã được chạy tại Bảng điều khiển Python của QGIS và kết quả là:

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

Nó hoạt động hoàn hảo và bây giờ, bạn có thể kiểm tra xem một điểm (x, y) có nằm trong ranh giới của từng tính năng hay không.

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.