RFC 11:快速格式识别
作者:弗兰克·温特丹
联系方式:warmerdam@pobox.com
状态:通过(和实施)
总结
这个RFC旨在增加应用程序快速识别文件系统中哪些文件是GDAL支持的文件格式的能力,而不必打开它们中的任何一个。它主要用于允许基于文件类型的GUI文件浏览器。
这是通过扩展GDALOpenInfo结构来保存更多的目录上下文,并通过在GDALDriver上添加一个Identify()方法来实现的,驱动程序可以实现该方法来快速标识文件的给定格式,而无需执行更昂贵的Open()操作。
GDALOpenInfo
许多驱动程序的Open()(或Identify())方法需要探测与目标文件相关联的文件,以便打开或识别具有特定格式的文件。例如,为了打开ESRIBIL文件(EHDR驱动程序),有必要探测与目标文件具有相同基名但扩展名为.hdr的驱动程序。目前,这通常是通过VSIFStatL()调用或类似的方法来完成的,这可能相当昂贵。
为了减少对操作系统文件系统机器的此类搜索的需要,GDALOpenInfo结构将被扩展以保存一个可选的文件列表。这是文件系统中与目标文件处于同一级别的所有文件(包括目标文件)的列表。文件名将 not 包含任何路径组件,本质上都是父目录上CPLReadDir()的输出。如果目标对象没有文件系统语义,则文件列表应为空。
将以下内容添加到GDALOpenInfo:
GDALOpenInfo( const char * pszFile, GDALAccess eAccessIn, char **papszSiblings );
char **papszSiblingFiles;
新的构造函数允许传入文件列表来填充papszSiblingFiles成员(参数将被复制)。现有的默认构造函数将使用CPLGetDirname()获取传递的pszFile的目录,并使用CPLReadDir()读取相应的文件列表。新的构造函数主要是为了高效地实现后面的GDALIdentifyDriver()函数,避免重新读取要测试的每个文件的文件列表。
识别()
GDALDriver类将使用以下函数进行扩展:
int (*pfnIdentify)( GDALOpenInfo * );
当由驱动程序实现时,如果驱动程序确定通过GDALOpenInfo传入的文件的格式似乎与实现驱动程序的格式相同,则函数将返回TRUE(非零)。要调用此应用程序,应调用新函数:
GDALDriverH *GDALIdentifyDriver( const char *pszDatasource, const char **papszDirFiles );
在内部GDALIdentifyDriver()将执行以下操作
将基于pszDatasource和papszdir文件初始化GDALOpenInfo结构。
它将迭代所有驱动程序,与GDALOpen()类似。对于每个驱动程序,如果可用,它将使用pfnIdentify函数,否则它将使用pfnOpen()方法来确定驱动程序是否支持该文件。
它将返回驱动程序句柄,以便第一个驱动程序作出肯定的响应,如果没有人接受它,则返回NULL。
驱动程序更改
理论上,不需要修改任何驱动程序,因为GDALIdentifyDriver()将回退到使用pfnOpen函数进行测试。但实际上,除非至少更新一些驱动程序(希望那些打开的驱动程序可能非常昂贵),否则无法实现优化。接下来正在进行的工作的一部分是实现GDAL驱动程序的标识功能。
一般来说,应该很容易从开放函数中的初始测试逻辑创建一个标识函数。例如,GeoTIFF驱动程序可能会这样更改:
int GTiffDataset::Identify( GDALOpenInfo * poOpenInfo )
{
/* -------------------------------------------------------------------- */
/* We have a special hook for handling opening a specific */
/* directory of a TIFF file. */
/* -------------------------------------------------------------------- */
if( EQUALN(poOpenInfo->pszFilename,"GTIFF_DIR:",10) )
return TRUE;
/* -------------------------------------------------------------------- */
/* First we check to see if the file has the expected header */
/* bytes. */
/* -------------------------------------------------------------------- */
if( poOpenInfo->nHeaderBytes < 2 )
return FALSE;
if( (poOpenInfo->pabyHeader[0] != 'I' || poOpenInfo->pabyHeader[1] != 'I')
&& (poOpenInfo->pabyHeader[0] != 'M' || poOpenInfo->pabyHeader[1] != 'M'))
return FALSE;
// We can't support BigTIFF files for now.
if( poOpenInfo->pabyHeader[2] == 43 && poOpenInfo->pabyHeader[3] == 0 )
return FALSE;
if( (poOpenInfo->pabyHeader[2] != 0x2A || poOpenInfo->pabyHeader[3] != 0)
&& (poOpenInfo->pabyHeader[3] != 0x2A || poOpenInfo->pabyHeader[2] != 0) )
return FALSE;
return TRUE;
}
然后可以修改open以使用identify函数来避免重复测试逻辑。
GDALDataset *GTiffDataset::Open( GDALOpenInfo * poOpenInfo )
{
TIFF *hTIFF;
if( !Identify( poOpenInfo ) )
return NULL;
/* -------------------------------------------------------------------- */
/* We have a special hook for handling opening a specific */
/* directory of a TIFF file. */
/* -------------------------------------------------------------------- */
if( EQUALN(poOpenInfo->pszFilename,"GTIFF_DIR:",10) )
return OpenDir( poOpenInfo->pszFilename );
GTiffOneTimeInit();
...
需要头文件(如EHdr驱动程序)的驱动程序可能实现如下Identify():
int EHdrDataset::Identify( GDALOpenInfo * poOpenInfo )
{
int i, bSelectedHDR;
const char *pszHDRFilename;
/* -------------------------------------------------------------------- */
/* We assume the user is pointing to the binary (ie. .bil) file. */
/* -------------------------------------------------------------------- */
if( poOpenInfo->nHeaderBytes < 2 )
return FALSE;
/* -------------------------------------------------------------------- */
/* Now we need to tear apart the filename to form a .HDR */
/* filename. */
/* -------------------------------------------------------------------- */
CPLString osBasename = CPLGetBasename( poOpenInfo->pszFilename );
pszHDRFilename = CPLFormCIFilename( "", osBasename, "hdr" );
if( CSLFindString( poOpenInfo->papszSiblingFiles, pszHDRFilename) )
return TRUE;
else
return FALSE;
}
在初始实现期间,将更新各种驱动程序,包括以下内容。此外,还将进行一些性能和文件系统活动日志记录,以确定当前价格昂贵的驱动程序。
HFA
GTiff
JPEG
PNG
GIF
HDF4型
DTED
美国地质勘探局数字高程模型
影像数据库
2千日元
ECW
EHdr
RST
CPLReadDir()
目前,在cpl_vsi_mem.cpp中实现的VSIMemFilesystemHandler提供了对内存中对象的“类文件系统”访问,但它没有实现目录读取服务。为了正确地填充目录列表,需要添加这个。
为此,还需要重新实现cpl ReadDir()函数,以使用VSIFilesystemHandler::ReadDir(),而不是在cpl dir.cpp中直接实现。VSIFilesystemHandler::ReadDir()的win32和unix/posix实现已经存在。这将基本上完成文件系统访问服务的虚拟化。
CPLReadDir()也将被重命名为VSIReadDir(),但使用旧名称下的存根可向后兼容。
兼容性
没有预期的向后兼容性问题。不过,转发兼容性将受到影响,因为在trunk中更新的带有标识功能的驱动程序将无法移植回1.4版本并使用它们。未修改的驱动程序和外部维护的驱动程序不应受到此开发的影响。
SWIG含义
GDALIdentifyDriver()和VSIReadDir()函数需要通过SWIG公开。
回归检验
Identify()函数的测试脚本将添加到autotest/gcore目录中。它将包括在a/vsimem内存集合中测试标识。
实施计划
新功能将由Frank wartemdam在 大旅行箱 对于GDAL/OGR 1.5.0版本。
性能试验
引入标识而不实际打开的快速测试将一个目录中包含70个TIFF文件(在NFS共享上)的所有文件的标识时间从2秒更改为0.5秒。因此,节省实际打开文件的开销对于某些格式来说非常重要,包括像GeoTIFF这样的非常常见的格式。