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. UVA11404:Palindromic Subsequence

    回文子串dp,最小字典序的话需要记录一下,注意是string型的,不能只记录一个字符,因为可能出现相等的情况 #include<cstdio> #include<cstdlib> ...

  2. C语言程序设计第二次作业0

    (一)改错题 1.输出带框文字:在屏幕上输出以下3行信息. ************* Welcome ************* 源程序 include int mian() { printf(&q ...

  3. glusterfs4.0.1 mempool 分析笔记

    关于3.2.5版本分析,详见<GlusterFS之内存池(mem-pool)实现原理及代码详解> 此4.0.1版本内存池与版本3中的描述变化有点大,总的原理还是类似LINUX中的SLAB算 ...

  4. postman 模拟请求中添加 header,post请求中传json参数

    1. GET 请求 2.Post 请求 (请求参数为Json,header中带有参数) 问题延伸 GET请求不能够 添加 Body 吗?[答案]

  5. Linux 查看系统硬件信息汇总 (部份实例详解Centoso为例)

    1.cpu #lscpu命令,查看的是cpu的统计信息.(部分旧版本不支持) Disk /dev/sda: bytes heads, sectors/track, cylinders Units = ...

  6. 零开始:NetCore项目权限管理系统:定义基本接口和实现

    上一篇讲了基础的框架搭建    地址:http://www.cnblogs.com/fuyu-blog/p/8909779.html 这篇主要讲解SqlSugar   ORM的数据库连接以及建表和接口 ...

  7. vue环境搭建过程中,遇到的坑爹的问题

    1,在配置package.json下载node依赖包时,执行$cnpm install过程中,这个过程是比较漫长的,尤其的这种core i5配置的电脑,简直有点卡的人怀疑人生,后来动了下有消息输出,我 ...

  8. rw 模板设置

    在编译的时候只有写出rw之后使用alt+/就可以将模板代码全部展现出来. rw读写模板代码: InputStream in = null; OutputStream out = null; try { ...

  9. 听说图像识别很难,大神十行代码进行Python图像识别

      随着深度学习算法的兴起和普及,人工智能领域取得了令人瞩目的进步,特别是在计算机视觉领域.21世纪的第二个十年迅速采用卷积神经网络,发明了最先进的算法,大量训练数据的可用性以及高性能和高性价比计算的 ...

  10. Hadoop系列:(一)hdfs文件系统的基本操作

    可以执行所有常用的Linux文件操作命令(读取文件,新建文件,移动文件,删除文件,列表文件等) 1.help命令获取没个命令的帮助 [cloudera@quickstart ~]$ hadoop fs ...