如何通过质心、裁剪或其他方式筛选Shapely多边形?
筛选生命之花中的完整圆形区域
问题分析
你通过buffer生成圆形,经unary_union和polygonize拆分出所有重叠区域,且已去除细碎多边形。现在需要保留原点(0,0)的中心圆和第一层环绕的6个花瓣圆,这类区域的核心特征是:未被其他圆切割的完整圆形,面积与原始生成圆一致,质心与原始圆心几乎重合。
可行解决方案
1. 面积筛选法(最直接)
原始圆半径为2,理论面积为π*2²≈12.566。经过polygonize后的完整圆形区域面积会非常接近该值,而重叠区域面积必然更小。通过设置误差容忍度筛选:
# 原始圆的理论面积 target_area = np.pi * 2**2 # 面积误差容忍度 area_tolerance = 0.1 # 筛选面积接近目标值的多边形 final_filtered = [poly for poly in filtered_polys if abs(poly.area - target_area) < area_tolerance]
2. 质心+距离筛选法
若面积筛选存在误差,可结合质心位置判断:完整圆的质心与原始生成该圆的点几乎重合。先定义目标圆心列表,再计算多边形质心与这些点的距离,设置小阈值筛选:
# 定义需要保留的原始圆心(中心+第一层) target_centers = [ Point(0, 0), Point(2, 0), Point(-2, 0), Point(1, h), Point(-1, h), Point(1, -h), Point(-1, -h) ] # 距离误差容忍度 distance_tolerance = 0.01 final_filtered = [] for poly in filtered_polys: poly_centroid = poly.centroid # 检查是否与任意目标圆心接近 for center in target_centers: if poly_centroid.distance(center) < distance_tolerance: final_filtered.append(poly) break
修改后的完整代码
将上述筛选逻辑替换原代码中filtered_polys之后的部分即可:
import matplotlib.pyplot as plt from shapely.geometry import Point, LineString from shapely.ops import unary_union, polygonize from matplotlib.pyplot import cm import numpy as np def plot_coords(coords, color): pts = list(coords) x, y = zip(*pts) plt.plot(x,y, color='k', linewidth=1) plt.fill_between(x, y, facecolor=color) def plot_polys(polys, color): for poly, color in zip(polys, color): plot_coords(poly.exterior.coords, color) x = 0 y = 0 h = 1.73205080757 points = [# center Point(x, y), # first ring Point((x + 2), y), Point((x - 2), y), Point((x + 1), (y + h)), Point((x - 1), (y + h)), Point((x + 1), (y - h)), Point((x - 1), (y - h)), # second ring Point((x + 3), h), Point((x - 3), h), Point((x + 3), -h), Point((x - 3), -h), Point((x + 2), (h + h)), Point((x - 2), (h + h)), Point((x + 2), (-h + -h)), Point((x - 2), (-h + -h)), Point((x + 4), y), Point((x - 4), y), Point(x, (h + h)), Point(x, (-h + -h)), #third ring Point((x + 4), (h + h)), Point((x - 4), (h + h)), Point((x + 4), (-h + -h)), Point((x - 4), (-h + -h)), Point((x + 1), (h + h + h)), Point((x - 1), (h + h + h)), Point((x + 1), (-h + -h + -h)), Point((x - 1), (-h + -h + -h)), Point((x + 5), h), Point((x - 5), h), Point((x + 5), -h), Point((x - 5), -h)] # buffer points to create circle polygons circles = [] for point in points: circles.append(point.buffer(2)) # unary_union and polygonize to find overlaps rings = [LineString(list(pol.exterior.coords)) for pol in circles] union = unary_union(rings) result_polys = [geom for geom in polygonize(union)] # remove tiny sliver polygons threshold = 0.01 filtered_polys = [polygon for polygon in result_polys if polygon.area > threshold] # --- 新增筛选逻辑:保留完整圆形 --- # 方法1:面积筛选(默认启用) target_area = np.pi * 2**2 area_tolerance = 0.1 final_filtered = [poly for poly in filtered_polys if abs(poly.area - target_area) < area_tolerance] # 或者用方法2:质心距离筛选(取消注释即可切换) # target_centers = [ # Point(0, 0), # Point(2, 0), Point(-2, 0), # Point(1, h), Point(-1, h), # Point(1, -h), Point(-1, -h) # ] # distance_tolerance = 0.01 # final_filtered = [] # for poly in filtered_polys: # poly_centroid = poly.centroid # for center in target_centers: # if poly_centroid.distance(center) < distance_tolerance: # final_filtered.append(poly) # break # --- 筛选结束 --- print("total polygons = " + str(len(result_polys))) print("filtered polygons = " + str(len(filtered_polys))) print("final selected polygons = " + str(len(final_filtered))) colors = cm.viridis(np.linspace(0, 1, len(final_filtered))) fig = plt.figure() ax = fig.add_subplot() fig.subplots_adjust(top=0.85) plot_polys(final_filtered, colors) ax.set_aspect('equal') plt.show()
补充说明
- 你之前尝试质心筛选未成功,大概率是未限定目标圆心范围或阈值设置不合理。上述质心方法明确指定了匹配的原始圆心,配合小阈值即可准确筛选。
snap()或clip_by_rect()更适合几何对齐或区域裁剪,对于当前筛选完整圆形的需求,面积+质心的组合方法更直接高效。
内容的提问来源于stack exchange,提问作者Doda
相关产品推荐
相关产品推荐

