Python 对多边形的边进行均匀采样
化边为点
在做实验时遇到了如何对多边形的边离散采样为点的问题。根据不同情况,对应找到了两种解决办法。
引言
- 第一种方法,适用情况:有多边形的线性矢量文件(dwg、shp),精度要求不严格。优点:无需编程,全程可视化,可对整个文件里的所有线性特征进行快速采样。
- 第二种方法,适用情况:在编程中遇到的多边形对象(即按顺序排列的点坐标),精度要求高,要求保留端点。 优点:自动处理。
方法一:CouldCompare
矢量的多边形文件(如DLG),如果原文件为dwg格式,可以先转成shp格式,在CouldCompare中进行处理。
CouldCompare(CC)是一款开源的三维点云处理软件,点云入门必备,而且还新出了python包(哎哟不错哦~),不过新手还是建议安装独立版。下载链接:http://www.danielgm.net/cc/release/
打开shp文件,选中矢量边,菜单栏Edit—Clone,将选中的边复制一遍,在左边DB Tree中最下面找到复制出的多边形,选中,菜单栏Edit—Polyline—Sample points,在弹出的对话框中可以选择按数量或密度进行采样。
点击OK进行采样,采样完成后选中采样出的点云,菜单栏Save图标保存为点云文件,对点云文件格式不了解的铁汁可以在保存类型中选择ASCII cloud,接下来还会有一些具体的保存设置,比如精度、分割符、属性顺序等等,默认保存,则保存的txt文件或csv等文件的前三列即为点的 (x,y,z) 三维坐标。
CC操作简单,当有对应的文件时可以直接用CC进行离散化。但是数据传输比较麻烦,最关键的!!!放大后可以看到它的点不一定在原来的线上,矢量线段的端点也不会被保留,精度要求高的话这种方法是不适用的。
方法二:Python
按距离d对每条边进行均匀采样,肯定会遇到边长S不能被d整除的问题,于是就对S/d进行了一个round舍入取整的处理,所以结果中每条边相邻两点的距离会有差别,但每条边的采样距离都接近d,在我的实际应用中,这样做是没毛病的。那么,上代码。
# File :LineProcess.py
# Author :WooChi
# Time :2021/10/27
# Function :对多边形的边进行均匀采样
# Version :
import time
import numpy as np
import matplotlib.pyplot as plt
from icecream import ic # pip install icecream 或注释相关代码(可替换 ic( 为 print(
def Line_GeneralEquation(line=[1, 1, 1, 2]):
"""直线一般式"""
A = line[3] - line[1]
B = line[0] - line[2]
C = line[2] * line[1] - line[0] * line[3]
line = np.array([A, B, C])
if B != 0:
line = line / B
return line
def SamplePointsOnLineSegment(point1, point2, distence):
"""在一条线段上均匀采样"""
line_dist = np.sqrt((point2[0] - point1[0]) ** 2 + (point2[1] - point1[1]) ** 2) # 线段长度
num = round(line_dist / distence) # 采样段数量
line = [point1[0], point1[1], point2[0], point2[1]] # 两点成线
line_ABC = Line_GeneralEquation(line) # 一般式规范化
newP = []
newP.append(point1) # 压入首端点
if num > 0:
dxy = line_dist / num # 实际采样距离
ic(dxy)
for i in range(1, num):
if line_ABC[1] != 0:
alpha = np.arctan(-line_ABC[0])
dx = dxy * np.cos(alpha)
dy = dxy * np.sin(alpha)
if point2[0] - point1[0] > 0:
newP.append([point1[0] + i * dx, point1[1] + i * dy])
else:
newP.append([point1[0] - i * dx, point1[1] - i * dy])
else:
if point2[1] - point1[1] > 0:
newP.append([point1[0], point1[1] + i * dxy])
else:
newP.append([point1[0], point1[1] - i * dxy])
newP.append([point2[0], point2[1]]) # 压入末端点
return np.array(newP)
def multiLine2Points(lineXY, distence):
'''将所给点连线并首尾连接,构成多边形,对每条边进行均匀采样'''
lineXY = np.array(lineXY)
newPoints = []
# 对所有线段进行处理
for i in range(len(lineXY) - 1):
newP = SamplePointsOnLineSegment(lineXY[i, :], lineXY[i + 1, :], distence)
newPoints.extend(newP)
# 连接首尾两点,再进行均匀采样
newP = SamplePointsOnLineSegment(lineXY[-1, :], lineXY[0, :], distence)
newPoints.extend(newP)
newPoints = np.array(newPoints)
# 删除重复端点
delInd = []
for i in range(len(newPoints) - 1):
if (newPoints[i, :] == newPoints[i + 1, :]).all():
delInd.append(i)
newPoints = np.delete(newPoints, delInd, axis=0)
plt.plot(newPoints[:, 0], newPoints[:, 1], color='blue', marker='o')
plt.pause(3)
return newPoints
def run():
'''运行,测试,计时'''
t_start = time.time()
point = np.array([[3984.44316009, 1726.24349976],
[4027.90084192, 1614.54548784],
[4089.2089216, 1612.69075405],
[4153.14220036, 1705.633401],
[4132.41845704, 1813.63680594],
[4124.74202474, 1830.90348307],
[4053.04286087, 1799.99257074]])
plt.plot(point[:, 0], point[:, 1], color='blue', marker='o')
plt.pause(3)
newPoints = multiLine2Points(point, 10)
ic(newPoints)
ic(len(newPoints))
# <editor-fold desc="计时并输出">
t_consume = time.time() - t_start
h = t_consume // 3600
m = (t_consume - h * 3600) // 60
s = t_consume - h * 3600 - m * 60
print('---------------------------------------------------')
print('Time consuming: %d hours %d minutes %.3f seconds' % (h, m, s))
print('---------------------------------------------------')
# </editor-fold>
if __name__ == '__main__':
run()
各个边实际采样距离如下:
ic| dxy: 9.987845383027489
ic| dxy: 10.222688099789762
ic| dxy: 10.255335138467348
ic| dxy: 9.997606744949913
ic| dxy: 9.448091764586126
ic| dxy: 9.75981445194561
ic| dxy: 10.072161842185897
输入的多边形顶点连线可视化:
多边形及采样点可视化:
小注
双11光棍节,不想干活,闲着也是闲着,于是对之前遇到的一个小问题进行了一个小小的记录以及分享。最近格局打开,不想光做一个索取者,也想试着做一个分享者。第一次在CSDN写博客,挺简单的一个东西还费了不少劲,离谱。不过给自己记录了,就不亏,但凡能帮到一个人,算我赚。
最后,欢迎交流讨论~