python:从TIN中选出特定点三维坐标

  • 1.需求说明
  • 2.原理说明
  • 2.1 构建TIN
  • 2.2 判断特征点所属的三角形
  • 2.3 通过插值生成特征点坐标
  • 2.4 精度评价
  • 代码实现
  • 示例


1.需求说明

本文描述如何从三维地形点云数据中构建TIN并通过构建的TIN求任一点的插值三维坐标

2.原理说明

2.1 构建TIN

构建TIN原理不过多解释,详情可参考:
PYthon TIN生成

2.2 判断特征点所属的三角形

此处使用的特征点为随机从原始点云均匀分块并随机选择的数据,数据格式为点号、横坐标、纵坐标、高程。此处用以生成TIN的点云数据为原始点云数据经过简化后所得。

思路为通过寻找距离特征点最近的点云A,然后通过A查找以A为顶点的三角形,然后判断特征点在哪个三角形内

Python实现十个点落入三个面中 python输入三个点的坐标_特征点

如图所示原理

2.3 通过插值生成特征点坐标

TIN为线性插值,因此只需判断三角形三个顶点,即可求得三角形面内任意一点的坐标。(根据三点成面原理)
公式推导较为复杂,不理解的可查看这个:
三点成面

2.4 精度评价

检查点法使用中误差(RMSE)作为精度评价指标,从整体上评估DEM高程与真值之间的离散程度。具体定义如下:

Python实现十个点落入三个面中 python输入三个点的坐标_特征点_02

其中,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进行调用。

示例

Python实现十个点落入三个面中 python输入三个点的坐标_3d_03


随机展示一个特征点云数据和相邻原始点云关系图,未连线处未特征点。

由于简化后点云数据暂存一定问题,因此特征点云并不在TIN三角网上,因此RMSE偏大,但整体方法可行。