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);
//获取后无需显式释放OGRLayer OGRLayer* 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字符串
}
}

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

  1. Linux资源控制-CPU和内存

    主要介绍Linux下, 如果对进程的CPU和内存资源的使用情况进行控制的方法. CPU资源控制 每个进程能够占用CPU多长时间, 什么时候能够占用CPU是和系统的调度密切相关的. Linux系统中有多 ...

  2. Linux资源控制-CPU和内存【转】

    转自:http://www.cnblogs.com/wang_yb/p/3942208.html 主要介绍Linux下, 如果对进程的CPU和内存资源的使用情况进行控制的方法. CPU资源控制 每个进 ...

  3. C#+AE实现类似IDentify功能及对高亮显示相关接口的总结

    kenika 原文C#+AE实现类似IDentify功能及对高亮显示相关接口的总结 ArcMap中的Identify功能是有目的查看要素(Feature)属性信息经常使用的工具.ArcMap中的Ide ...

  4. C#开发微信门户及应用(42)--使用Autofac实现微信接口处理的控制反转处理

    在很多情况下,我们利用IOC控制反转可以很方便实现一些接口的适配处理,可以在需要的时候切换不同的接口实现,使用这种方式在调用的时候,只需要知道相应的接口接口,具体调用哪个实现类,可以在配置文件中动态指 ...

  5. Spring中Aware相关接口原理

    Spring中提供一些Aware相关接口,像是BeanFactoryAware. ApplicationContextAware.ResourceLoaderAware.ServletContextA ...

  6. Servlet相关接口和Servlet的生命周期

    http://www.cnblogs.com/luotaoyeah/p/3860292.html Servlet相关接口和Servlet的生命周期 创建一个Servlet类最直接的方式是实现javax ...

  7. 【Spring源码分析系列】ApplicationContext 相关接口架构分析

    [原创文章,转载请注明出处][本文地址]http://www.cnblogs.com/zffenger/p/5813470.html 在使用Spring的时候,我们经常需要先得到一个Applicati ...

  8. Identity4实现服务端+api资源控制+客户端请求

    准备写一些关于Identity4相关的东西,最近也比较对这方面感兴趣.所有做个开篇笔记记录一下,以便督促自己下一个技术方案方向 已经写好的入门级别Identity4的服务+api资源访问控制和简单的客 ...

  9. 5.cgroup资源控制

    控制组( CGroups)是 Linux 内核的一个特性,主要用来对共享资源进行隔离.限制.审计等. 只有将分配到容器的资源进行控制,才能避免多个容器同时运行时对宿主机系统的资源竞争.每个控制组是一组 ...

  10. Linux systemd资源控制初探

    Linux systemd资源控制初探 本文记录一次cgroup子目录丢失问题,并简单探索了Linux systemd的资源控制机制. 问题现象 我们希望通过systemd拉起服务并通过cgroup限 ...

随机推荐

  1. 拯救php性能的神器webman-初入门

    无意间发现的这个神器webman,真是秋名山上的腾源拓海! 该框架是workerman下的一个web开发的生态,我们可以先看看这里workerman的官方网站. workerman早有耳闻,知道它蛮厉 ...

  2. 设计模式(17)-Chain of Responsibility Pattern

    行为模式(Behavioral Pattern)是对在不同的对象之间划分责任和算法的抽象化.行为模式不仅仅是关于类和对象的,而且是关于它们之间的相互作用的. 行为模式分为类的行为模式和对象的行为模式两 ...

  3. golang之异步队列Asynq

    Asynq[1]是一个Go实现的分布式任务队列和异步处理库,基于redis,类似Ruby的sidekiq[2]和Python的celery[3].Go生态类似的还有machinery[4]和gowor ...

  4. 从解决Github TimeOut到经典面试题:从输入URL到浏览器显示页面发生了什么?

    问题描述 在Windows 操作系统上,push代码到git的时候,出现了Failed to connect to github.com port 443: Timed out的错误.一脸懵逼,浏览器 ...

  5. uniapp h5 和 小程序互相传值

    小程序端 <template> <div> <web-view :webview-styles="webviewStyles" :src=" ...

  6. iOS Aliyun语音识别&语音合成

    Aliyun 语音识别&语音合成 导入 SDK 将ZIP包中的nuisdk.framework添加到工程中,并在工程Build Phases的Link Binary With Librarie ...

  7. MYSQL数据库设计操作规范 ❤️【建议收藏】

    1.背景及其意义 MySQL数据库与 Oracle. SQL Server 等数据库相比,有其内核上的优势与劣势.我们在使用MySQL数据库的时候需要遵循一定规范,扬长避短.本文档旨在帮助或指导数据中 ...

  8. 【Vue】学习笔记:Vue组件

    文末有我看的这个视频的链接. 目录 组件注册 全局注册 组件基础 组件命名规则 template选项 单项数据流 data选项 局部注册 单独配置组件的选项对象 ES6对象属性简写 组件通信 父组件向 ...

  9. Mockito入门:如何在Spring中Mock部分对象

    前情提要 随着分布式应用的开发逐渐成为标配,多个微服务团队合作来完成垂直业务的开发成为了一种常态.微服务使得团队可以专注于自己的业务逻辑,在和下游依赖和上游对接的团队聚焦好接口之后,就进入正式的开发. ...

  10. Go channel 原理

    作用 Go 语言的 channel 是一种 goroutine 之间的通信方式,它可以用来传递数据,也可以用来同步 goroutine 的执行. chan 是 goroutine 之间的通信桥梁,可以 ...