OGR库读写mif/tab文件lonefox使用经验小结
===================================
本人log中所有未注明转载的文章和blog一般为本人原创或整理加工,
原创文章版权本人(lonefox)所有;转载文章版权归原作者所有;
欢迎转载,但请注明出处,保留作者和版权信息。
===================================
最近项目中使用GDAL/OGR库读写mif和tab文件, 经过几天的折腾,积累了点点经验,且记之.
1. OGRSFDriverRegistrar::Open(Path, FALSE);只能读文件
对于OGRSFDriverRegistrar::Open方法, ogr内置的mif/TAB读写库MITAB是不支持该方法的更新的,看下面的MITAB源码:
if( bUpdate )//请注意这儿,如果bUpdate为TRUE,则直接返回NULL
{
return NULL;
}
//other code
}
再深入源码,你会发现,通过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格式的互转就再简单不过了。直接源码吧
OGRSFDriver *pDriver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(“MapInfo File”);
if( pDriver == NULL )
{
return FALSE;
}
//打开原始mif
//CNILayer::CreateEmptyMID(MIFPath);//将空mid改非空,避免ogr加载失败
OGRDataSource* pDSMIF = OGRSFDriverRegistrar::Open(MIFPath);
if (pDSMIF == NULL)
{
TRACE1(“warning:MIF file open failed %s/n”, MIFPath);
return FALSE;
}
OGRLayer* pLayerMIF = pDSMIF->GetLayer(0);
//创建结果TAB
OGRDataSource *pDSTAB = pDriver->CreateDataSource(TabPath);
if (pDSTAB == NULL)
{
TRACE1(“error:TAB file created failed %s/n”, TabPath);
OGRDataSource::DestroyDataSource( pDSMIF );
return FALSE;
}
//拷贝数据
OGRLayer* pLayerTAB = pDSTAB->CopyLayer(pLayerMIF, “test”, NULL);
//关闭MIF文件
OGRDataSource::DestroyDataSource(pDSMIF);
if( pLayerTAB == NULL )
{
TRACE2(“error:copied layer to TAB failed %s->%s/n”, MIFPath, TabPath);
OGRDataSource::DestroyDataSource( pDSTAB );
return FALSE;
}
//保存结果
OGRErr err = pLayerTAB->SyncToDisk();
//关闭TAB文件
OGRDataSource::DestroyDataSource( pDSTAB );
if (err != OGRERR_NONE)
{
TRACE1(“error:saved TAB file failed %s/n”, TabPath);
return FALSE;
}
return TRUE;
}
tab转mif和上面代码雷同,略之。
6. ogr库的bug
6.1. 读取空mif文件失败的限制
如果一个mif文件是空文件(当然,文件格式是符合mif格式的,只是图层中没有feature),即mid文件尺寸为0;那么,ogr的open方法会返回NULL,意味着读取失败。
这是因为MITAB库在打开mid文件时会尝试读一行
if (bTestOpenNoError)
CPLErrorReset();
return -1;
}
这段代码位于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方法的实现代码
if( !bForce )
return -1;
ResetReading();
while( (poFeature = GetNextFeature()) != NULL )
{
nFeatureCount++;
delete poFeature;
}
ResetReading();
return nFeatureCount;
}
由上可以看到,大部分情况下,GetFeatureCount的参数取False时将直接得到-1;如果采用TRUE,不好意思,就是一次整图层所有feature的遍历。
很遗憾的是,MITAB库并未override该方法,也就没有更高效的算法了。
因此, 如果想判断图层是否有feature,例如sql查询的查询结果是否为空,不明确要求得到要素个数时,请先使用ResetReading再判断GetNextFeature是否为NULL,避免使用GetFeatureCount。
切记,OGR的GetFeatureCount不同于ado库的GetRecordCount。
时间匆忙,lonefox上班间隙随手写就,再加就几天的使用,经验有限,错误之处恳请斧正;另,GIS同行欢迎加好友共同探讨!
转载自:https://blog.csdn.net/boythl/article/details/4819613