点集合的三角剖分

news2025/2/27 5:33:08

点集合的三角剖分是指如何将一些离散的点集合组合成不均匀的三角形网格,使得每个点成为三角网中三角面的顶点。这个算法的用处很多,一个典型的意义在于可以通过一堆离散点构建的TIN实现对整个构网区域的线性控制,比如用带高程的离散点构建的TIN来表达地形。

在实际工作中,使用最多的三角剖分是Delaunay三角剖分。通过Delaunay三角剖分算法能够构建一个具有空圆特性和最大化最小角特性的三角网。空圆特性其实就是对于两个共边的三角形,任意一个三角形的外接圆中都不能包含有另一个三角形的顶点,这种形式的剖分产生的最小角最大。这些特性可能有些难以理解,但是我们可以先谨记一点:Delaunay三角网是一种特性最优的三角剖分。

通过CGAL,我们可以直接通过离散点集生成Delaunay三角网,实现代码如下:

#include <CGAL/Delaunay_triangulation_2.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Projection_traits_xy_3.h>

typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Projection_traits_xy_3<K> Gt;
typedef CGAL::Delaunay_triangulation_2<Gt> Delaunay;
typedef K::Point_3 Point;

#include <ogrsf_frmts.h>

#include <iostream>
#include <string>

using namespace std;

bool ReadVector(vector<Point> &vertexList) {
  string srcFile = "Data/Vector/points.shp";

  GDALDataset *poDS = (GDALDataset *)GDALOpenEx(srcFile.c_str(), GDAL_OF_VECTOR,
                                                NULL, NULL, NULL);
  if (!poDS) {
    printf("无法读取该文件,请检查数据是否存在问题!");
    return false;
  }

  if (poDS->GetLayerCount() < 1) {
    printf("该文件的层数小于1,请检查数据是否存在问题!");
    return false;
  }

  for (int li = 0; li < poDS->GetLayerCount(); li++) {
    OGRLayer *poLayer = poDS->GetLayer(li);  //读取层
    poLayer->ResetReading();

    //遍历特征
    OGRFeature *poFeature = nullptr;
    while ((poFeature = poLayer->GetNextFeature()) != nullptr) {
      OGRGeometry *geometry = poFeature->GetGeometryRef();
      OGRwkbGeometryType geometryType = geometry->getGeometryType();

      switch (geometryType) {
        case wkbPoint:
        case wkbPointM:
        case wkbPointZM: {
          OGRPoint *ogrPoint = dynamic_cast<OGRPoint *>(geometry);
          if (ogrPoint) {
            vertexList.emplace_back(ogrPoint->getX(), ogrPoint->getY(), 0);
          }
          break;
        }
        case wkbMultiPoint:
        case wkbMultiPointM:
        case wkbMultiPointZM: {
          OGRMultiPoint *ogrMultiPoint =
              dynamic_cast<OGRMultiPoint *>(geometry);
          if (!ogrMultiPoint) {
            continue;
          }

          for (int gi = 0; gi < ogrMultiPoint->getNumGeometries(); gi++) {
            OGRPoint *ogrPoint =
                dynamic_cast<OGRPoint *>(ogrMultiPoint->getGeometryRef(gi));
            if (ogrPoint) {
              vertexList.emplace_back(ogrPoint->getX(), ogrPoint->getY(), 0);
            }
          }

          break;
        }
        default: {
          printf("未处理的特征类型\n");
          break;
        }
      }

      OGRFeature::DestroyFeature(poFeature);
    }
  }

  GDALClose(poDS);
  poDS = nullptr;

  return true;
}

bool WriteVector(const Delaunay &dt) {
  string dstFile = "Data/Out.shp";

  GDALDriver *driver =
      GetGDALDriverManager()->GetDriverByName("ESRI Shapefile");
  if (!driver) {
    printf("Get Driver ESRI Shapefile Error!\n");
    return false;
  }

  GDALDataset *dataset =
      driver->Create(dstFile.c_str(), 0, 0, 0, GDT_Unknown, NULL);
  OGRLayer *poLayer = dataset->CreateLayer("tin", NULL, wkbPolygon, NULL);

  //创建面要素
  for (const auto &f : dt.finite_face_handles()) {
    OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());

    OGRLinearRing ogrring;
    for (int i = 0; i < 3; i++) {
      ogrring.setPoint(i, f->vertex(i)->point().x(), f->vertex(i)->point().y());
    }
    ogrring.closeRings();

    OGRPolygon polygon;
    polygon.addRing(&ogrring);
    poFeature->SetGeometry(&polygon);

    if (poLayer->CreateFeature(poFeature) != OGRERR_NONE) {
      printf("Failed to create feature in shapefile.\n");
      return false;
    }
  }

  //释放
  GDALClose(dataset);
  dataset = nullptr;

  return true;
}

int main() {
  GDALAllRegister();
  CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");  //支持中文路径
  CPLSetConfigOption("SHAPE_ENCODING", "");           //解决中文乱码问题

  vector<Point> vertexList;
  ReadVector(vertexList);

  Delaunay dt(vertexList.begin(), vertexList.end());

  WriteVector(dt);
  return 0;
}

这里我们先从一个矢量中读取了离散点集,在QGIS中显示如下图4.21所示:

三角构网前的离散点集

在程序最后,将生成的Delaunay三角网输出成另外一个矢量文件,在QGIS中显示如下图4.22所示:

三角构网后的TIN

读取和写出比较好理解,关键是调用CGAL进行构建Delaunay三角网,其实相当简短:

typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Projection_traits_xy_3<K> Gt;
typedef CGAL::Delaunay_triangulation_2<Gt> Delaunay;
typedef K::Point_3 Point;

int main() {
{
  //...
  vector<Point> vertexList;
  //...
  Delaunay dt(vertexList.begin(), vertexList.end());
  //...
}

CGAL大量应用了C++的模板(泛型)技术,因而使用的接口比较抽象可能难以理解,这里可以解释一下CGAL的设计逻辑。学过任何一门编程语言的都知道,浮点型数值的相等判断不能直接使用相等运算符;正确的做法是使用两者相减的绝对值与容差进行判断,因为计算机表达的浮点型是个近似值。计算几何的核心问题正在于此,内置数据类型的精度是有限的,处理容差是非常麻烦的事情。所以数值需要更为精确的表达,比如0.5就应该就是0.5,不能是0.49999999。因此CGAL确定了一个Kernel(核)的概念,通过模板来控制不同精度。

这里的typedef CGAL::Exact_predicates_inexact_constructions_kernel K;表示精确谓词,但不精确构造的内核。predicates(谓词)表示一个操作;(constructions)构造意味着会有新的数值对象作为结果,如果算法是一个不进行构造的算法中,就可以使用精确谓词但不精确构造的内核。比如这里的构建Delaunay三角网,并没有新的点对象生成出来,只是对点集进行了组织,点还是原来哪些点,并没有变化。

另外,typedef K::Point_3 Point;表示我们使用该精度下的内置三维点类型。但是另外一个问题在于,如果我们需要定义三个维度中的哪两个维度数值参与构网计算,或者使用自定义数据结构该怎么办呢?所以可以传入Traits类型,这其实是C++的模板中的traits技术,描述了传入数据的数值特性:比如类型,排序,方向测试或者相等判断等。每个Kernel中都有定义好的Traits类型,这里使用的就是typedef CGAL::Projection_traits_xy_3<K> Gt;,使用点的xy值参与构网计算。最后将该类型作为模板参数传入到Delaunay三角网构建类中:typedef CGAL::Delaunay_triangulation_2<Gt> Delaunay;

上述的解析读者如果没有一定的C++模板知识的基础,肯定看的云里雾里。其实不要紧,笔者也只是希望大家能够理解CGAL如此设计接口的内在逻辑,并不是故意设计的如此抽象和繁琐,而是希望最大程度的保证精度和性能。更多更具体的解析,读者可以参看CGAL文档。对C++模板知识不熟悉的初学者,建议直接参考文档中的给出的实例,在实际使用过程中逐渐增加自己的认识。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1128185.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Windows网络监视工具

对于任何规模的企业来说&#xff0c;网络管理在信息技术中都起着至关重要的作用。管理、监控和密切关注网络基础设施对任何组织都至关重要。在Windows网络中&#xff0c;桌面&#xff0c;服务器&#xff0c;虚拟服务器和虚拟机&#xff08;如Hyper-V&#xff09;在Windows操作系…

医院电力系统智能能效监控平台的应用

0引言 随着社会和科学技术的发展&#xff0c;配电系统的智能化已经成为一种发展趋势。医院建设电力智能监控平台&#xff0c;可对供电系统进行集中管理和调度、实时控制和数据采集&#xff0c;监控供电系统设备的运行情况&#xff0c;及时掌握和处理供电系统的各种事故、报警事…

Day07 Stream流递归Map集合Collections可变参数

Stream 也叫Stream流&#xff0c;是Jdk8开始新增的一套API (java.util.stream.*)&#xff0c;可以用于操作集合或者数组的数据。 Stream流大量的结合了Lambda的语法风格来编程&#xff0c;提供了一种更加强大&#xff0c;更加简单的方式操作 public class Demo1 {public stati…

【机器学习合集】标准化与池化合集 ->(个人学习记录笔记)

文章目录 标准化与池化1. 标准化/归一化1.1 归一化归一化的作用 1.2 标准化批标准化方法 Batch Normailzation标准化方法的对比自动学习标准化方法 2. 池化2.1 池化的作用2.2 常见的池化方法2.3 池化方法的差异2.4 池化的必要性 标准化与池化 1. 标准化/归一化 1.1 归一化 归…

django建站过程(3)定义模型与管理页

定义模型与管理页 定义模型[models.py]迁移模型向管理注册模型[admin.py]注册模型使用Admin.site.register(模型名)修改Django后台管理的名称定义管理列表页面应用名称修改管理列表添加查询功能 django shell交互式shell会话 认证和授权 定义模型[models.py] 模仿博客形式&…

kali查看wifi破解密码,实测有效

首先需要安装kali系统 这个系统是安装在虚拟机上的 还需要一个无线网卡(最好是kali系统免驱的 否则是无法识别的) 有着两个工具就可以pojie密码了 kali官网:Kali Linux | Penetration Testing and Ethical Hacking Linux Distribution 下载这里大家去比站上或者博客都可以…

某雀服务器崩溃,引发数据安全性讨论,应该选择私有化部署吗?

随着云计算技术的飞速发展&#xff0c;越来越多的企业和个人选择将数据存储于云端。然而&#xff0c;云服务的稳定性和数据安全性问题也成为了用户关注的焦点。昨天下午&#xff0c;语雀服务器崩溃事件引起了广泛关注。这一事件再次凸显了私有化的重要性。又一批人群开始考虑将…

12 结构型模式-桥接模式

1 桥接模式介绍 2 桥接模式原理

跟人一样,手机太烫也会“生病”!如何给太烫的手机降温

高温是你手机最大的敌人。现代智能手机在纤薄的外壳中装有强大的处理器和大容量电池&#xff0c;即使在正常工作条件下&#xff08;看看你&#xff0c;Galaxy Note 7&#xff0c;也许还有iPhone 15&#xff09;&#xff0c;过热也会成为一个真正的问题。无论是充电、闲置还是执…

【每天学习一点新知识】安全设备IDS、IRS、IPS

IDS&#xff1a;入侵检测系统 对那些异常的、可能是入侵行为的数据进行检测和报警&#xff0c;告知使用者网络中的实时状况&#xff0c;并提供相应的解决、处理方法&#xff1b;是一种侧重于风险管理的安全产品。 IRS&#xff1a;入侵响应系统 深入网络数据内部&#xff0c;查…

项目结束需要经历的5个关键步骤

项目结束是项目管理不可或缺的一部分。这是项目的最后阶段&#xff0c;根据关键绩效指标和范围对交付成果进行测试&#xff0c;收尾&#xff0c;总结经验教训&#xff0c;完成交接&#xff0c;并签署项目。 项目结束与启动会议和一样重要。管理人员应为此留出时间&#xff0c;…

【剑指Offer】:循环有序列表的插入(涉及链表的知识)

给定循环单调非递减列表中的一个点&#xff0c;写一个函数向这个列表中插入一个新元素 insertVal &#xff0c;使这个列表仍然是循环升序的 给定的可以是这个列表中任意一个顶点的指针&#xff0c;并不一定是这个列表中最小元素的指针 如果有多个满足条件的插入位置&#xff0c…

从VTI7064与W25Qxx了解SPI通信协议

在学习过程中记录。 学习背景 最近在做的项目需要设计电路包含外扩FLASH&#xff08;W25Q128&#xff09;与SRAM(VTI7064)&#xff0c;二者都用到了SPI通信协议&#xff0c;之前没学过&#xff0c;学习记录一下。 顺便说一下这次学习中发现的好用工具WPS AI。可以对文档进行…

Leetcode周赛368补题(3 / 3)

目录 1、元素和最小的山型三元组 | - 三层for暴力循环 2、元素和最小的山型三元组 || - 维护前后最小值 遍历 3、合法分组的最少组数 - 思维 哈希表 1、元素和最小的山型三元组 | - 三层for暴力循环 100106. 元素和最小的山形三元组 I class Solution {public int minimu…

Linux流量监控

yum install -y iptrafiptraf-ng -d ens33

centos7磁盘动态扩容

1.查看磁盘空间 df -h 2.fdisk -l 查看当前的磁盘分区信息(主要是分区表信息) linux新增磁盘后&#xff0c;用fdisk等命令查询不到 #ls /sys/class/scsi_host/ &#xff08;会看到有host0、host1…hostN&#xff0c;对每个host进行如下操作&#xff09; echo “- - -” …

IP证书针对公网IP签发

很多项目应用需要采用IP地址数据桥接访问&#xff0c;这种情况下需要确保数据安全性及信任不被劫持的情况下&#xff0c;需要使用给IP地址增加数字证书进行保护。针对这种情况下我们对公网IP地址申请SSL证书做了详细的介绍&#xff0c;让我们可以更快地了解如何用IP地址去申请S…

sknearl-7处理文本数据

本章代码大部分没跑&#xff0c;只供学习 第四节特征工程里提到&#xff0c;有连续特征和离散特征&#xff0c;对于文本数据&#xff0c;文本特征可以看作第三种特征 1 用字符串表示的数据类型 2 例子 电影评论情感分析 给定一个影评&#xff08;输入&#xff09;&#xff…

Spring Boot配置 application.yml,根据application.yml选择启动配置

在Spring Boot 中可以选择applicant.properties 作为配置文件&#xff0c;也可以通过在application.yml中进行配置&#xff0c;让Spring Boot根据你的选择进行加载启动配置文件。 这种配置方式&#xff0c;我们通常在实际开发中经常使用&#xff0c;主要为了发布版本和以及开发…

ATFX汇市:美元指数跌破关键支撑,黄金触及2000关口后回落

ATFX汇市&#xff1a;昨日&#xff0c;在没有重磅数据公布的情况下&#xff0c;美元指数大跌0.52%&#xff0c;最低触及105.51&#xff0c;令市场人士感到意外。美元指数与美债价格呈反向波动关系&#xff1a;美联储加息&#xff0c;美债价格下跌&#xff08;收益率提高&#x…