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. [BZOJ]1063 道路设计(Noi2008)

    省选一试后的第一篇blog! Description Z国坐落于遥远而又神奇的东方半岛上,在小Z的统治时代,公路成为这里主要的交通手段.Z国共有n座城市,一些城市之间由双向的公路所连接.非常神奇的是Z ...

  2. *hdu 5536(字典树的运用)

    Input The first line of input contains an integer T indicating the total number of test cases. The f ...

  3. [bzoj4828][Ah/Hnoi2017]大佬

    来自FallDream的博客,未经允许,请勿转载,谢谢. 人们总是难免会碰到大佬.他们趾高气昂地谈论凡人不能理解的算法和数据结构,走到任何一个地方,大佬的气场就能让周围的人吓得瑟瑟发抖,不敢言语. 你 ...

  4. 【vijos1943】上学路上

    题目戳这里 描述 小雪与小可可吵架了,他们决定以后互相再也不理对方了.尤其是,他们希望以后上学的路上不会再相遇. 我们将他们所在城市的道路网视作无限大的正交网格图,每一个整数点 (x,y) 对应了一个 ...

  5. Python-闭包详解

    在函数编程中经常用到闭包.闭包是什么,它是怎么产生的及用来解决什么问题呢.给出字面的定义先:闭包是由函数及其相关的引用环境组合而成的实体(即:闭包=函数+引用环境)(想想Erlang的外层函数传入一个 ...

  6. 修改SQL数据库中表字段类型时,报“一个或多个对象访问此列”错误的解决方法

    在SQL数据库中使用SQL语句(格式:alter table [tablename] alter column [colname] [newDataType])修改某表的字段类型时,报一下错误:由于一 ...

  7. Spring之定时器(QuartzJobBean)的实现

      需求:做个分配任务的功能模块,在任务截止前的十五分钟进行提醒(发送邮件.短信.系统提醒).每隔五分钟提醒一次,直到任务完成! 想法:刚开始是想着是不是可以做个监听器,监听截止时间.当时间到了开始进 ...

  8. 数据结构之并查集Union-Find Sets

    1.  概述 并查集(Disjoint set或者Union-find set)是一种树型的数据结构,常用于处理一些不相交集合(Disjoint Sets)的合并及查询问题. 2.  基本操作 并查集 ...

  9. MarkDown语法学习

    功能性需求 输入密码 输入账号 多少度 输入 多少~~@~~度 sdsd 多少度 D是多少啊 [百度]http://www.baidu.com 百度 版本 内容 时间 v0. 需求描述 2018-4- ...

  10. python脚本文件传参并通过token登录后爬取数据实例

    from bs4 import BeautifulSoup import requests import sys class Zabbix(object): def __init__(self, he ...