本文描述如何从三维地形点云数据中构建TIN并通过构建的TIN求任一点的插值三维坐标
构建TIN原理不过多解释,详情可参考:
PYthon TIN生成
此处使用的特征点为随机从原始点云均匀分块并随机选择的数据,数据格式为点号、横坐标、纵坐标、高程。此处用以生成TIN的点云数据为原始点云数据经过简化后所得。
思路为通过寻找距离特征点最近的点云A,然后通过A查找以A为顶点的三角形,然后判断特征点在哪个三角形内
如图所示原理
TIN为线性插值,因此只需判断三角形三个顶点,即可求得三角形面内任意一点的坐标。(根据三点成面原理)
公式推导较为复杂,不理解的可查看这个:
三点成面
检查点法使用中误差(RMSE)作为精度评价指标,从整体上评估DEM高程与真值之间的离散程度。具体定义如下:
其中,Ei为点Pi从简化后LiDAR点构建的TIN中内插得到的高程值,Hi为点Pi在原始LiDAR地面点云数据中的高程值,均以m为单位,N为选择的检查点个数。
import os
import math
from program import CreateTIN as ct
import datetime
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
# 判断检查点是否与原始点存在同坐标情况
def checkdata(checkpoint_list, initialdata_list):
checkName_list = [] # 保存检查点的点名标签
initialName_list = [] # 保存原始点的点名标签
for checkpoint in checkpoint_list:
for initialdata in initialdata_list:
if checkpoint.X == initialdata.X and checkpoint.Y == initialdata.Y:
checkName_list.append(checkpoint.PointName)
initialName_list.append(initialdata.PointName)
if checkName_list != [] and initialName_list != []:
return checkName_list, initialName_list
else:
print("没找到同坐标点")
def getNearistPoint(Point,initialdata_list,Net):
# 判断点处于哪个三角形中,并返回三个角点
line_list = Net[0] # 边列表
Triangle_list = Net[1] # 三角形列表
nearpoint = initialdata_list[ct.NearistPoint(Point, initialdata_list)] ## 在原始数据中返回距离最近的点
nearpoint_list = [] # 用以存储与最近点有关系的点
nearline_list = [] # 用以储存最近点相关边
slope_list = [] # 储存斜率
for line in line_list:
if line.BeginPoint == nearpoint:
slope = slopee(line.BeginPoint.X, line.BeginPoint.Y, line.EndPoint.X, line.EndPoint.Y)
slope_list.append(slope) # 添加斜率
nearline_list.append(line) # 添加最近点相关边
Point_aothr = line.EndPoint
nearpoint_list.append(Point_aothr) # 添加与最近点有关系的点
elif line.EndPoint == nearpoint:
slope = slopee(line.BeginPoint.X, line.BeginPoint.Y, line.EndPoint.X, line.EndPoint.Y)
slope_list.append(slope) # 添加斜率
nearline_list.append(line) # 添加最近点相关边
Point_aothr = line.BeginPoint
nearpoint_list.append(Point_aothr) # 添加与最近点有关系的点
slope_label = list(np.argsort(slope_list)) # 返回斜率递减下标
slope_label.append(slope_label[0]) # 把开头扩充到结尾
nearline_list.append(nearline_list[0]) # 把列表扩充一个开头
slope = slopee(nearpoint.X, nearpoint.Y, Point.X, Point.Y) # 目标斜率
# 判断目标斜率在哪两个斜率之间
index = 0
for i in range(len(slope_label) - 1):
if slope_list[slope_label[i]] <= slope <= slope_list[slope_label[i + 1]]:
index = i
# nearline_list[slope_label[index]] # 一条边
# nearline_list[slope_label[index+1]] # 另一条边
if (nearline_list[slope_label[index]].BeginPoint ==nearline_list[slope_label[index+1]].BeginPoint or nearline_list[slope_label[index]].BeginPoint ==nearline_list[slope_label[index+1]].EndPoint):
Point_1 = nearline_list[slope_label[index]].EndPoint
Point_2 = nearline_list[slope_label[index+1]].BeginPoint
Point_3 = nearline_list[slope_label[index+1]].EndPoint
elif (nearline_list[slope_label[index]].EndPoint == nearline_list[slope_label[index+1]].BeginPoint or nearline_list[slope_label[index]].EndPoint ==nearline_list[slope_label[index+1]].EndPoint):
Point_1 = nearline_list[slope_label[index]].BeginPoint
Point_2 = nearline_list[slope_label[index+1]].BeginPoint
Point_3 = nearline_list[slope_label[index+1]].EndPoint
return Point_1,Point_2,Point_3
def getHigh(point1,point2,point3,point4):
# 依据三点成面,判断一个已知X,Y坐标的点的H坐标,此公式没错
# A*x+B*y+C*z+D=0 ,求解A,B,C
M = ((point3.H-point1.H)*(point2.X-point1.X)-(point2.H-point1.H)*(point3.X-point1.X))
N = ((point3.Y-point1.Y)*(point2.X-point1.X)-(point2.Y-point1.Y)*(point3.X-point1.X))
D = ((-point1.H*(point2.X-point1.X)+(point2.H-point1.H)*point1.X)*N+(point1.Y*(point2.X-point1.X)-(point2.Y-point1.Y)*point1.X)*M)
A = -(point2.H-point1.H)*N+M*(point2.Y-point1.Y)
B = -(point2.X-point1.X)*M
C = (point2.X-point1.X)*N
if C!=0:
hight = (-D -A*point4.X-B*point4.Y)/C
else:
hight = 0
return hight
def draw(point_list,line_list):
#ax.scatter(xs, ys, zs, s=20, c=None, depthshade=True, *args, *kwargs)
# np.random.rand(n)产生1*n数组,元素大小0-1
fig = plt.figure() # 打开画图窗口,在三维空间中绘图
ax = fig.add_subplot(111, projection='3d')
for point in point_list:
ax.scatter(point.X, point.Y, point.H, c='c', marker= 'o')
for line in line_list:
x = [line.BeginPoint.X,line.EndPoint.X]
y = [line.BeginPoint.Y,line.EndPoint.Y]
z = [line.BeginPoint.H,line.EndPoint.H]
ax.plot(x,y,z,c='r')
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()
def slopee(x1,y1,x2,y2): # 求斜率
x = (y2 - y1) / (x2 - x1)
return x
if __name__ == '__main__':
start = datetime.datetime.now() # 起始时间
print('起始时间:', start)
# '''*******************调试********************'''
# 读取数据
initialdata_path = r'\\'
checkpoint_path = r"\\"
initialdata_list = ct.ReadDataTXT(initialdata_path)
checkpoint_list = ct.ReadDataTXT(checkpoint_path)
checkdata(checkpoint_list, initialdata_list)
Net = ct.CreatTIN(initialdata_list) # 生成三角网
# 求RMSE
MSE = 0
for checkpoint in checkpoint_list:
point_list = getNearistPoint(checkpoint, initialdata_list, Net) # 返回三个点坐标
hight = getHigh(point_list[0],point_list[1],point_list[2],checkpoint)# 通过三个坐标返回高程值
MSE = MSE + (hight - checkpoint.H)**2
RMSE = math.sqrt(MSE / len(checkpoint_list))
print(RMSE)# 求RMSE
end = datetime.datetime.now() # 结束时间
print('总用时:', end - start)
此处调用了PYthon TIN生成所蕴含的代码,并将文件保存为CreateTIN.py进行调用。
随机展示一个特征点云数据和相邻原始点云关系图,未连线处未特征点。
由于简化后点云数据暂存一定问题,因此特征点云并不在TIN三角网上,因此RMSE偏大,但整体方法可行。