Skuteczne uzyskiwanie przecięcia wielu wielokątów w Pythonie

12

Chciałbym uzyskać przecięcie wielu wielokątów. Korzystając z shapelypakietu Pythona , mogę znaleźć przecięcie dwóch wielokątów za pomocą intersectionfunkcji. Czy istnieje podobna skuteczna funkcja uzyskiwania przecięcia wielu wielokątów?

Oto fragment kodu, aby zrozumieć, co mam na myśli:

from shapely.geometry import Point

coord1 = ( 0,0 )
point1 = Point(coord1)
circle1 = point1.buffer(1)

coord2 = ( 1,1 )
point2 = Point(coord2)
circle2 = point2.buffer(1)

coord3 = ( 1,0 )
point3 = Point(coord3)
circle3 = point3.buffer(1) 

Przecięcie dwóch kręgów można znaleźć przez circle1.intersection(circle2). Mogę znaleźć przecięcie wszystkich trzech kręgów według circle1.intersection(circle2).intersection(circle3). Jednak tego podejścia nie można zastosować do dużej liczby wielokątów, ponieważ wymaga ono coraz więcej kodu. Chciałbym funkcji, która pobiera dowolną liczbę wielokątów i zwraca ich przecięcie.

drzazga
źródło
myślę, że może przechowywać współrzędne w słowniku i przeglądać je podczas korzystania z kombinacji importu itertools. Wkrótce
opublikuję
Co rozumiesz przez „ich skrzyżowania”? Czy masz na myśli wszystkie obszary, które przecinają się z co najmniej jednym innym wielokątem, czy obszary, które przecinają wszystkie dane wejściowe?
jpmc26
Mam na myśli przecięcie wszystkich wielokątów, przynajmniej jednego.
odprysk
Powinieneś to wyjaśnić powyżej (być może z przykładowym wyjściem). Jestem pewien, że większość odpowiedzi nie zachowuje się tak, jak chcesz. (A fakt, że kilku respondentów źle zrozumiało, jest wystarczającym dowodem, że pytanie wymaga wyjaśnienia.)
jpmc26
1
@ jpmc26 Właśnie dodałem aktualizację do mojej odpowiedzi, w której używany jest rtree. Podejście jest teraz bardziej wydajne i skalowalne. Mam nadzieję że to pomoże!
Antonio Falciano,

Odpowiedzi:

7

Jednym z możliwych podejść może być rozważenie kombinacji par wielokątów, ich skrzyżowań i wreszcie połączenia wszystkich skrzyżowań poprzez połączenie kaskadowe (jak sugerowano tutaj ):

from shapely.geometry import Point
from shapely.ops import cascaded_union
from itertools import combinations

circles = [
    Point(0,0).buffer(1),
    Point(1,0).buffer(1),
    Point(1,1).buffer(1),
]

intersection = cascaded_union(
    [a.intersection(b) for a, b in combinations(circles, 2)]
)
print intersection

Bardziej wydajne podejście powinno wykorzystywać indeks przestrzenny, taki jak Rtree , aby poradzić sobie z wieloma geometriami (nie dotyczy to trzech okręgów):

from shapely.geometry import Point
from shapely.ops import cascaded_union
from rtree import index

circles = [
    Point(0,0).buffer(1),
    Point(1,0).buffer(1),
    Point(1,1).buffer(1),
]
intersections = []
idx = index.Index()

for pos, circle in enumerate(circles):
    idx.insert(pos, circle.bounds)

for circle in circles:
    merged_circles = cascaded_union([circles[pos] for pos in idx.intersection(circle.bounds) if circles[pos] != circle])
    intersections.append(circle.intersection(merged_circles))

intersection = cascaded_union(intersections)
print intersection
Antonio Falciano
źródło
Nie wierzę, że robi to, co chce OP. Zwraca obszary, które pokrywają co najmniej 2 wielokąty, podczas gdy OP szuka tylko obszarów pokrytych przez wszystkie wielokąty w zestawie. Zobacz wyjaśnienie w komentarzach.
jpmc26 18.04.17
3

Dlaczego nie skorzystać z iteracji lub rekurencyjności? coś jak :

from shapely.geometry import Point

def intersection(circle1, circle2):
    return circle1.intersection(circle2)

coord1 = ( 0,0 )
point1 = Point(coord1)
circle1 = point1.buffer(1)

coord2 = ( 1,1 )
point2 = Point(coord2)    
circle2 = point2.buffer(1)


coord3 = ( 1,0 )
point3 = Point(coord3)
circle3 = point3.buffer(1)
circles = [circle1, circle2, circle3]
intersectionResult = None

for j, circle  in enumerate(circles[:-1]):

    #first loop is 0 & 1
    if j == 0:
        circleA = circle
        circleB = circles[j+1]
     #use the result if the intersection
    else:
        circleA = intersectionResult
        circleB = circles[j+1]
    intersectionResult = intersection(circleA, circleB)

result= intersectionResult
julsbreakdown
źródło
2

Daj temu kodowi szansę. jest dość prosty w koncepcji i wierzę, że dostaniesz to, czego szukasz.

from shapely.geometry import Point
from itertools import combinations
dic ={}
dic['coord1']=Point(0,0).buffer(1)
dic['coord2']=Point(1,1).buffer(1)
dic['coord3']=Point(1,0).buffer(1)
inter = {k[0]+v[0]:k[1].intersection(v[1]) for k,v in combinations(dic.items(),2)}
print inter

a jeśli chcesz, aby dane wyjściowe były przechowywane jako plik kształtu, użyj fiona:

from shapely.geometry import Point,mapping
import fiona
from itertools import combinations
schema = {'geometry': 'Polygon', 'properties': {'Place': 'str'}}
dic ={}
dic['coord1']=Point(0,0).buffer(1)
dic['coord2']=Point(1,1).buffer(1)
dic['coord3']=Point(1,0).buffer(1)
inter = {k[0]+v[0]:k[1].intersection(v[1]) for k,v in combinations(dic.items(),2)}
print inter
with fiona.open(r'C:\path\abid', "w", "ESRI Shapefile", schema) as output:
    for x,y in inter.items():
        output.write({'properties':{'Place':x},'geometry':mapping(y)})

dane wyjściowe -

wprowadź opis zdjęcia tutaj

zygzak
źródło
3
Nie wierzę, że robi to, co chce OP. Zwraca obszary, które pokrywają co najmniej 2 wielokąty, podczas gdy OP szuka tylko obszarów pokrytych przez wszystkie wielokąty w zestawie. Zobacz wyjaśnienie w komentarzach. Dodatkowo, ki vsą kiepskim wyborem dla nazw zmiennych w twoim dictrozumieniu. Każda z tych zmiennych odnosi się do różnych elementów dic.items(), a nie pary klucz-wartość. Coś takiego a, bbyłoby mniej mylące.
jpmc26 18.04.17
1
och, okej, tak, nie rozumiem, co miał na myśli
ziggy
i dobrze przemyślane na temat moich opcji k, v - po prostu automatycznie używam k, v podczas zapętlania słownika .. nie zastanawiałem się długo
ziggy 18.04.17