我正在寻找一种平滑多边形的方法,以便相邻/接触的多边形保持接触。单个多边形可以轻松平滑,例如使用 PAEK 或 Bezier 插值 ( https://pro.arcgis.com/en/pro-app/latest/tool-reference/cartography/smooth-polygon.htm ),这自然会改变它们的边界边缘。但是如何平滑所有多边形以使接触的多边形保持这种状态?
我正在寻找理想的 Python 解决方案,以便它可以轻松实现自动化。我发现了 Arcgis 的一个等效问题( https://gis.stackexchange.com/questions/183718/how-to-smooth-adjacent-polygons ),其中最上面的答案概述了一个很好的策略(转换多边形)从多边形交汇点到交汇点的边到线),平滑这些然后重建多边形)。也许这是最好的策略,但我不确定如何在 Python 中将共享多边形边界转换为单独的折线。
这里有一些示例代码,显示了我正在尝试对 2 个多边形执行的操作(但我'已经手动创建了“平滑”多边形):
import matplotlib.pyplot as plt
import geopandas as gpd
from shapely import geometry
x_min, x_max, y_min, y_max = 0, 20, 0, 20
## Create original (coarse) polygons:
staircase_points = [[(ii, ii), (ii, ii + 1)] for ii in range(x_max)]
staircase_points_flat = [coord for double_coord in staircase_points for coord in double_coord] + [(x_max, y_max)]
list_points = {1: staircase_points_flat + [(x_max, y_min)],
2: staircase_points_flat[1:-1] + [(x_min, y_max)]}
pols_coarse = {}
for ind_pol in [1, 2]:
list_points[ind_pol] = [geometry.Point(x) for x in list_points[ind_pol]]
pols_coarse[ind_pol] = geometry.Polygon(list_points[ind_pol])
df_pols_coarse = gpd.GeoDataFrame({'geometry': pols_coarse.values(), 'id': pols_coarse.keys()})
## Create smooth polygons (manually):
pols_smooth = {1: geometry.Polygon([geometry.Point(x) for x in [(x_min, y_min), (x_max, y_min), (x_max, y_max)]]),
2: geometry.Polygon([geometry.Point(x) for x in [(x_min, y_min), (x_min, y_max), (x_max, y_max)]])}
df_pols_smooth = gpd.GeoDataFrame({'geometry': pols_smooth.values(), 'id': pols_smooth.keys()})
## Plot
fig, ax = plt.subplots(1, 2, figsize=(10, 4))
df_pols_coarse.plot(column='id', ax=ax[0])
df_pols_smooth.plot(column='id', ax=ax[1])
ax[0].set_title('Original polygons')
ax[1].set_title('Smoothed polygons');
更新: 使用下面山的建议和 这篇文章 ,我认为问题可以分解为以下步骤:|| |找到每对接触多边形之间的边界边缘(例如,使用
- 此建议 )。 将它们转换为 numpy 数组并按照 Mountain 的 bspline 建议进行平滑
- 使用更新/平滑的边缘重建多边形。
- 另请注意,对于单个 (
) 多边形,可以使用以下方法对其进行平滑:
shapely.geometry
使用 Douglas-Peucker 算法。
pol.simplify()
using Douglas-Peucker algorithm.
import geopandas as gpd
from shapely.geometry import Polygon, LineString
from shapely.ops import polygonize, unary_union
import numpy as np
from scipy.interpolate import splprep, splev
def smooth_shared_edges(gdf, tolerance=0.01, s=0.0):
"""
平滑 GeoDataFrame 中相邻多边形的共享边。
Args:
gdf (GeoDataFrame): 包含多边形的 GeoDataFrame。
tolerance (float, optional): 用于 Douglas-Peucker 算法简化线段的容差。
s (float, optional): 用于样条插值的平滑度参数。
Returns:
GeoDataFrame: 包含平滑多边形的 GeoDataFrame。
"""
# 创建所有多边形边的列表
edges = []
for pol in gdf.geometry:
for i in range(len(pol.exterior.coords) - 1):
edges.append(LineString([pol.exterior.coords[i], pol.exterior.coords[i+1]]))
# 查找共享边(接触两次或多次的边)
shared_edges = []
for i, edge1 in enumerate(edges):
for j, edge2 in enumerate(edges):
if i != j and edge1.equals(edge2):
shared_edges.append(edge1)
break
# 平滑共享边
smoothed_edges = []
for edge in shared_edges:
# 将 LineString 转换为 numpy 数组
coords = np.array(edge.coords)
# 应用 B 样条平滑
tck, u = splprep(coords.T, u=None, s=s)
u_new = np.linspace(u.min(), u.max(), 100)
new_coords = splev(u_new, tck)
# 创建平滑的 LineString
smoothed_edge = LineString(new_coords.T)
smoothed_edges.append(smoothed_edge)
# 使用平滑的边和原始的非共享边创建新的多边形
all_edges = smoothed_edges + [edge for edge in edges if edge not in shared_edges]
new_polygons = list(polygonize(unary_union(all_edges)))
# 创建一个新的 GeoDataFrame
gdf_smoothed = gpd.GeoDataFrame({'geometry': new_polygons}, crs=gdf.crs)
return gdf_smoothed
# --- 使用的示例数据测试功能 ---
x_min, x_max, y_min, y_max = 0, 20, 0, 20
## 创建原始(粗略)多边形:
staircase_points = [[(ii, ii), (ii, ii + 1)] for ii in range(x_max)]
staircase_points_flat = [coord for double_coord in staircase_points for coord in double_coord] + [(x_max, y_max)]
list_points = {1: staircase_points_flat + [(x_max, y_min)],
2: staircase_points_flat[1:-1] + [(x_min, y_max)]}
pols_coarse = {}
for ind_pol in [1, 2]:
list_points[ind_pol] = [geometry.Point(x) for x in list_points[ind_pol]]
pols_coarse[ind_pol] = geometry.Polygon(list_points[ind_pol])
df_pols_coarse = gpd.GeoDataFrame({'geometry': pols_coarse.values(), 'id': pols_coarse.keys()})
# 平滑多边形
df_pols_smooth = smooth_shared_edges(df_pols_coarse)
# 绘制结果
fig, ax = plt.subplots(1, 2, figsize=(10, 4))
df_pols_coarse.plot(column='id', ax=ax[0])
df_pols_smooth.plot(column='id', ax=ax[1])
ax[0].set_title('原始多边形')
ax[1].set_title('平滑多边形');
plt.show()
此代码首先找到所有共享边,然后仅平滑这些边。然后,它使用平滑的共享边和原始的非共享边重建多边形。这可以确保相邻多边形在平滑后保持接触。
可以调整
tolerance
和
s
参数以控制平滑度。
tolerance
参数控制用于简化线段的 Douglas-Peucker 算法。
s
参数控制样条插值的平滑度。