OGR库读写mif/tab文件lonefox使用经验小结

 ===================================

本人log中所有未注明转载的文章和blog一般为本人原创或整理加工,
原创文章版权本人(lonefox)所有;转载文章版权归原作者所有;

http://blog.csdn.net/boythl

欢迎转载,但请注明出处,保留作者和版权信息。

===================================

  

最近项目中使用GDAL/OGR库读写mif和tab文件, 经过几天的折腾,积累了点点经验,且记之.

 

1. OGRSFDriverRegistrar::Open(Path, FALSE);只能读文件

对于OGRSFDriverRegistrar::Open方法, ogr内置的mif/TAB读写库MITAB是不支持该方法的更新的,看下面的MITAB源码:

  再深入源码,你会发现,通过Open方法传给IMapInfoFile类访问文件的方式是TABRead,而非TABWrite

  也许你会说"这是OGRTABDriver的方法, 可以看看OGRMIFDriver的方法?", 遗憾, 我们只能用 RegisterOGRTAB(); 注册mif和tab通用的驱动,RegisterOGRMIF();是一个只有定义而没有函数实现的美丽谎言, 不知道OGR库留着这个定义的目的何在。也不存在OGRMIFDriver,OGRTABDriver通吃mif和tab文件。这也是我们的第二点。

 

2.只使用RegisterOGRTAB();注册tab/MIF文件读写驱动

原因上面已经提过。单凭RegisterOGRTAB();已足够。

mif和tab文件的读取都是通过Open方法,MITAB库内部会调用SmartOpen方法区分是mif/mid后缀或是tab后缀。

 

3.写mif/tab文件的方法

OGRSFDriver *pDriver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName("MapInfo File");
OGRDataSource *pDSTAB = pDriver->CreateDataSource(strTabPath);//创建一个文件,根据扩展名自动判断是创建mif文件或tab文件

lonefox的解释: MITAB库在实现CreateDataSource方法时会调用OGRTABDataSource的Create方法,而非Open方法

Create方法内部会调用IMapInfoFile类的Open方法,但区别在于访问模式参数传递的是"wb"!

而该模式将标记 m_eAccessMode = TABWrite; 不是OGRDataSource::Open方法对应的TABRead模式;

顺带提句,IMapInfoFile内只有TABWrite和TABRead, 是不能又读又写的

 

4.读写mif/tab的方法

如果我一个文件已有部分数据,但我想打开后继续加数据,怎么办?

我采取一种曲线救国的方法解决该问题: i. 打开该文件; ii.按上面第三条的方法新建一个临时文件供写入; iii. 使用OGRDataSource类的CopyLayer方法将 i 中打开的旧数据贴进新layer,iv. 调用OGRLayer::SyncToDisk方法保存新文件;v. 删除旧文件将临时文件更名

若有大侠有更好的方法,一定要不吝指教才好!!!

 

5. mif/tab格式互转

利用OGRDataSource::CopyLayer,mif和tab格式的互转就再简单不过了。直接源码吧

tab转mif和上面代码雷同,略之。

 

6. ogr库的bug

6.1. 读取空mif文件失败的限制

如果一个mif文件是空文件(当然,文件格式是符合mif格式的,只是图层中没有feature),即mid文件尺寸为0;那么,ogr的open方法会返回NULL,意味着读取失败。

这是因为MITAB库在打开mid文件时会尝试读一行

这段代码位于int MIFFile::Open(const char *pszFname, const char *pszAccess, GBool bTestOpenNoError /*=FALSE*/ )方法内部。

有两种方法解决该问题: 1. 修改ogr库源码,先前我们是这么干的;2. 打开前向空mid文件插入一个换行符,该字符的存在能“欺骗”上文GetLine方法不返回NULL

 

6.2. 字符集的限制

ogr写出的mif/tab文件,字符集全部都是Neutral,根本不管先前的charset,鄙人驽钝,未发现修改字符集的方法,有大虾知道的话请千万要不吝指教啊!

但在IMapInfoFile类中存在一个SetCharset接口,该方法由IMapInfoFile默认实现,未被override;且该方法在MITAB库内部未被调用。

问题是OGR库未提供类似接口。

解决方法: 1. 修改ogr库,增加一个设置charset的接口。2. 抛弃OGR库,直接使用IMapInfoFile类操纵mif文件

 

6.3. 只支持少量sql查询,不支持空间查询

sql查询的相关内容可以看swq.h文件; 空间查询在OGR1.60版还未看到接口,期待。

 

7. 尽量使用GetNextFeature代替GetFeatureCount

我们看看OGRLayer::GetFeatureCount方法的实现代码

由上可以看到,大部分情况下,GetFeatureCount的参数取False时将直接得到-1;如果采用TRUE,不好意思,就是一次整图层所有feature的遍历。

很遗憾的是,MITAB库并未override该方法,也就没有更高效的算法了。

因此, 如果想判断图层是否有feature,例如sql查询的查询结果是否为空,不明确要求得到要素个数时,请先使用ResetReading再判断GetNextFeature是否为NULL,避免使用GetFeatureCount。

切记,OGR的GetFeatureCount不同于ado库的GetRecordCount。

 

    时间匆忙,lonefox上班间隙随手写就,再加就几天的使用,经验有限,错误之处恳请斧正;另,GIS同行欢迎加好友共同探讨!

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
ESRI Shape文件是一种常用的矢量数据格式,它包含了点、线、面等要素和属性信息。为了处理这种格式的数据,我们可以使用GDAL/OGR。本文将介绍如何使用C#语言和GDAL/OGR读写ESRI Shape文件。 一、安装GDAL/OGR GDAL/OGR是一个开源的地理信息系统扩展,它可以读写各种GIS数据格式。我们可以从官网(https://gdal.org/download.html)下载最新的二进制版本,并进行安装。 二、创建工作空间 在C#中使用GDAL/OGR进行ESRI Shape文件读写,需要先创建一个工作空间。工作空间是一个抽象的概念,它代表了一个包含多个数据集的环境。我们可以使用以下代码创建一个工作空间: ```csharp using OSGeo.OGR; using OSGeo.GDAL; Gdal.AllRegister(); Ogr.RegisterAll(); string workspace = @"C:\data\shapefiles"; Driver driver = Ogr.GetDriverByName("ESRI Shapefile"); DataSource dataSource = driver.Open(workspace, 0); ``` 这里使用了GDAL/OGR中的`Driver`、`DataSource`和`Gdal`等类。首先,我们调用`Gdal.AllRegister()`方法和`Ogr.RegisterAll()`方法注册GDAL/OGR中的所有驱动程序。然后,我们指定了一个路径作为工作空间,并使用`Ogr.GetDriverByName()`方法获取ESRI Shapefile驱动程序。最后,我们调用`driver.Open()`方法打开工作空间,并将返回的`DataSource`对象存储到`dataSource`变量中。 三、读取ESRI Shape文件 在创建了工作空间之后,我们可以使用`dataSource`对象读取ESRI Shape文件中的要素和属性信息。以下代码演示了如何读取ESRI Shape文件中的所有点要素: ```csharp Layer layer = dataSource.GetLayerByName("points"); Feature feature = null; while ((feature = layer.GetNextFeature()) != null) { Geometry geometry = feature.GetGeometryRef(); double x = geometry.GetX(0); double y = geometry.GetY(0); Console.WriteLine($"Point ({x}, {y})"); } ``` 这里使用了GDAL/OGR中的`Layer`、`Feature`和`Geometry`等类。首先,我们使用`dataSource.GetLayerByName()`方法获取名为“points”的图层,并将返回的`Layer`对象存储到`layer`变量中。然后,我们使用`layer.GetNextFeature()`方法循环遍历图层中的所有要素,并将返回的`Feature`对象存储到`feature`变量中。接着,我们使用`feature.GetGeometryRef()`方法获取要素的几何体,并将返回的`Geometry`对象存储到`geometry`变量中。最后,我们使用`geometry.GetX(0)`和`geometry.GetY(0)`方法获取要素的坐标,并将其打印到控制台上。 四、写入ESRI Shape文件 在读取了ESRI Shape文件之后,我们可以使用`dataSource`对象写入新的要素和属性信息。以下代码演示了如何创建一个新的ESRI Shape文件,并向其中添加一个点要素: ```csharp Layer layer = dataSource.CreateLayer("new_points", null, wkbGeometryType.wkbPoint, null); FieldDefn fieldDefn = new FieldDefn("name", FieldType.OFTString); layer.CreateField(fieldDefn, 1); Feature feature = new Feature(layer.GetLayerDefn()); Geometry geometry = new Point(121.5, 31.2); feature.SetGeometry(geometry); feature.SetField("name", "Shanghai"); layer.CreateFeature(feature); dataSource.SyncToDisk(); ``` 这里使用了GDAL/OGR中的`FieldDefn`、`Point`和`wkbGeometryType`等类。首先,我们使用`dataSource.CreateLayer()`方法创建一个名为“new_points”的图层,并将返回的`Layer`对象存储到`layer`变量中。然后,我们使用`new FieldDefn("name", FieldType.OFTString)`语句创建一个名为“name”的字符串型属性。接着,我们使用`layer.CreateField()`方法创建一个新的属性字段,并将`fieldDefn`变量和`1`参数传递给该方法。接下来,我们使用`new Point(121.5, 31.2)`语句创建一个新的点几何体,并将其存储到`geometry`变量中。然后,我们使用`new Feature(layer.GetLayerDefn())`语句创建一个新的要素,并将其存储到`feature`变量中。接着,我们使用`feature.SetGeometry(geometry)`方法设置要素的几何体。最后,我们使用`feature.SetField("name", "Shanghai")`方法设置要素的属性值,并使用`layer.CreateFeature(feature)`方法将要素添加到图层中。最后,我们使用`dataSource.SyncToDisk()`方法将修改保存到磁盘中。 总结 本文介绍了如何使用C#语言和GDAL/OGR读写ESRI Shape文件。我们首先创建了一个工作空间,并使用它读取了ESRI Shape文件中的点要素。然后,我们创建了一个新的ESRI Shape文件,并向其中添加了一个点要素。使用GDAL/OGR可以方便地处理各种GIS数据格式,为GIS应用程序的开发提供了便利。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值