Перевірте, чи крапка потрапляє в мультиполігон з 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'

Перший приклад прекрасно спрацьовує з одним багатокутником одночасно, але коли я ввожу крапку в одній із фігур у своєму мультиполігоновому форматі, він повертається "назовні", хоча він потрапляє всередину однієї з багатьох частин.

Для другого прикладу я отримую помилку "об'єкт типу" Геометрія "не має len ()", який я припускаю, тому що поле геометрії не можна читати як звичайний, індексований список / масив.

Я також спробував замінити фактичну точку коду полігону, як тут пропонується, щоб переконатися, що це не та частина коду, яка не працює. І хоча приклади цього посилання добре працюють із простими формами багатокутника, я не можу змусити мій складний багатополігон перевірити належним чином.

Тому я не можу придумати будь-який інший спосіб перевірити, чи потрапляє точка в мультиполігонний файл форми через python ... Можливо, там є інші бібліотеки, яких мені не вистачає?


ваш другий приклад виглядає так, що це може бути примусовий багатокутник до багатокутника? Це може бути лише перевірка точки проти першої частини багатокутника. Спробуйте перемістити точку навколо різних частин і побачити, чи перевірка ніколи не вдається.
obrl_soil

@obrl_soil Дякую за пропозицію. Однак другий приклад ніколи не працює через описане вище повідомлення про помилку (об’єкт типу "Геометрія" не має len ()) "я намагаюся MultiPolygon (геометрія) або просто Polygon (геометрія). Я намагався багато пунктів у Перший приклад і лише ті, що знаходяться в основному творі багатокутника. Сподіваюся, що це уточнення допоможе.
Спартмар

Так, я думаю, що вам потрібно замінити polygon = Polygon(geometry)якусь цикл спробу, де він переходить, polygon = MultiPolygon(geometry)якщо виникає ця помилка.
obrl_soil

Проблема у вашому першому прикладі полягає у першому циклі.
xunilk

Відповіді:


24

У Shapefiles немає типу MultiPolygon (type = Polygon), але вони все одно підтримують їх (усі кільця зберігаються в одній функції = список багатокутників. Подивіться на Перетворення величезного багатокутника в полігони )

Проблема

введіть тут опис зображення

Якщо я відкрию файл форми MultiPolygon, геометрія - "Полігон"

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

Рішення 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 інтерпретує цю функцію як MultiPolygon, і ви можете застосувати рішення, представлене в " Ефективнішому просторовому об'єднанні в 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)

Це доповнення до відповіді 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 як у більш ефективному просторовому приєднанні до 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 потрапляють у межі Мультиполігону


Тут трохи стара тема, але як ви називаєте multi = shape(polys.shapeRecords()[0].shape.__geo_interface__)рішення 2? Не можу отримати дзвінок до методу shape () від 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'
Аарон

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

Наведений вище код був запущений на консолі Python QGIS, і результат:

введіть тут опис зображення

Це прекрасно працює, і тепер ви можете перевірити, чи точка (x, y) потрапляє в межі кожної функції.


0

Якщо ви намагаєтеся перевірити точку широти, довготи в полігоні, переконайтеся, що у вас є об'єкт точки, створений таким чином:

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

Точка займає довготу, а потім широту в аргументі. Найперше не широта. Ви можете викликати polygon_object.withinфункцію, щоб перевірити, чи точка знаходиться у формі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.