GDAL은 플러그인을 사용하여 HDF4, HDF5 및 NetCDF의 버그 수정을 컴파일합니다.

GDAL 라이브러리는 지원하는 데이터 형식을 확장하기 위해 편리한 플러그인 메커니즘을 제공합니다. 예를 들어 HDF4, HDF5, NetCDF, File GDB, Postgre, Oralce 등입니다.GDAL은 플러그인을 통해 적절한 형식을 지원할 수 있습니다.최근에 플러그인으로 컴파일할 수 있는 모든 형식을 플러그인으로 컴파일했습니다. 이렇게 하면 발표할 때 일부 사용하지 않는 데이터 형식은 대응하는 플러그인과 이래의 dll를 넣지 않고 설치 패키지의 부피를 줄일 수 있습니다.
HDF4, HDF5와 NetCDF 세 개를 플러그인으로 컴파일한 후에 몇 가지 문제가 발생할 수 있다. 예를 들어 HDF4와 HDF5의 데이터를 열 수 있지만 안의 하위 데이터 집합을 열 수 없다. 한참을 찾아서야 GDAL의 플러그인 메커니즘이 약간 부족하다는 것을 발견했다(작은 문제).
스스로 연구한 결과 GDAL의 플러그인 메커니즘은 이렇다. 코드를 결합해서 보면 파일 gcore/gdaldriver 관리자에 있다.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를 뺀 다음 함수 GDALRegisterXXX, 플러그인 dll에서 이 함수 찾기 GDALRegisterXXX 포인터, 찾지 못하면 GDAL RegisterMe 함수를 찾습니다.그리고 이 함수를 호출하여 등록합니다.OGR에 대한 플러그인도 유사합니다.
일반적인 플러그인의 경우 내보낸 함수 중 GDALRegister처음 함수는 하나뿐입니다. 플러그인에 GDAL Register가 두 개 있으면함수, 그럼 큰일이다. 플러그인은 플러그인 이름과 같은 하나만 등록할 수 있고 다른 하나는 등록하지 않아서 대응하는 데이터도 열리지 않을 것이다.HDF4, HDF5 및 NetCDF가 이러한 예외입니다.HDF4와 HDF5 플러그인에는 2개의 GDAL Register가 포함되어 있습니다함수, 하나는 데이터를 열기 위한 것이고, 다른 하나는 하위 데이터를 열기 위한 것이다. 즉 GDAL RegisterHDF4 및 GDALRegisterHDF4Image 및 GDALRegisterHDF5 및 GDALRegisterHDF5Image.이렇게 되면 플러그인 방식에 있어서 이 두 개의 뒤에 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 );
}

좋은 웹페이지 즐겨찾기