GDAL矢量数据集相关接口的资源控制问题

news/2025/2/4 22:00:23/文章来源:https://www.cnblogs.com/charlee44/p/18698533

1. 引言

笔者在《使用GDAL读写矢量文件》这篇文章中总结了通过GDAL读写矢量的具体实现。不过这篇文章中并没有谈到涉及到矢量数据集相关接口的资源控制问题。具体来说,GDAL/OGR诞生的年代连C++语言本身都不是很完善(c++11之前),因此提供的C++接口往往存在申请的资源需要释放的问题,因此在这里将其总结一下。

2. 详论

2.1 数据集类GDALDataset

矢量数据集GDALDataset对象需要通过GDALOpenEx来读取或者更新。在不需要这个对象之后,使用GDALClose进行关闭。例如:

GDALDataset *poDS = (GDALDataset*)GDALOpenEx(filePath, GDAL_OF_VECTOR, NULL, NULL, NULL);//...GDALClose(poDS);
poDS = nullptr;

另一方面,通过驱动类GDALDriver创建矢量数据集,不需要之后仍然使用GDALClose进行关闭。例如:

GDALDriver* driver = GetGDALDriverManager()->GetDriverByName("ESRI Shapefile");
if (!driver)
{printf("Get Driver ESRI Shapefile Error!\n");return false;
}GDALDataset* dataset = driver->Create(filePath, 0, 0, 0, GDT_Unknown, NULL);GDALClose(dataset);
dataset = nullptr;

理论上来说,GDALDataset对象在打开或者创建之后,使用delete进行释放也是可以的。但是一般而言,最好使用GDAL本身提供出来的释放接口。因为这个接口的内部实现可能并不只是delete那么简单,可能有其他的资源释放操作。不仅仅是GDAL,其他类库也是同理。

2.2 图层类OGRLayer

GDALDataset既可以是矢量数据集,也可以是栅格数据集。但是只有矢量数据集才能获取或创建图层类OGRLayer。但是无论是获取还是创建OGRLayer,再无需使用之后,都不用再进行主动释放了,OGRLayer对象会被GDALDataset对象托管,在GDALClose释放数据集对象之后,图层类OGRLayer就会随之释放。

OGRLayer* poLayer = poDS->GetLayer(0);
//获取后无需显式释放OGRLayerOGRLayer* poLayer = dataset->CreateLayer("houseType", NULL, wkbPolygon, NULL);
//创建后无需显式释放OGRLayer

2.3 要素类OGRFeature

要素类OGRFeature一般从图层类OGRLayer对象中获取或者创建,不过无论是获取还是创建都需要进行显式释放。例如读取矢量数据集时遍历获取要素:

OGRFeature *poFeature;
while ((poFeature = poLayer->GetNextFeature()) != NULL)
{OGRGeometry *pGeo = poFeature->GetGeometryRef();//...OGRFeature::DestroyFeature(poFeature);
}

这里的OGRFeature::DestroyFeature(poFeature);就是GDAL提供的用于销毁要素对象的方法。另一方面,如果是写出数据集创建要素,比如笔者这里创建一个经纬度网格的矢量:

for (int yi = -90; yi < 90; ++yi) {for (int xi = -180; xi < 180; ++xi) {    OGRFeature poFeature(poLayer->GetLayerDefn());OGRLinearRing ogrRing;ogrRing.addPoint(xi, yi);ogrRing.addPoint(xi + 1, yi);ogrRing.addPoint(xi + 1, yi + 1);ogrRing.addPoint(xi, yi + 1);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;}}}

OGRFeature使用的是值对象,在超出作用域之后会自动销毁。经过验证笔者这样写并没有问题,可以推断OGRLayer对于OGRFeature对象的管理应该是采用的深拷贝方式,并且会托管这个拷贝后的OGRFeature对象。

2.4 几何类OGRGeometry

几何类OGRGeometry使用了C++类的继承和多态特性,本身其是一个基类,但是继承出了如OGRLinearRing、OGRPolygon等子类来表达点线面多种要素几何类型。因此GDAL提供了一个工厂类来创建和销毁,这是一种非常经典的设计模式:

OGRGeometry* poGeom = OGRGeometryFactory::createGeometry(wkbPoint);
// 使用完 poGeom 后释放它
OGRGeometryFactory::destroyGeometry(poGeom);

也就是OGRGeometryFactory::createGeometry和OGRGeometryFactory::destroyGeometry需要成对出现。不过笔者认为如果不是为了多态表达,直接使用值对象更加方便,如第2.3节中的示例所示。

另外,OGRGeometry对象是需要放置到OGRFeature对象中的,因此OGRFeature提供了两个接口:

  1. OGRErr SetGeometryDirectly(OGRGeometry*):浅拷贝OGRGeometry对象,OGRFeature对象直接托管OGRGeometry对象的所有权。
  2. OGRErr SetGeometry(const OGRGeometry*):深拷贝OGRGeometry对象,OGRFeature对象托管OGRGeometry拷贝对象的所有权。

另外,几何类之间的相互引用也是如此,如第2.3节中的示例所示,多边形增加环也有两个接口:

  1. addRingDirectly() 浅拷贝OGRLinearRing对象,OGRPolygon对象直接托管OGRLinearRing对象的所有权。
  2. addRing()深拷贝OGRLinearRing对象,OGRPolygon对象托管OGRLinearRing拷贝对象的所有权。

也就是一般而言,GDAL通常使用Directly后缀的函数接口来表达对原几何对象的托管。

3. 其他

可以看到,GDAL的资源控制方面还是有点混乱的,有的要显式释放,有的又可以托管,有的干脆提供了两个接口。据说新的GDAL版本引入了很多新的C++特性,估计资源控制的逻辑要清晰一点。另外,我们也可以主动使用一些新的C++特性来避免资源控制需要主动释放的问题。例如使用智能指针,配合自定义删除器来销毁OGRFeature对象,如下例所示:

// 获取第一个图层
OGRLayer* poLayer = poDS->GetLayer(0);
if (poLayer == nullptr) {std::cerr << "Failed to get the layer." << std::endl;GDALClose(poDS);return -1;
}// 自定义删除器用于销毁 OGRFeature
auto featureDeleter = [](OGRFeature* poFeature) {OGRFeature::DestroyFeature(poFeature);
};// 遍历图层中的要素
poLayer->ResetReading();
std::unique_ptr<OGRFeature, decltype(featureDeleter)> poFeature(nullptr, featureDeleter);while ((poFeature.reset(poLayer->GetNextFeature())), poFeature) {// 获取几何体OGRGeometry* poGeometry = poFeature->GetGeometryRef();if (poGeometry != nullptr) {// 输出几何体的WKT表示char* pszWKT = nullptr;poGeometry->exportToWkt(&pszWKT);std::cout << "Geometry: " << pszWKT << std::endl;CPLFree(pszWKT);  // 释放WKT字符串}
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【vscode配置】一种基于Beta版UTF-8解决VSCode中C/C++/Python等的中文乱码问题的简便方法

在VSCode或其他编码场景下输入输出中文时会出现乱码,常用的解决方式是通过GBK编码重新打开或保存文件后再使用 但又是来回切换保存会导致代码中的中文变成乱码难以恢复 而且个人觉得来回切换很麻烦 下面给出一种了解到的解决方法:控制面板——时钟和区域——区域——管理——…

Graphviz-画数据结构图的利器

1.Win系统上的安装 安装Graphviz的方法有很多,以下是手动安装的方式: 1.访问 https://graphviz.org/download/,选择你想要下载的版本,手动安装选择ZIP安装包;2.解压下载的zip压缩包到你选择的文件目录,例如:D:\Graphviz-12.2.1-win64; 3.将Graphviz的安装目录(如:D:\…

4G优先级切换

本文来自博客园,作者:{IceSparks},转载请注明原文链接:https://www.cnblogs.com/IceSparks/p/18698508

4G与5GSIB对比

本文来自博客园,作者:{IceSparks},转载请注明原文链接:https://www.cnblogs.com/IceSparks/p/18698503

AI赋能软件测试:未来已来,你准备好了吗?

ps:文末有福利领取哦引言 在数字化转型的浪潮中,软件测试作为保障产品质量的关键环节,正面临着前所未有的挑战。 传统的测试方法已难以满足快速迭代和复杂场景的需求,而人工智能(AI)的引入,则为软件测试带来了革命性的变化。 本文将从现状、优势、挑战及未来趋势四个方面…

C语言 输出回文数

回文数,既把数值的每个数 颠倒输出(从右往左输出),再合起来的数如果和原来的值相等 例:输出0 至 1993 的回文数#include <stdio.h>int main() { int i ,x ,d ,a ; for(i=0;i<=1993;i++) {a = i; x = 0; while (a != 0) { d = a%10; //每次都取a值的最后一位 x = …

洛谷 P3842 [TJOI2007] 线段 题解

题目大意 原题链接 给你一个大小为 \(n \times n\) 的矩阵,上面有 \(n\) 条线段(一行一条)。你只能向右,向下,向左走。每一条线段都必须完整的经过。请问从 \((1,1)\) 到 \((n,n)\) 最短路径长度是多少? 解法 这道题可以使用最短路或者DP来做。我觉得DP本题更简单一点所以我…

数据降维技术研究:Karhunen-Love展开与快速傅里叶变换的理论基础及应用

在现代科学计算和数据分析领域,数据降维与压缩技术对于处理高维数据具有重要意义。本文主要探讨两种基础而重要的数学工具:Karhunen-Love展开(KLE)和快速傅里叶变换(FFT)。通过分析这两种方法的理论基础和应用特点,阐述它们在数据降维中的优势和适用场景。 Karhunen-Lov…

一个Python GC的小Bug

这个代码是在看了“码农高天”的视频照模仿的。虽然 GC 能完成大部分内存管理的工作,但是基于性能计数器的GC确实存在小 Bug,这一点还是需要通过del和gc.collect手动管理。 from typing import Optionalclass Object:def __init__(self, name: str) -> None:self.obj: Opt…

2月做题记录

还有一个月。2月做题记录✩ trick ✯ 会大部分,要\(tj\)提示 ✬ 会小部分/完全没想到,看了\(tj\)才会 ◈ 脑电波 ✡ 有某一算法的神秘通用性质 ⊗ 待补目录2月做题记录字符串CF1827CCF1913FP10716P11150 ✩✯树[NOI2021] 轻重边做法1 ✩做法2做法3 ✡《简单树剖练习题》[QOJ9…

第六节 特征提取和无监督学习

没有标签y依旧提取出特征你的模型朝着哪个方向压缩特征,由你的标签来定自监督学习:从x中提取一部分作为自己的标签y,然后用x预测y 在自监督学习中,需要根据数据的特点设计一些自监督任务,这些任务通常基于数据的一些内在属性或关系来构建。例如,在图像数据中,可以设计图像…