【问题标题】:Python : shapely, cascaded intersections within one polygonPython:一个多边形内的匀称的级联交叉点
【发布时间】:2019-01-29 15:28:44
【问题描述】:

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

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

结果应该是 4 个多边形的列表,对应于 A 的 4 个互补部分(上图:[AC!B, ABC, AB!C, rest of A])。

这个问题与从覆盖的 LineStrings 列表中将多边形分成最小的部分一样。

cascaded_intersections怎么写?

【问题讨论】:

    标签: python polygon shapely


    【解决方案1】:

    我的一位同事 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))
    

    【讨论】:

      【解决方案2】:

      再次嗨,这里有一个比我自己更好的解决方案,使用基因的部分答案 @stackexchange.com 它使用 shapely.ops 函数 cascaded_unionunary_union 多边形化

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

      【讨论】:

      • 同上:最初的问题不是拆分(circleA,circleB,circleC),而是只有circleA和(circleB,circleC)。尽管如此,使用 itertools.combination() 似乎是个好主意。请不要将绘图包含在您的解决方案中,而仅包含严格的结果。
      • 由于 c.difference() 会出现一些毛刺。结果显示了一些 MultiPolygons 而不是纯多边形。这个(不可避免的副作用)可以通过我在 1 月 30 日的回答中描述的消除小区域()来消除。
      • cascaded_union 是 superceded by unary_union,所以这里有什么用途吗?
      【解决方案3】:

      科莫弗吉尼亚州。嗨 Eric,我尝试使用 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
      

      【讨论】:

      • 最开始的问题不是拆分(circleA, circleB, circleC),而是只拆分circleA和(circleB, circleC)。尽管如此,使用 itertools.combination() 似乎是个好主意。请不要在您的解决方案中包含带有笛卡尔的绘图,但要包含严格的结果。
      猜你喜欢
      • 2013-01-05
      • 2017-01-20
      • 2019-01-29
      • 2015-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-08
      • 2019-11-07
      相关资源
      最近更新 更多