简体   繁体   中英

Python : shapely, cascaded intersections within one polygon

I'd like to split a polygon into a list of polygons corresponding to all intersections with other polygons (and intersections between themselves).

多边形A和清单(B,C)

from shapely.geometry import Point

circleA = Point((0, 0)).buffer(1)
circleB = Point((1, 0)).buffer(1)
circleC = Point((1, 1)).buffer(1)

def cascaded_intersections(poly1, lst_poly):
    # ???
    return result

result = cascaded_intersections(circleA, (circleB, circleC))

The result should be a list of 4 Polygons, corresponding to the 4 complementary parts of A (above: [AC!B, ABC, AB!C, rest of A]).

The problem is the same than spitting a polygon into its smallest parts from a list of covering LineStrings.

How to write cascaded_intersections ?

A colleague of mine, Pascal L., found a solution :

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from shapely.geometry import MultiPolygon, Polygon, Point, GeometryCollection
from shapely.ops import cascaded_union

EMPTY = GeometryCollection()

def partition(poly_a, poly_b):
    """
    Splits polygons A and B into their differences and intersection.
    """
    if not poly_a.intersects(poly_b):
        return poly_a, poly_b, EMPTY
    only_a = poly_a.difference(poly_b)
    only_b = poly_b.difference(poly_a)
    inter  = poly_a.intersection(poly_b)
    return only_a, only_b, inter


def eliminate_small_areas(poly, small_area):
    """
    Eliminates tiny parts of a MultiPolygon (or Polygon)
    """
    if poly.area < small_area:
        return EMPTY
    if isinstance(poly, Polygon):
        return poly
    assert isinstance(poly, MultiPolygon)
    l = [p for p in poly if p.area > small_area]
    if len(l) == 0:
        return EMPTY
    if len(l) == 1:
        return l[0]
    return MultiPolygon(l)


def cascaded_intersections(poly1, lst_poly):
    """
    Splits Polygon poly1 into intersections of/with list of other polygons.
    """

    result = [(lst_poly[0], (0,))]

    for i, poly in enumerate(lst_poly[1:], start=1):

        current = []

        while result:
            result_geometry, result_indexes = result.pop(0)
            only_result, only_poly, inter = partition(result_geometry, poly)
            for geometry, indexes in ((only_result, result_indexes), (inter, result_indexes + (i,))):
                if not geometry.is_empty:
                    current.append((geometry, indexes))
        current_union = cascaded_union([elt[0] for elt in current])
        only_poly = poly.difference(current_union)
        if not only_poly.is_empty:
            current.append((only_poly, (i,)))
        result = current

    for r in range(len(result)-1, -1, -1):
        geometry, indexes = result[r]
        if poly1.intersects(geometry):
            inter = poly1.intersection(geometry)
            result[r] = (inter, indexes)
        else:
            del result[r]

    only_poly1 = poly1.difference(cascaded_union([elt[0] for elt in result]))
    only_poly1 = eliminate_small_areas(only_poly1, 1e-16*poly1.area)
    if not only_poly1.is_empty:
        result.append((only_poly1, None))

    return [r[0] for r in result]

a=Point(0,0).buffer(1)
b1=Point(0,1).buffer(1)
b2=Point(1,0).buffer(1)
b3=Point(1,1).buffer(1)

result = cascaded_intersections(a, (b1,b2,b3))

Como va. Hi Eric, I tried using the split function from shapely.ops. Here is the result. This is not the most time efficient or elegant solution but it works:

import matplotlib.pyplot as plt
import numpy as np #use np.random to give random RGB color to each polygon

import shapely.geometry as sg
from shapely.ops import split

import descartes

from itertools import combinations

def cascade_split(to_split,splitters): #Helper function for split recursion
    '''
    Return a list of all intersections between multiple polygons.
    to_split: list, polygons or sub-polygons to split
    splitters: list, polygons used as splitters

    Returns a list of all the polygons formed by the multiple intersections.
    '''

    if len(splitters) == 0: # Each splitting geometry will be removed
        return to_split     #  at the end of the function, reaching len == 0 at some point,
                            # only the it will return all the final splits.
    new_to_split = [] # make a list that will run again though the function

    for ts in to_split:
        s = split(ts,splitters[0].boundary) # split geometry using the boundaries of another 

        for i in list(s):
            new_to_split.append(i) #save the splits

    splitters.remove(splitters[0]) #remove the splitting geometry to 
                                #allow the split with the next polygon in line.

    return cascade_split(new_to_split,splitters) #Use recursion to exhaust all splitting possibilities

#Create polygons, in this case circles.
circleA = sg.Point((0, 0)).buffer(1)
circleB = sg.Point((1, 0)).buffer(1)
circleC = sg.Point((1, 1)).buffer(1)

#Put all circles in list
circles = [circleA,circleB,circleC]

#The combinations tool takes the last polygon 
#from list to split with the remaning polygons in list,
#creating a backwards copy of the circles list will help keep track of shapes.

back_circles = circles[::-1] #backwards copy of circles list

index_count = 0 #Keep track of which circle will get splitted

polys = [] #Final list of splitted polygons

for i in combinations(circles,len(circles)-1):

    c_split = cascade_split([back_circles[index_count]],list(i)) #Use helper function here

    for p in c_split:
        #There will be duplicate polygon splits, the following condition will filter those: 
        if not any(poly.equals(p) for poly in polys): 
            polys.append(p) 

    index_count += 1

#plotting settings
plt.figure(figsize=(5,5)) 
ax = plt.gca()

for e in range(len(polys)):

    ax.add_patch(descartes.PolygonPatch(polys[e], 
                                        fc=np.random.rand(3), #give random color to each split
                                        ec=None, 
                                        alpha=0.5))

    ax.text(polys[e].centroid.x,polys[e].centroid.y,
            '%s' %(e+1),fontsize=9,
            bbox=dict(facecolor='orange', alpha=0.5),
            color='blue',
            horizontalalignment='center')

plt.xlim(-1.5,2.5)
plt.ylim(-1.5,2.5)
plt.show()

polys #Output the polys list to see all the splits

在此处输入图片说明

Hi hi again , here's a better solution than my own, using part of gene's answer @ stackexchange.com it uses shapely.ops functions cascaded_union , unary_union and polygonize .

import matplotlib.pyplot as plt
import numpy as np

import shapely.geometry as sg
from shapely.ops import cascaded_union, unary_union, polygonize
import shapely.affinity

import descartes

from itertools import combinations



circleA = sg.Point((0, 0)).buffer(1)
circleB = sg.Point((1, 0)).buffer(1)
circleC = sg.Point((1, 1)).buffer(1)

circles = [circleA,circleB,circleC]

listpoly = [a.intersection(b) for a, b in combinations(circles, 2)] #list of intersections

rings = [sg.LineString(list(pol.exterior.coords)) for pol in listpoly] #list of rings

union = unary_union(rings)

result = [geom for geom in polygonize(union)] #list all intersection geometries

multi = cascaded_union(result) #Create a single geometry out of all intersections
fin = [c.difference(multi) for c in circles] #Cut multi from circles and leave only outside geometries.

result = result + fin #add the outside geometries to the intersections geometries

#Plot settings:

plt.figure(figsize=(5,5))
ax = plt.gca()

name = 1

for e in result:

    ax.add_patch(descartes.PolygonPatch(e, 
                                        fc=np.random.rand(3), 
                                        ec=None, 
                                        alpha=0.5))

    ax.text(e.centroid.x,e.centroid.y,
            '%s'%name,fontsize=9,
            bbox=dict(facecolor='orange', alpha=0.5),
            color='blue',
            horizontalalignment='center')
    name += 1

plt.xlim(-1.5,2.5)
plt.ylim(-1.5,2.5)
plt.show()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM