Python:一个多边形内的形状,级联相交点

问题描述 投票:1回答:3

我想将一个多边形拆分为与其他多边形(以及它们之间的交集)的所有交点相对应的多边形列表。

Polygon A and list (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))

结果应该是与A的4个互补部分相对应的4个多边形的列表(上方:[AC!B,ABC,AB!C,A的其余部分]。]

问题与将多边形从覆盖LineString的列表中分割成最小的部分相同。

如何编写级联的交叉点?

python polygon shapely
3个回答
0
投票

[我的一位同事Pascal L.找到了解决方案:

#!/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))

0
投票

再次打个招呼,这是一个比我自己更好的解决方案,使用基因答案的一部分@ stackexchange.com,它使用shapely.ops函数cascaded_unionunary_unionpolygonize

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()

0
投票

科莫VA。嗨,埃里克,我尝试使用shapely.ops中的split函数。这是结果。这不是最省时或优雅的解决方案,但它可以起作用:

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

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.