【问题标题】:Sorting according to clockwise point coordinates按顺时针点坐标排序
【发布时间】:2018-12-07 01:40:26
【问题描述】:

给定一个 Python 列表,其中包含 4 个点的 8 个 x、y 坐标值(全部为正),如 [x1, x2, x3, x4, y1, y2, y3, y4](xi, yi) 是第 i 个点的 x 和 y 坐标),

我如何对其进行排序,以便新列表 [a1, a2, a3, a4, b1, b2, b3, b4] 使得 1 2 3 4 的坐标 (ai, bi) 按顺时针顺序排列,其中 1 最接近 xy 平面的原点,即类似

          2--------3
          |        |
          |        |
          |        |
          1--------4

点将大致形成一个平行四边形。

目前,我正在考虑找到 (x+y) 的最小值为 1 的点,然后在剩余坐标中找到 x 最小的点为 2,通过 (x + y) 的最大值找到 3,其余为 4点

【问题讨论】:

  • 从中心获取 atan2。
  • @IgnacioVazquez-Abrams 这是个好主意
  • 不需要直接计算角度。只需使用 2D 叉积的符号作为比较器对它们进行排序。
  • @meowgoesthedog 这也可以,但不如计算角度那么有效,因为使用 2D 叉积的符号意味着使用 cmp 函数来比较列表中的两个坐标一次。计算角度意味着我们可以将它们用作key 参数的比较值,这样效率更高。见Why is the cmp parameter removed from sort/sorted in Python3.0?
  • @meowgoesthedog 第二个想法实际上是行不通的,因为对于列表中的每个坐标,总会有另一个坐标,其到质心的向量与所述坐标的向量顺时针方向,所以不会有比较的“底部”。在 OP 的情况下,我们需要 -135 度作为已排序坐标列表的“底部”。 2D 叉积的符号只能帮助确定一个向量是否顺时针指向另一个向量,但不能帮助确定向量与“底部”(即 -135 度)的距离。

标签: python sorting math coordinates


【解决方案1】:

您应该使用包含 2 项元组的列表作为数据结构,以有意义的方式表示可变数量的坐标。

from functools import reduce
import operator
import math
coords = [(0, 1), (1, 0), (1, 1), (0, 0)]
center = tuple(map(operator.truediv, reduce(lambda x, y: map(operator.add, x, y), coords), [len(coords)] * 2))
print(sorted(coords, key=lambda coord: (-135 - math.degrees(math.atan2(*tuple(map(operator.sub, coord, center))[::-1]))) % 360))

这个输出:

[(0, 0), (0, 1), (1, 1), (1, 0)]

【讨论】:

  • 你能解释一下这个解决方案吗?
  • 有理由转换为 degress 吗?这似乎增加了相当多的复杂性。
【解决方案2】:
import math

def centeroidpython(data):
    x, y = zip(*data)
    l = len(x)
    return sum(x) / l, sum(y) / l

xy = [405952.0, 408139.0, 407978.0, 405978.0, 6754659.0, 6752257.0, 6754740.0, 6752378.0]
xy_pairs = list(zip(xy[:int(len(xy)/2)], xy[int(len(xy)/2):]))

centroid_x, centroid_y = centeroidpython(xy_pairs)
xy_sorted = sorted(xy_pairs, key = lambda x: math.atan2((x[1]-centroid_y),(x[0]-centroid_x)))
xy_sorted_x_first_then_y = [coord for pair in list(zip(*xy_sorted)) for coord in pair]

【讨论】:

    【解决方案3】:
    # P4=8,10 P1=3,5   P2=8,5   P3=3,10
    points=[8,3,8,3,10,5,5,10]
    k=0
    #we know these numbers are extreme and data won't be bigger than these
    xmin=1000
    xmax=-1000
    ymin=1000
    ymax=-1000
    #finding min and max values of x and y
    for i in points:
        if  k<4:
            if (xmin>i): xmin=i
            if (xmax<i): xmax=i        
        else:
            if (ymin>i): ymin=i
            if (ymax<i): ymax=i        
        k +=1
    
    sortedlist=[xmin,xmin,xmax,xmax,ymin,ymax,ymax,ymin]
    print(sortedlist)
    

    输出:[3, 3, 8, 8, 5, 10, 10, 5] 对于其他地区,您需要更改 sortedlist 行。如果中心在框内,则需要更多的条件控制

    【讨论】:

      【解决方案4】:

      我们要排序的是与起始坐标的角度。我在这里使用 numpy 将起始坐标中的每个向量解释为一个复数,有一种简单的方法可以计算角度(沿单位球体逆时针方向)

      def angle_with_start(coord, start):
          vec = coord - start
          return np.angle(np.complex(vec[0], vec[1]))
      

      完整代码:

      import itertools
      import numpy as np
      
      
      def angle_with_start(coord, start):
          vec = coord - start
          return np.angle(np.complex(vec[0], vec[1]))
      
      
      def sort_clockwise(points):
          # convert into a coordinate system
          # (1, 1, 1, 2) -> (1, 1), (1, 2)
          coords = [np.array([points[i], points[i+4]]) for i in range(len(points) // 2)]
      
          # find the point closest to the origin,
          # this becomes our starting point
          coords = sorted(coords, key=lambda coord: np.linalg.norm(coord))
          start = coords[0]
          rest = coords[1:]
      
          # sort the remaining coordinates by angle
          # with reverse=True because we want to sort by clockwise angle
          rest = sorted(rest, key=lambda coord: angle_with_start(coord, start), reverse=True)
      
          # our first coordinate should be our starting point
          rest.insert(0, start)
          # convert into the proper coordinate format
          # (1, 1), (1, 2) -> (1, 1, 1, 2)
          return list(itertools.chain.from_iterable(zip(*rest)))
      

      一些示例输入的行为:

      In [1]: a
      Out[1]: [1, 1, 2, 2, 1, 2, 1, 2]
      
      In [2]: sort_clockwise(a)
      Out[2]: [1, 1, 2, 2, 1, 2, 2, 1]
      
      In [3]: b
      Out[3]: [1, 2, 0, 2, 1, 2, 3, 1]
      
      In [4]: sort_clockwise(b)
      Out[4]: [1, 0, 2, 2, 1, 3, 2, 1]
      

      【讨论】:

        【解决方案5】:

        基于 BERA 的回答,但作为一个班级:

        代码

        import math
        
        def class Sorter:
            @staticmethod    
            def centerXY(xylist):
                x, y = zip(*xylist)
                l = len(x)
                return sum(x) / l, sum(y) / l  
        
            @staticmethod    
            def sortPoints(xylist):  
                cx, cy = Sorter.centerXY(xylist)
                xy_sorted = sorted(xylist, key = lambda x: math.atan2((x[1]-cy),(x[0]-cx)))
                return xy_sorted
        

        测试

        def test_SortPoints():
            points=[(0,0),(0,1),(1,1),(1,0)]
            center=Sorter.centerXY(points)
            assert center==(0.5,0.5)
            sortedPoints=Sorter.sortPoints(points)
            assert sortedPoints==[(0, 0), (1, 0), (1, 1), (0, 1)]
        

        【讨论】:

          【解决方案6】:

          按照 IgnacioVazquez-Abrams 的建议,我们也可以根据 atan2 角度进行排序:

          代码:

          import math
          import copy
          import matplotlib.pyplot as plt
          
          a = [2, 4, 5, 1, 0.5, 4, 0, 4]
          print(a)
          
          
          def clock(a):
              angles = []
              (x0, y0) = ((a[0]+a[1]+a[2]+a[3])/4, (a[4]+ a[5] + a[6] + a[7])/4)  # centroid
              for j in range(4):
                  (dx, dy) = (a[j] - x0, a[j+4] - y0)
                  angles.append(math.degrees(math.atan2(float(dy), float(dx))))
              for k in range(4):
                  angles.append(angles[k] + 800)
              # print(angles)
          
              z = [copy.copy(x) for (y,x) in sorted(zip(angles,a), key=lambda pair: pair[0])]
              print("z is: ", z)
          
          plt.scatter(a[:4], a[4:8])
          plt.show()
          
          clock(a)
          

          输出是:

          [2, 4, 5, 1, 0.5, 4, 0, 4]
          [-121.60750224624891, 61.92751306414704, -46.73570458892839, 136.8476102659946, 678.3924977537511, 861.9275130641471, 753.2642954110717, 936.8476102659946]
          z is:  [2, 5, 4, 1, 0.5, 0, 4, 4]
          

          【讨论】:

            【解决方案7】:

            试试这行代码

            def sort_clockwise(pts):
                rect = np.zeros((4, 2), dtype="float32")
                s = pts.sum(axis=1)
                rect[0] = pts[np.argmin(s)]
                rect[2] = pts[np.argmax(s)]
                diff = np.diff(pts, axis=1)
                rect[1] = pts[np.argmin(diff)]
                rect[3] = pts[np.argmax(diff)]
                return rect
            

            【讨论】:

              猜你喜欢
              • 2012-10-29
              • 2011-10-22
              • 2010-09-19
              • 1970-01-01
              • 2011-10-23
              • 1970-01-01
              • 2011-10-16
              • 2021-08-16
              • 2018-06-05
              相关资源
              最近更新 更多