python 如何从多边形数据中提取边作为连接特征?

k0pti3hp  于 2023-03-28  发布在  Python
关注(0)|答案(2)|浏览(160)

我有一个多边形数据结构及其提取的边缘,但使用extract_feature_edges函数计算为未连接的单元(分离的线)。
有没有可能从它们的共同点连接这些单元(线),然后得到不同的特征(土地,岛屿,如你在图像中看到的-南极洲,澳大利亚,... -顺便说一句,它们是古大陆)?
在简历中,我想从我的网格和它的边缘提取不同的土地部分作为单独的多边形数据。我已经尝试了与python模块shapely和多边形函数,它的工作,但不与三维坐标(https://shapely.readthedocs.io/en/latest/reference/shapely.polygonize.html)。

import pyvista as pv

! wget -q -nc https://thredds-su.ipsl.fr/thredds/fileServer/ipsl_thredds/brocksce/pyvista/mesh.vtk
mesh = pv.PolyData('mesh.vtk')
edges = mesh.extract_feature_edges(boundary_edges=True)

pl = pv.Plotter()

pl.add_mesh(pv.Sphere(radius=0.999, theta_resolution=360, phi_resolution=180))
pl.add_mesh(mesh, show_edges=True, edge_color="gray")
pl.add_mesh(edges, color="red", line_width=2)

viewer = pl.show(jupyter_backend='pythreejs', return_viewer=True)
display(viewer)

你知道吗?

brvekthn

brvekthn1#

下面是使用vtk.vtkStripper()将连续线段连接成折线的解决方案。

import pyvista as pv
import vtk
import random

! wget -q -nc https://thredds-su.ipsl.fr/thredds/fileServer/ipsl_thredds/brocksce/pyvista/mesh.vtk
mesh = pv.PolyData('mesh.vtk')
edges = mesh.extract_feature_edges(boundary_edges=True)

pl = pv.Plotter()

pl.add_mesh(pv.Sphere(radius=0.999, theta_resolution=360, phi_resolution=180))
pl.add_mesh(mesh, show_edges=True, edge_color="gray")

regions = edges.connectivity()
regCount = len(set(regions.get_array("RegionId")))

connectivityFilter = vtk.vtkPolyDataConnectivityFilter()
stripper = vtk.vtkStripper()

for r in range(regCount):
    connectivityFilter.SetInputData(edges)
    connectivityFilter.SetExtractionModeToSpecifiedRegions()
    connectivityFilter.InitializeSpecifiedRegionList()
    connectivityFilter.AddSpecifiedRegion(r)
    connectivityFilter.Update()

    if r == 11:
        b = pv.PolyData(a)
        b.save('poly1.vtk')
    
    stripper.SetInputData(connectivityFilter.GetOutput())
    stripper.SetJoinContiguousSegments(True)
    stripper.Update()
    reg = stripper.GetOutput()
    
    random_color = "#"+''.join([random.choice('0123456789ABCDEF') for i in range(6)])
    pl.add_mesh(reg, color=random_color, line_width=4)

viewer = pl.show(jupyter_backend='pythreejs', return_viewer=True)
display(viewer)
lb3vh1jj

lb3vh1jj2#

这个问题在in github discussions之前就已经出现了,结论是PyVista没有内置的任何东西来重新排序边,但是可能有第三方库可以做到这一点(这个答案提到了libigl,但是我没有这方面的经验)。
我有一些关于如何解决这个问题的想法,但是在一般情况下,这样一个助手的适用性存在问题。然而,在您的特定情况下,我们知道每个边都是一个闭环,并且没有太多的边,所以我们不必担心性能(特别是内存占用)。
这里有一个手动的方法来重新排序边,通过构建一个邻接图和遍历,直到我们在每个循环开始的地方结束:

from collections import defaultdict

import pyvista as pv

# load example mesh
mesh = pv.read('mesh.vtk')

# get edges
edges = mesh.extract_feature_edges(boundary_edges=True)

# build undirected adjacency graph from edges (2-length lines)
# (potential performance improvement: use connectivity to only do this for each closed loop)
# (potentially via calling edges.split_bodies())
lines = edges.lines.reshape(-1, 3)[:, 1:]
adjacency = defaultdict(set)  # {2: {1, 3}, ...} if there are lines from point 2 to point 1 and 3
for first, second in lines:
    adjacency[first].add(second)
    adjacency[second].add(first)

# start looping from whichever point, keep going until we run out of adjacent points
points_left = set(range(edges.n_points))
loops = []
while points_left:
    point = points_left.pop()  # starting point for next loop
    loop = [point]
    loops.append(loop)
    while True:
        # keep walking the loop
        neighb = adjacency[point].pop()
        loop.append(neighb)
        if neighb == loop[0]:
            # this loop is done
            break
        # make sure we never backtrack
        adjacency[neighb].remove(point)
        # bookkeeping
        points_left.discard(neighb)
        point = neighb

# assemble new lines based on the existing ones, flatten
lines = sum(([len(loop)] + loop for loop in loops), [])

# overwrite the lines in the original edges; optionally we could create a copy here
edges.lines = lines

# edges are long, closed loops by construction, so it's probably correct
# plot each curve with an individual colour just to be safe
plotter = pv.Plotter()
plotter.add_mesh(pv.Sphere(radius=0.999))
plotter.add_mesh(edges, scalars=range(edges.n_cells), line_width=3, show_scalar_bar=False)
plotter.enable_anti_aliasing('msaa')
plotter.show()

这段代码用14个更大的行来定义每个循环,替换了原来的1760个2-length行。不过,你必须小心一点:在澳大利亚北部有一个自相交的环路

交点出现了4次而不是2次。这意味着我的暴力解算器没有给予一个定义良好的结果:它将在交叉点随机选择,如果运气不好,我们从交叉点开始循环,算法可能会失败。让它更鲁棒是留给读者的练习(我关于将边缘分割成单个边缘的评论可以帮助解决这个问题)。

相关问题