• 多边形内部水平方向近似最大矩形python实现


    参考 求任意多边形内部水平方向似最大矩形算法实现_nullpo_的博客

    原文是用 grooovy代码写的,不太好懂,这里把主要逻辑改成python版,测了下效果还行。

    代码如下: )

    1. import math
    2. import pandas as pd
    3. import numpy as np
    4. from collections import deque
    5. import matplotlib.pyplot as plt
    6. from numba import jit
    7. class Rectangle:
    8. """
    9. 矩形范围(包围盒).
    10. """
    11. def __init__(self, minLon, maxLon, minLat, maxLat):
    12. self.minLon = minLon
    13. self.maxLon = maxLon
    14. self.minLat = minLat
    15. self.maxLat = maxLat
    16. def myceil(d, scale):
    17. """
    18. scale 表示精度如0.1, 0.01等
    19. """
    20. n = int(1 / scale)
    21. return math.ceil(d * n) / n
    22. def myfloor(d, scale):
    23. n = int(1 / scale)
    24. return math.floor(d * n) / n
    25. def min_enclosing_rectangle(pt_list):
    26. """
    27. 获取多边形区域的最小外接矩形.
    28. """
    29. rec = Rectangle(
    30. minLon=float('inf'), maxLon=float('-inf'),
    31. minLat=float('inf'), maxLat=float('-inf')
    32. )
    33. pts = np.array(pt_list)
    34. rec.minLon = np.min(pts[:, 0])
    35. rec.maxLon = np.max(pts[:, 0])
    36. rec.minLat = np.min(pts[:, 1])
    37. rec.maxLat = np.max(pts[:, 1])
    38. return rec
    39. def compute_regional_slices(rec, scale):
    40. """
    41. 根据矩形和切分尺度获取切分矩阵的点阵.
    42. 为了减少计算量,这里输出的并不是真的矩形,由于是等距连续切分,直接输出切分的数据点即可,以左上角数据点作为标的.
    43. """
    44. # 1.根据切分尺度标准化矩形
    45. minLon = myfloor(rec.minLon, scale)
    46. maxLon = myceil(rec.maxLon, scale)
    47. minLat = myfloor(rec.minLat, scale)
    48. maxLat = myceil(rec.maxLat, scale)
    49. ndigit = int(math.log10(int(1 / scale))) + 1
    50. # 2.切分(出于人类习惯,暂定从上往下按行切分,即按纬度从大到小,经度从小到大)
    51. matrix = []
    52. for posLat in np.arange(maxLat, minLat, -scale):
    53. row = []
    54. for posLon in np.arange(minLon, maxLon+scale, scale):
    55. row.append([round(posLon, ndigit), round(posLat, ndigit)])
    56. matrix.append(row)
    57. return matrix
    58. @jit(nopython=True)
    59. def PNPoly(vertices, testp):
    60. """
    61. 返回一个点是否在一个多边形区域内(开区间) PNPoly算法
    62. @param vertices: 多边形的顶点
    63. @param testp: 测试点[x, y]
    64. """
    65. n = len(vertices)
    66. j = n - 1
    67. res = False
    68. for i in range(n):
    69. if (vertices[i][1] > testp[1]) != (vertices[j][1] > testp[1]) and \
    70. testp[0] < (vertices[j][0] - vertices[i][0]) * (testp[1] - vertices[i][1]) / (
    71. vertices[j][1] - vertices[i][1]) + vertices[i][0]:
    72. res = not res
    73. j = i
    74. return res
    75. def isPolygonContainsPoint(pt_list, p):
    76. """
    77. 返回一个点是否在一个多边形区域内(开区间).
    78. """
    79. nCross = 0
    80. for i in range(len(pt_list)):
    81. p1 = pt_list[i]
    82. p2 = pt_list[(i + 1) % len(pt_list)]
    83. if p1[1] == p2[1]:
    84. continue
    85. if p[1] < min(p1[1], p2[1]) or p[1] >= max(p1[1], p2[1]):
    86. continue
    87. x = p1[0] + (p2[0] - p1[0]) * (p[1] - p1[1]) / (p2[1] - p1[1])
    88. if x > p[0]:
    89. nCross += 1
    90. return nCross % 2 == 1
    91. def compute_mark_matrix(polygon, regionalSlices):
    92. """
    93. 根据切分矩形和多边形计算出标记矩阵.
    94. """
    95. m = len(regionalSlices)
    96. n = len(regionalSlices[0])
    97. rectangleMarks = [[1] * (n - 1) for _ in range(m - 1)]
    98. def inRange(num, min, max):
    99. return num >= min and num <= max
    100. for posM in range(m):
    101. print(f'mark {posM}')
    102. for posN in range(n):
    103. p = regionalSlices[posM][posN]
    104. if not PNPoly(polygon, p):
    105. if inRange(posM - 1, 0, m - 2) and inRange(posN - 1, 0, n - 2):
    106. rectangleMarks[posM - 1][posN - 1] = 0
    107. if inRange(posM - 1, 0, m - 2) and inRange(posN, 0, n - 2):
    108. rectangleMarks[posM - 1][posN] = 0
    109. if inRange(posM, 0, m - 2) and inRange(posN - 1, 0, n - 2):
    110. rectangleMarks[posM][posN - 1] = 0
    111. if inRange(posM, 0, m - 2) and inRange(posN, 0, n - 2):
    112. rectangleMarks[posM][posN] = 0
    113. return rectangleMarks
    114. def maximal_rectangle(matrix):
    115. """
    116. 根据标记矩阵求最大矩形,返回【最小行标 最大行标 最小列标 最大列标 最大面积】
    117. """
    118. m = len(matrix)
    119. n = len(matrix[0])
    120. left = [[0] * n for _ in range(m)]
    121. for i in range(m):
    122. for j in range(n):
    123. if matrix[i][j] == 1:
    124. left[i][j] = (left[i][j-1] if j else 0) + 1
    125. min_c, max_c, min_r, max_r, ret = -1, -1, -1, -1, 0
    126. # 对于每一列,使用基于柱状图的方法
    127. for j in range(n):
    128. up = [0] * m
    129. down = [0] * m
    130. que = deque()
    131. for i in range(m):
    132. while len(que) > 0 and left[que[-1]][j] >= left[i][j]:
    133. que.pop()
    134. up[i] = que[-1] if que else -1
    135. que.append(i)
    136. que.clear()
    137. for i in range(m-1, -1, -1):
    138. while que and left[que[-1]][j] >= left[i][j]:
    139. que.pop()
    140. down[i] = que[-1] if que else m
    141. que.append(i)
    142. for i in range(m):
    143. height = down[i] - up[i] - 1
    144. area = height * left[i][j]
    145. if area > ret:
    146. ret = area
    147. min_c = up[i] + 1
    148. max_c = down[i] - 1
    149. min_r = j - left[i][j] + 1
    150. max_r = j
    151. return min_c, max_c, min_r, max_r, ret
    152. def largest_internal_rectangle(polygon):
    153. """
    154. 求一个多边形区域的水平方向最大内接矩形,由于是经纬度数据,精确到小数点后两位,误差(只小不大)约一公里.
    155. """
    156. scale = 0.01
    157. # 1.区域切块,不是真的切成矩形,而是切分成数据点
    158. min_enclosing_rect = min_enclosing_rectangle(polygon)
    159. # 2.标记矩阵,这里将点阵经纬度转换为矩形标记矩阵,每个矩形以左上角作为标的,
    160. # 比如矩形marks[0][0]的左上角坐标为regionalSlices[0][0],右下角坐标为regionalSlices[1][1]
    161. regional_slices = compute_regional_slices(min_enclosing_rect, scale)
    162. marks = compute_mark_matrix(polygon, regional_slices)
    163. # 3.计算最大内接矩阵,返回矩形
    164. min_c, max_c, min_r, max_r, area = maximal_rectangle(marks)
    165. minLon = regional_slices[0][min_r][0]
    166. maxLon = regional_slices[0][max_r+1][0]
    167. minLat = regional_slices[max_c+1][0][1]
    168. maxLat = regional_slices[min_c][0][1]
    169. return Rectangle(minLon, maxLon, minLat, maxLat)
    170. def largest_internal_rectangle_recursion(polygon, minArea=64):
    171. scale = 0.01
    172. rect_list = []
    173. # 1.区域切块,不是真的切成矩形,而是切分成数据点
    174. min_enclosing_rect = min_enclosing_rectangle(polygon)
    175. # 2.标记矩阵
    176. regional_slices = compute_regional_slices(min_enclosing_rect, scale)
    177. marks = compute_mark_matrix(polygon, regional_slices)
    178. # 3. 把最大矩形的mark置零,剩余部分重新计算出最大矩形,直到面积小于minArea
    179. while True:
    180. min_c, max_c, min_r, max_r, area = maximal_rectangle(marks)
    181. if area < minArea:
    182. break
    183. minLon = regional_slices[0][min_r][0]
    184. maxLon = regional_slices[0][max_r+1][0]
    185. minLat = regional_slices[max_c+1][0][1]
    186. maxLat = regional_slices[min_c][0][1]
    187. rect = Rectangle(minLon, maxLon, minLat, maxLat)
    188. for i in range(min_c, max_c+1):
    189. for j in range(min_r, max_r+1):
    190. marks[i][j] = 0
    191. rect_list.append(rect)
    192. return rect_list
    193. def plot_rect(df, rect_list):
    194. # plot edge
    195. plt.scatter(df['lng'], df['lat'], s=5, alpha=0.8)
    196. for rect in rect_list:
    197. points = np.array([[rect.minLon, rect.minLat], [rect.minLon, rect.maxLat],
    198. [rect.maxLon, rect.maxLat], [rect.maxLon,rect.minLat],
    199. [rect.minLon, rect.minLat]
    200. ])
    201. plt.plot(*zip(*points), color='r')
    202. plt.show()
    203. if __name__ == '__main__':
    204. df = pd.read_csv('D:/data/edge_points.csv')
    205. point_list = df[['lng', 'lat']].values
    206. ret_rect = largest_internal_rectangle_recursion(point_list, 64)
    207. print('rect size {}'.format(len(ret_rect)))
    208. plot_rect(df, ret_rect)

  • 相关阅读:
    1997-2020年各省三废排放量和熵值法计算的环境规制综合指数(无缺失值)
    C# 代码合集
    我在Vscode学OpenCV 色彩空间转换
    SpringCloud - Spring Cloud Alibaba 之 Gateway 集成Sentinel (十一)
    【C++】一篇了解AVL实现细节(易懂图解版)
    软考 ----- UML设计与分析(下)
    深入理解.Net中的线程同步之构造模式(二)内核模式1.内核模式构造物Event事件
    10.27 知识总结(前端)
    flex加 grid 布局笔记
    开发运维-常用远程桌面开源软件
  • 原文地址:https://blog.csdn.net/rover2002/article/details/132909178