GDAL使用插件方式编译HDF4、HDF5以及NetCDF的bug修改
来源:互联网 发布:中国政法大学网络教育 编辑:程序博客网 时间:2024/05/16 17:13
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修改
- GDAL+HDF4+HDF5+netCDF库编译C#
- VC++8.0下编译支持HDF4和HDF5的GDAL
- GDAL+GEOS+PROJ4+HDF4+HDF5的编译安装
- GDAL+GEOS+PROJ4+HDF4+HDF5的编译安装
- 安装和配置gdal以及让其支持hdf4和hdf5格式数据的步骤
- Comparison Between NetCDF and HDF5
- GDAL的一个BUG
- 编译使用GDAL的几个链接
- 基于gdal用c#读取hdf4文件
- 安装好netcdf hdf5 后 在lib中make的错误 怎么办
- GDAL/OGR 调试方式编译
- GDAL编译Windows平台下64位的方式
- GDAL编译Windows平台下64位的方式
- 编译 Windows 版的 netCDF 4.3.0
- netcdf源码在windows上的编译
- GDAL库的编译
- 支持MPI的hdf5库的编译
- LeetCode——Search for a Range
- 《北塔教你做插件 从RibbonX开始》第三讲:再建Ribbon——ATL的实现方法
- 求多边形面积
- class. isPrimitive() 8种基本类型的时候为 true,其他为false
- Java垃圾回收机制
- GDAL使用插件方式编译HDF4、HDF5以及NetCDF的bug修改
- C# 二维码生成和解析
- android binder c++层 - 回调客户端服务 - 客户端(c++层) 调用 服务端(c++层) 例子,服务端回调客户端服务
- Valgrind的使用方法
- [JAVA] java动态生成PDF文档
- Digital Roots
- 在js中动态创建表格
- Opencv图像数据类型
- Apple Watch人机交互指南:UI设计基础--动画、品牌化