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修改的更多相关文章

  1. 如何实现Windows Phone代码与Unity相互通信(插件方式)

    原地址:http://www.cnblogs.com/petto/p/3915943.html 一些废话 原文地址: http://imwper.com/unity/petto/%E5%A6%82%E ...

  2. Heka 的编译 和 Heka 插件的编译

    相关英文文档在: https://hekad.readthedocs.io/en/latest/installing.html 所有系统都必须的如下: Prerequisites (all syste ...

  3. [转]Windows中使用命令行方式编译打包Android项目

    http://my.oschina.net/liux/blog/37875 网上很多用Ant来编译打包Android应用的文章,毕竟Ant是纯Java语言编写的,具有很好的跨平台性.今天想写个纯win ...

  4. AndroidStudio用Cmake方式编译NDK代码(cmake配置.a库)

    1.cmake是什么? CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程).他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C+ ...

  5. Django【设计】可插拔的插件方式实现

    需求: 在CMDB系统中,我们需要对资产进行采集和资产入库,包括serverBasic.disk.memory.nic信息等,客户端需要采集这些硬件的信息,服务端则负责资产入库,但是需要采集的硬件并不 ...

  6. 编译spark源码 Maven 、SBT 2种方式编译

    由于实际环境较为复杂,从Spark官方下载二进制安装包可能不具有相关功能或不支持指定的软件版本,这就需要我们根据实际情况编译Spark源代码,生成所需要的部署包. Spark可以通过Maven和SBT ...

  7. mybatis通过插件方式实现读写分离

    原理:通过自定义mybatis插件,拦截Executor的update和query方法,检查sql中有select就用读的库,其它的用写的库(如果有调用存储过程就另当别论了) @Intercepts( ...

  8. OSGI.NET插件方式开发你的应用

    之前一直从事C# WEB开发.基本都是业务开发,性能优化. 体力活占比90%吧.模块真的很多很多,每次部署经常出先各种问题.发布经常加班. 今年开始接触winform 开发.发现C# 的事件  委托 ...

  9. MyBatis代码生成器(maven插件方式和控制台命令运行方式)

    代码生成器的作用: 1.生成domain 2.生成mapper接口 3.生成mapper映射文件 准备工作:导入MyBatis所需要的包 第一步:在src/main/resources(必须)目录下创 ...

随机推荐

  1. 【HNOI2017】大佬

    题目描述 人们总是难免会碰到大佬.他们趾高气昂地谈论凡人不能理解的算法和数据结构,走到任何一个地方,大佬的气场就能让周围的人吓得瑟瑟发抖,不敢言语. 你作为一个 OIER,面对这样的事情非常不开心,于 ...

  2. ●BOZJ 2229 [Zjoi2011]最小割

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2229 题解: 首先先去看看这个博客:http://blog.csdn.net/jyxjyx2 ...

  3. es6新增

    首先要说let,他是只在代码块中执行的变量,例如: {    let a = 10;    var b = 1;}console.log(a);//definedconsole.log(b);//1 ...

  4. 00-Unit_Common综述-RecyclerView封装

    自学安卓也有一年的时间了,与代码相伴的日子里,苦乐共存.能坚持到现在确实已见到了"往日所未曾见证的风采".今2018年4月2日,决定用一个案例:Unit_Common,把安卓基础的 ...

  5. javascript面向对象(给对象添加属性和方法的方式)

    1.在定义对象时,直接把属性和方法添加 <script type="text/JavaScript"> //给对象直接在定义时添加属性和方法         var g ...

  6. Python小代码_12_生成前 n 行杨辉三角

    def demo(t): print([1]) print([1, 1]) line = [1, 1] for i in range(2, t): r = [] for j in range(0, l ...

  7. 31. Next Permutation(中等,搞清楚啥是 next permutation)

    Implement next permutation, which rearranges numbers into the lexicographically next greater permuta ...

  8. dict的操作和三级菜单

    dict的基本操作 # Author:nadech info = { "stu001":"sjp", "stu002":"cxx& ...

  9. 终止Docker容器

    可以使用 docker stop 来终止一个运行中的容器. 此外,当Docker容器中指定的应用终结时,容器也自动终止. 例如对于上一章节中只启动了一个终端的容器,用户通过 exit 命令或 Ctrl ...

  10. Android Multimedia框架总结(二十一)MediaCodec中创建到start过程(到jni部分)

    转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/53386117 我最近正在参加CS ...