首页 > 编程问答 >如何在Python中平滑相邻的多边形?

如何在Python中平滑相邻的多边形?

时间:2024-07-31 06:40:32浏览次数:14  
标签:python gis polygon geopandas shapely

我正在寻找一种平滑多边形的方法,以便相邻/接触的多边形保持接触。单个多边形可以轻松平滑,例如使用 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');

Expected outcome smoothed touching 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 参数控制样条插值的平滑度。

标签:python,gis,polygon,geopandas,shapely
From: 75534590

相关文章

  • Python多处理池不启动多个进程
    我正在尝试使用多处理池来创建多个进程。我有一个工作函数dummy_proc定义如下:importrefrommultiprocessingimportPooldefregex_check(input_string):#Patterntomatchboth"pm_lat"and"pm_lon_coslat"followedbytwofloatspattern=r"(c......
  • 迟滞建模作为 Python GEKKO 中 MPC 的控制约束
    我试图使用PythonGEKKO在用于控制信号调度的MPC优化问题中引入滞后约束。这已成为一项艰巨的任务,因为我无法将以下问题转换为GEKKO理解的方程。问题:如果开启时间<最短开启时间,则给定资产的控制调度不应将其关闭。如果关闭时间<最小关闭时间......
  • 在 Lambda Python 中获取 errorMessage": "期望值: 第 1 行第 1 列 (char 0)"
    我正在尝试使用slackapi和awslambda函数创建一个slack机器人。现在我只希望每当用户说“你好”时它就响应“你好”。当我在Lambda代码编辑器中测试代码时,出现此错误。我对Lambda很陌生,并且已经被困在这个问题上有一段时间了。非常感谢任何帮助!完整错误:Response......
  • 具有 Python lambda 函数的 QTimer 使用先前的数据运行
    我有一个GUI项目,它使用PySide2和Python3.8,它在QThread中执行一些后台任务。在该QThread中,我有QTimer成员对象,该对象必须定期运行一个函数,每次向其传递不同的数据。我没有使用QTimer.singleShot静态函数,因为如果需要某些特定场景,我需要......
  • 我如何在 python 上使用 spire.pdf 修复此错误
    我使用spirepdf,但出现以下错误:“DllNotFound_Windows,libSkiaSharp,Nosepuedeencontrarelmóduloespecificado.:....”defextract_text_from_pdf(file_path,output_file):#LoadaPDFdocumentdoc=PdfDocument()doc.LoadFromFile(file_path)ex......
  • C++ 函数返回极其缓慢,远慢于功能等效的 python 代码
    我有一个在我编写的脚本中使用的函数,用于从列表中删除多余的阻塞关键字。基本上,输入(以任何顺序):{"apple","bapple","banana","cherry","bananaman","sweetherrypie","sweet","b"}它应该输出一个缩小的字符串数组(以任何顺序):......
  • 在预定时间从 python telegram bot 发起对话
    对于没有提供代码,我提前表示歉意。我明天会尝试添加它,但我现在还没有接近它,思考如何解决这个问题让我一直在思考。我已经为一个机器人创建了一个程序,该程序的数据帧充满了之前请求的用户添加到机器人的消息列表中。现在,机器人使用job_queue在一天中的随机时间向每个用户发......
  • python函数:带星号的参数一个接一个
    我想知道为什么定义以下函数被认为是好的。deffoo(*x,y):pass据我所知,现在有调用此函数的方法,因为它总是缺少y的值。(如果我错了,请纠正我。)这有什么我不知道的用处吗?你说得对,在Python中定义一个像deffoo(*x,y):...这样的函数,在试图以常规方......
  • 基于遗传(GA)、粒子群(PSO)、模拟退火(SA)、禁忌搜索(ST)、蚁群算法(ACO)、自自组织神
        ......
  • 10个append()函数在Python程序开发中的创新应用
    文末赠免费精品编程资料~~在Python编程的世界里,append()函数是列表操作中最常见的方法之一。它允许我们在列表的末尾添加一个元素,这一简单的功能却能激发无限的创造力。今天,我们将探讨append()函数在Python程序开发中的10种创新应用,从基本用法到高级技巧,逐步深入。1.构......