GDAL使用插件方式编译HDF4、HDF5以及NetCDF的bug修改
GDAL库中提供了很方便的插件机制来扩展支持的数据格式,比如HDF4、HDF5、NetCDF、FileGDB、Postgre、Oralce等等。都可以通过插件的方式来使得GDAL支持相应的格式。最近将所有的能编译成插件的格式都编译成插件,这样在发布的时候有些用不到的数据格式就可以不用将对应的插件以及以来的dll放进去,减少安装包的体积等。
发现HDF4、HDF5和NetCDF这三个编译成插件之后会出现几个问题,比如可以打开HDF4和HDF5的数据,但是不能打开里面的子数据集,找了好久,才发现GDAL的插件机制有点小欠缺(小问题)。
自己研究发现,GDAL的插件机制是这样的,结合代码看看,在文件gcore/gdaldrivermanager.cpp中的函数void GDALDriverManager::AutoLoadDrivers()中,节选最关键的一点代码,如下所示:
/* -------------------------------------------------------------------- */
/* Format the ABI version specific subdirectory to look in. */
/* -------------------------------------------------------------------- */
CPLString osABIVersion; osABIVersion.Printf( "%d.%d", GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR ); /* -------------------------------------------------------------------- */
/* Scan each directory looking for files starting with gdal_ */
/* -------------------------------------------------------------------- */
for( int iDir = 0; iDir < CSLCount(papszSearchPath); iDir++ )
{
char **papszFiles = NULL;
VSIStatBufL sStatBuf;
CPLString osABISpecificDir =
CPLFormFilename( papszSearchPath[iDir], osABIVersion, NULL ); if( VSIStatL( osABISpecificDir, &sStatBuf ) != 0 )
osABISpecificDir = papszSearchPath[iDir]; papszFiles = CPLReadDir( osABISpecificDir );
int nFileCount = CSLCount(papszFiles); for( int iFile = 0; iFile < nFileCount; iFile++ )
{
char *pszFuncName;
const char *pszFilename;
const char *pszExtension = CPLGetExtension( papszFiles[iFile] );
void *pRegister; if( !EQUAL(pszExtension,"dll")
&& !EQUAL(pszExtension,"so")
&& !EQUAL(pszExtension,"dylib") )
continue; if( EQUALN(papszFiles[iFile],"gdal_",strlen("gdal_")) )
{
pszFuncName = (char *) CPLCalloc(strlen(papszFiles[iFile])+20,1);
sprintf( pszFuncName, "GDALRegister_%s",
CPLGetBasename(papszFiles[iFile]) + strlen("gdal_") );
}
else if ( EQUALN(papszFiles[iFile],"ogr_",strlen("ogr_")) )
{
pszFuncName = (char *) CPLCalloc(strlen(papszFiles[iFile])+20,1);
sprintf( pszFuncName, "RegisterOGR%s",
CPLGetBasename(papszFiles[iFile]) + strlen("ogr_") );
}
else
continue; pszFilename =
CPLFormFilename( osABISpecificDir,
papszFiles[iFile], NULL ); CPLErrorReset();
CPLPushErrorHandler(CPLQuietErrorHandler);
pRegister = CPLGetSymbol( pszFilename, pszFuncName );
CPLPopErrorHandler();
if( pRegister == NULL )
{
CPLString osLastErrorMsg(CPLGetLastErrorMsg());
strcpy( pszFuncName, "GDALRegisterMe" );
pRegister = CPLGetSymbol( pszFilename, pszFuncName );
if( pRegister == NULL )
{
CPLError( CE_Failure, CPLE_AppDefined,
"%s", osLastErrorMsg.c_str() );
}
} if( pRegister != NULL )
{
CPLDebug( "GDAL", "Auto register %s using %s.",
pszFilename, pszFuncName ); ((void (*)()) pRegister)();
} CPLFree( pszFuncName );
} CSLDestroy( papszFiles );
} CSLDestroy( papszSearchPath );
仔细分析上面的代码,可以看到GDAL在加载驱动并进行注册的时候,将插件的名称里面的gdal_XXX后面的字符串XXX取出来,然后组成函数GDALRegister_XXX,在插件的dll中查找这个函数GDALRegister_XXX指针,如果找不到就找GDALRegisterMe函数。然后调用这个函数进行注册。对于OGR的插件也是类似。
一般的插件来说,导出的函数中GDALRegister_开头的函数只有一个,如果一个插件里面有两个GDALRegister_函数,那就惨了,插件只能注册那个和插件名字一样的那一个另外一个没注册进行,所以肯定也打不开对应的数据了。HDF4、HDF5以及NetCDF就是这样的特例。HDF4和HDF5插件里面实现了两个GDALRegister_函数,一个是用来打开数据的,另外一个是用来打开子数据的,也就是GDALRegister_HDF4和GDALRegister_HDF4Image以及GDALRegister_HDF5和GDALRegister_HDF5Image。这样的话,对于插件方式来说,这两个后面带有Image的肯定注册不了。
知道插件注册的原理,那么解决方式就有了,不想改代码的话,就把GDAL的HDF4和HDF5的插件dll复制一份,然后改名字,后面加一个Image就好了,这样最简单,但是同样的dll会以不同的名字存在两边,觉得不太爽。那么第二种就是改源码了。具体就是修改上面这段代码,如果是HDF的格式的话,单独判断一下,然后将Image这个函数注册了就好了(对于NetCDF也有类似的问题)。修改后的代码如下:
/* -------------------------------------------------------------------- */
/* Format the ABI version specific subdirectory to look in. */
/* -------------------------------------------------------------------- */
CPLString osABIVersion; osABIVersion.Printf( "%d.%d", GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR ); /* -------------------------------------------------------------------- */
/* Scan each directory looking for files starting with gdal_ */
/* -------------------------------------------------------------------- */
for( int iDir = 0; iDir < CSLCount(papszSearchPath); iDir++ )
{
char **papszFiles = NULL;
VSIStatBufL sStatBuf;
CPLString osABISpecificDir =
CPLFormFilename( papszSearchPath[iDir], osABIVersion, NULL ); if( VSIStatL( osABISpecificDir, &sStatBuf ) != 0 )
osABISpecificDir = papszSearchPath[iDir]; papszFiles = CPLReadDir( osABISpecificDir );
int nFileCount = CSLCount(papszFiles); for( int iFile = 0; iFile < nFileCount; iFile++ )
{
char *pszFuncName;
const char *pszFilename;
const char *pszExtension = CPLGetExtension( papszFiles[iFile] );
void *pRegister; if( !EQUAL(pszExtension,"dll")
&& !EQUAL(pszExtension,"so")
&& !EQUAL(pszExtension,"dylib") )
continue; if( EQUALN(papszFiles[iFile],"gdal_",strlen("gdal_")) )
{
pszFuncName = (char *) CPLCalloc(strlen(papszFiles[iFile])+20,1);
sprintf( pszFuncName, "GDALRegister_%s",
CPLGetBasename(papszFiles[iFile]) + strlen("gdal_") );
}
else if ( EQUALN(papszFiles[iFile],"ogr_",strlen("ogr_")) )
{
pszFuncName = (char *) CPLCalloc(strlen(papszFiles[iFile])+20,1);
sprintf( pszFuncName, "RegisterOGR%s",
CPLGetBasename(papszFiles[iFile]) + strlen("ogr_") );
}
else
continue; pszFilename =
CPLFormFilename( osABISpecificDir,
papszFiles[iFile], NULL ); CPLErrorReset();
CPLPushErrorHandler(CPLQuietErrorHandler);
pRegister = CPLGetSymbol( pszFilename, pszFuncName );
CPLPopErrorHandler();
if( pRegister == NULL )
{
CPLString osLastErrorMsg(CPLGetLastErrorMsg());
strcpy( pszFuncName, "GDALRegisterMe" );
pRegister = CPLGetSymbol( pszFilename, pszFuncName );
if( pRegister == NULL )
{
CPLError( CE_Failure, CPLE_AppDefined,
"%s", osLastErrorMsg.c_str() );
}
} if( pRegister != NULL )
{
CPLDebug( "GDAL", "Auto register %s using %s.",
pszFilename, pszFuncName ); ((void (*)()) pRegister)(); // 如果一个插件dll中有多个注册函数,目前针对特殊的几种格式进行特殊处理
const char* pszBaseName = CPLGetBasename(papszFiles[iFile]) + 5;
if( EQUAL(pszBaseName, "HDF4") || EQUAL(pszBaseName, "HDF5") )
{
CPLString osLastErrorMsg(CPLGetLastErrorMsg());
sprintf( pszFuncName, "GDALRegister_%sImage",
CPLGetBasename(papszFiles[iFile]) + 5 ); pRegister = CPLGetSymbol( pszFilename, pszFuncName );
if( pRegister == NULL )
{
CPLError( CE_Failure, CPLE_AppDefined,
"%s", osLastErrorMsg.c_str() );
} CPLDebug( "GDAL", "Auto register %s using %s.",
pszFilename, pszFuncName ); ((void (*)()) pRegister)();
}
else if(EQUAL(pszBaseName, "NETCDF"))
{
CPLString osLastErrorMsg(CPLGetLastErrorMsg());
strcpy( pszFuncName, "GDALRegister_GMT" );
pRegister = CPLGetSymbol( pszFilename, pszFuncName );
if( pRegister == NULL )
{
CPLError( CE_Failure, CPLE_AppDefined,
"%s", osLastErrorMsg.c_str() );
} CPLDebug( "GDAL", "Auto register %s using %s.",
pszFilename, pszFuncName ); ((void (*)()) pRegister)();
}
} CPLFree( pszFuncName );
} CSLDestroy( papszFiles );
} CSLDestroy( papszSearchPath );
}
GDAL使用插件方式编译HDF4、HDF5以及NetCDF的bug修改的更多相关文章
- 如何实现Windows Phone代码与Unity相互通信(插件方式)
原地址:http://www.cnblogs.com/petto/p/3915943.html 一些废话 原文地址: http://imwper.com/unity/petto/%E5%A6%82%E ...
- Heka 的编译 和 Heka 插件的编译
相关英文文档在: https://hekad.readthedocs.io/en/latest/installing.html 所有系统都必须的如下: Prerequisites (all syste ...
- [转]Windows中使用命令行方式编译打包Android项目
http://my.oschina.net/liux/blog/37875 网上很多用Ant来编译打包Android应用的文章,毕竟Ant是纯Java语言编写的,具有很好的跨平台性.今天想写个纯win ...
- AndroidStudio用Cmake方式编译NDK代码(cmake配置.a库)
1.cmake是什么? CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程).他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C+ ...
- Django【设计】可插拔的插件方式实现
需求: 在CMDB系统中,我们需要对资产进行采集和资产入库,包括serverBasic.disk.memory.nic信息等,客户端需要采集这些硬件的信息,服务端则负责资产入库,但是需要采集的硬件并不 ...
- 编译spark源码 Maven 、SBT 2种方式编译
由于实际环境较为复杂,从Spark官方下载二进制安装包可能不具有相关功能或不支持指定的软件版本,这就需要我们根据实际情况编译Spark源代码,生成所需要的部署包. Spark可以通过Maven和SBT ...
- mybatis通过插件方式实现读写分离
原理:通过自定义mybatis插件,拦截Executor的update和query方法,检查sql中有select就用读的库,其它的用写的库(如果有调用存储过程就另当别论了) @Intercepts( ...
- OSGI.NET插件方式开发你的应用
之前一直从事C# WEB开发.基本都是业务开发,性能优化. 体力活占比90%吧.模块真的很多很多,每次部署经常出先各种问题.发布经常加班. 今年开始接触winform 开发.发现C# 的事件 委托 ...
- MyBatis代码生成器(maven插件方式和控制台命令运行方式)
代码生成器的作用: 1.生成domain 2.生成mapper接口 3.生成mapper映射文件 准备工作:导入MyBatis所需要的包 第一步:在src/main/resources(必须)目录下创 ...
随机推荐
- 【JZOJ4307】喝喝喝
Description solution 正解:尺取法. 很容易想到尺取法,维护左右指针,\(a[i]\%a[j]==K\),当且仅当 \(a[j]>K\) 并且 \(a[i]-K\) 的约数中 ...
- ●BOZJ 2229 [Zjoi2011]最小割
题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2229 题解: 首先先去看看这个博客:http://blog.csdn.net/jyxjyx2 ...
- 决战 状压dp
决定在这个小巷里排兵布阵.小巷可以抽象成一个们彼此之间并不是十分和♂谐.具体来说,一个哲学家会有一个的矩形.每一位哲学家会占据一个格子.然而哲学家的01矩阵来表示他自己的守备范围.哲学家自己位于这个矩 ...
- Linux之软链接与硬链接
什么是链接? 链接简单说实际上是一种文件共享的方式,是 POSIX 中的概念,主流文件系统都支持链接文件. 它是用来干什么的? 你可以将链接简单地理解为 Windows 中常见的快捷方式(或是 OS ...
- SVN与Git
一:SVN是什么?SVN是Subversion的简称,是一个开放源代码的版本控制系统,相较于RCS.CVS,它采用了分支管理系统,它的设计目标就是取代CVS.互联网上很多版本控制服务已从CVS迁移到S ...
- 【.NET Core】docker Jenkins ASP.NET Core自动化部署
本文基于GitHub演示自动化部署,实际上你可以选择任意的Git托管环境. 使用的模式:DooD(Docker-outside-of-Docker). 本文所有内容均开源 链接 欢迎关注我的GitHu ...
- 树莓派3B(2)- 配置多个wifi,自动寻找可用网络
一.背景 在上篇<Raspberry Pi 3B 安装系统并联网>中,树莓派使用wifi连接,但是把树莓派带到公司,树莓派就连不了公司的wifi,要是支持连接多个wifi就好了,在此整理分 ...
- 自调用匿名函数和js的Module模式
编写自调用匿名函数的结构一般如下: :(function( window, undefined ) { // your code })(window); 传入的参数window,和参数列表中的unde ...
- npm下载包很慢和node-sass编译错误的解决办法
最近研究一个ionic cordova angular2的前端项目 发现npm install下载包非常慢的问题 最近整理了一些解决这些问题的方法. 1.通过config命令修改https为http ...
- JavaScript的BOM、DOM操作、节点以及表格(二)
BOM操作 一.什么是BOM BOM(Browser Object Model)即浏览器对象模型. BOM提供了独立于内容 而与浏览器窗口进行交互的对象: BOM由一系列相关的对象构成 ...