zoukankan      html  css  js  c++  java
  • GDALOpen 代码分析

    先来一句话,看了这么多GDAL的源代码,并不喜欢其C风格的烙印太重,还是更喜欢boost风格的简洁的现代C++风格。不过为了更好地应用GDAL,更深的定制它,还是需要将源代码看到底。因为GDAL毕竟是一个很好的图像处理的解决方案。复用它,可以省掉很多人年的工作。

    GDALOpen函数代码:注释值得一看

    /************************************************************************/
    /*                              GDALOpen()                              */
    /************************************************************************/
    
    /**
     * rief Open a raster file as a GDALDataset.
     *
     * This function will try to open the passed file, or virtual dataset
     * name by invoking the Open method of each registered GDALDriver in turn. 
     * The first successful open will result in a returned dataset.  If all
     * drivers fail then NULL is returned and an error is issued.
     *
     * Several recommandations :
     * <ul>
     * <li>If you open a dataset object with GA_Update access, it is not recommanded
     * to open a new dataset on the same underlying file.</li>
     * <li>The returned dataset should only be accessed by one thread at a time. If you
     * want to use it from different threads, you must add all necessary code (mutexes, etc.)
     * to avoid concurrent use of the object. (Some drivers, such as GeoTIFF, maintain internal
     * state variables that are updated each time a new block is read, thus preventing concurrent
     * use.) </li>
     * </ul>
     *
     * sa GDALOpenShared()
     *
     * @param pszFilename the name of the file to access.  In the case of
     * exotic drivers this may not refer to a physical file, but instead contain
     * information for the driver on how to access a dataset.  It should be in UTF8
     * encoding.
     *
     * @param eAccess the desired access, either GA_Update or GA_ReadOnly.  Many
     * drivers support only read only access.
     *
     * @return A GDALDatasetH handle or NULL on failure.  For C++ applications
     * this handle can be cast to a GDALDataset *. 
     */
    
    GDALDatasetH CPL_STDCALL 
    GDALOpen( const char * pszFilename, GDALAccess eAccess )
    
    {
        return GDALOpenInternal(pszFilename, eAccess, NULL);
    }
    

    注释提示了几个地方:

    1. 会依次调用每个已经注册的driver的open函数,第一个成功的会返回Dataset。这个依次应该是按照注册顺序,先注册的driver先被调用。

    2. Dataset对象不是线程安全的,使用者自己注意维护多线程环境下的安全性。

    3. 返回NULL代表打开失败

    GDALDatasetH实际上是个void* , 又是C的玩法。逃过了编译器类型检查。

    /** Opaque type used for the C bindings of the C++ GDALDataset class */
    typedef void *GDALDatasetH;


    实际上就是CDALDataset* 指针。

    真正实现代码在下面的函数里面:

    /* The drivers listed in papszAllowedDrivers can be in any order */
    /* Only the order of registration will be taken into account */
    GDALDatasetH GDALOpenInternal( const char * pszFilename, GDALAccess eAccess,
                                   const char* const * papszAllowedDrivers)
    {
        VALIDATE_POINTER1( pszFilename, "GDALOpen", NULL );
    
        int         iDriver;
        GDALDriverManager *poDM = GetGDALDriverManager();
        GDALOpenInfo oOpenInfo( pszFilename, eAccess );
        CPLLocaleC  oLocaleForcer;
    
        CPLErrorReset();
        CPLAssert( NULL != poDM );
    
        for( iDriver = 0; iDriver < poDM->GetDriverCount(); iDriver++ )
        {
            GDALDriver      *poDriver = poDM->GetDriver( iDriver );
            GDALDataset     *poDS;
    
            if (papszAllowedDrivers != NULL &&
                CSLFindString((char**)papszAllowedDrivers, GDALGetDriverShortName(poDriver)) == -1)
                continue;
    
            if ( poDriver->pfnOpen == NULL )
                continue;
    
            poDS = poDriver->pfnOpen( &oOpenInfo );
            if( poDS != NULL )
            {
                if( strlen(poDS->GetDescription()) == 0 )
                    poDS->SetDescription( oOpenInfo.pszFilename );
    
                if( poDS->poDriver == NULL )
                    poDS->poDriver = poDriver;
    
    
                if( CPLGetPID() != GDALGetResponsiblePIDForCurrentThread() )
                    CPLDebug( "GDAL", "GDALOpen(%s, this=%p) succeeds as %s (pid=%d, responsiblePID=%d).",
                              pszFilename, poDS, poDriver->GetDescription(),
                              (int)CPLGetPID(), (int)GDALGetResponsiblePIDForCurrentThread() );
                else
                    CPLDebug( "GDAL", "GDALOpen(%s, this=%p) succeeds as %s.",
                              pszFilename, poDS, poDriver->GetDescription() );
    
                return (GDALDatasetH) poDS;
            }
    
            if( CPLGetLastErrorNo() != 0 )
                return NULL;
        }
    
        if( oOpenInfo.bStatOK )
            CPLError( CE_Failure, CPLE_OpenFailed,
                      "`%s' not recognised as a supported file format.
    ",
                      pszFilename );
        else
            CPLError( CE_Failure, CPLE_OpenFailed,
                      "`%s' does not exist in the file system,
    "
                      "and is not recognised as a supported dataset name.
    ",
                      pszFilename );
    
        return NULL;
    }


    这段就和注释说的一样,遍历driver,依次尝试打开文件。在我的GeoTiff driver中,open方法会调用geotiff.cpp文件的Open方法:

    /************************************************************************/
    /*                                Open()                                */
    /************************************************************************/
    
    GDALDataset *GTiffDataset::Open( GDALOpenInfo * poOpenInfo )
    
    {
        TIFF	*hTIFF;
        int          bAllowRGBAInterface = TRUE;
        const char  *pszFilename = poOpenInfo->pszFilename;
    
    /* -------------------------------------------------------------------- */
    /*      Check if it looks like a TIFF file.                             */
    /* -------------------------------------------------------------------- */
        if (!Identify(poOpenInfo))
            return NULL;
    
        if( EQUALN(pszFilename,"GTIFF_RAW:", strlen("GTIFF_RAW:")) )
        {
            bAllowRGBAInterface = FALSE;
            pszFilename +=  strlen("GTIFF_RAW:");
        }
    
    /* -------------------------------------------------------------------- */
    /*      We have a special hook for handling opening a specific          */
    /*      directory of a TIFF file.                                       */
    /* -------------------------------------------------------------------- */
        if( EQUALN(pszFilename,"GTIFF_DIR:",strlen("GTIFF_DIR:")) )
            return OpenDir( poOpenInfo );
    
        if (!GTiffOneTimeInit())
            return NULL;
    
    /* -------------------------------------------------------------------- */
    /*      Try opening the dataset.                                        */
    /* -------------------------------------------------------------------- */
        if( poOpenInfo->eAccess == GA_ReadOnly )
    	hTIFF = VSI_TIFFOpen( pszFilename, "r" );
        else
            hTIFF = VSI_TIFFOpen( pszFilename, "r+" );
        
        if( hTIFF == NULL )
            return( NULL );
    
    /* -------------------------------------------------------------------- */
    /*      Create a corresponding GDALDataset.                             */
    /* -------------------------------------------------------------------- */
        GTiffDataset 	*poDS;
    
        poDS = new GTiffDataset();
        poDS->SetDescription( pszFilename );
        poDS->osFilename = pszFilename;
        poDS->poActiveDS = poDS;
    
        if( poDS->OpenOffset( hTIFF, &(poDS->poActiveDS),
                              TIFFCurrentDirOffset(hTIFF), TRUE,
                              poOpenInfo->eAccess, 
                              bAllowRGBAInterface, TRUE,
                              poOpenInfo->papszSiblingFiles) != CE_None )
        {
            delete poDS;
            return NULL;
        }
    
    /* -------------------------------------------------------------------- */
    /*      Initialize any PAM information.                                 */
    /* -------------------------------------------------------------------- */
        poDS->TryLoadXML();
        poDS->ApplyPamInfo();
    
        int i;
        for(i=1;i<=poDS->nBands;i++)
        {
            GTiffRasterBand* poBand = (GTiffRasterBand*) poDS->GetRasterBand(i);
    
            /* Load scale, offset and unittype from PAM if available */
            if (!poBand->bHaveOffsetScale)
            {
                poBand->dfScale = poBand->GDALPamRasterBand::GetScale(&poBand->bHaveOffsetScale);
                poBand->dfOffset = poBand->GDALPamRasterBand::GetOffset();
            }
            if (poBand->osUnitType.size() == 0)
            {
                const char* pszUnitType = poBand->GDALPamRasterBand::GetUnitType();
                if (pszUnitType)
                    poBand->osUnitType = pszUnitType;
            }
        }
    
        poDS->bMetadataChanged = FALSE;
        poDS->bGeoTIFFInfoChanged = FALSE;
    
    /* -------------------------------------------------------------------- */
    /*      Check for external overviews.                                   */
    /* -------------------------------------------------------------------- */
        poDS->oOvManager.Initialize( poDS, pszFilename );
        
        return poDS;
    }


    这里主要看poDs->OpenOffset函数,它负责读取tiff文件的基本信息,以便后面快速读取。因为这么大的文件,显然不可能一次读进内存来。

    /************************************************************************/
    /*                             OpenOffset()                             */
    /*                                                                      */
    /*      Initialize the GTiffDataset based on a passed in file           */
    /*      handle, and directory offset to utilize.  This is called for    */
    /*      full res, and overview pages.                                   */
    /************************************************************************/
    
    CPLErr GTiffDataset::OpenOffset( TIFF *hTIFFIn, 
                                     GTiffDataset **ppoActiveDSRef,
                                     toff_t nDirOffsetIn, 
    				 int bBaseIn, GDALAccess eAccess,
                                     int bAllowRGBAInterface,
                                     int bReadGeoTransform,
                                     char** papszSiblingFiles )


    该函数的定义在geotiff.cpp文件中,非常长,所以这里就不列代码了。

    如果要完整的将GeoTiff整个加载过程分析透,需要更多的篇幅。我以后会不断的修改已经有的文章,使得其更准确,并增加新的文章,描述更多的细节。

    欢迎讨论。

  • 相关阅读:
    【TCP/IP】【网络基础】网页访问流程
    【linux】【rpm】确定程序是否 rpm 安装
    【linux】【CPU】【x86】平台说明
    【linux】【进程】stand alone 与 super daemon 区别
    【内存】堆内存和栈内存
    【英语】【音标】元音字母 和 开音节 闭音节
    【php】【异步】php实现异步的几种方法
    【操作系统】多处理器系统的用户模式和特权模式
    张量漫谈(第三篇)
    张量漫谈(前两篇)
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3260400.html
Copyright © 2011-2022 走看看