gdalbuildvrt.cpp
上传用户:cjj257
上传日期:2021-07-26
资源大小:7302k
文件大小:20k
源码类别:

Windows编程

开发平台:

Visual C++

  1. /******************************************************************************
  2.  * $Id: gdalbuildvrt.cpp rouault $
  3.  *
  4.  * Project:  GDAL Utilities
  5.  * Purpose:  Commandline application to build VRT datasets from raster products or content of SHP tile index
  6.  * Author:   Even Rouault, even.rouault at mines-paris.org
  7.  *
  8.  ******************************************************************************
  9.  * Copyright (c) 2007, Even Rouault
  10.  *
  11.  * Permission is hereby granted, free of charge, to any person obtaining a
  12.  * copy of this software and associated documentation files (the "Software"),
  13.  * to deal in the Software without restriction, including without limitation
  14.  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  15.  * and/or sell copies of the Software, and to permit persons to whom the
  16.  * Software is furnished to do so, subject to the following conditions:
  17.  *
  18.  * The above copyright notice and this permission notice shall be included
  19.  * in all copies or substantial portions of the Software.
  20.  *
  21.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  22.  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  24.  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  26.  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  27.  * DEALINGS IN THE SOFTWARE.
  28.  ****************************************************************************/
  29. #include "gdal_priv.h"
  30. #include "cpl_string.h"
  31. #include "ogrsf_frmts/shape/shapefil.h"
  32. #include "vrt/vrtdataset.h"
  33. CPL_CVSID("$Id: gdalbuildvrt.cpp rouault $");
  34. #define GEOTRSFRM_TOPLEFT_X            0
  35. #define GEOTRSFRM_WE_RES               1
  36. #define GEOTRSFRM_ROTATION_PARAM1      2
  37. #define GEOTRSFRM_TOPLEFT_Y            3
  38. #define GEOTRSFRM_ROTATION_PARAM2      4
  39. #define GEOTRSFRM_NS_RES               5
  40. typedef enum
  41. {
  42.     LOWEST_RESOLUTION,
  43.     HIGHEST_RESOLUTION,
  44.     AVERAGE_RESOLUTION
  45. } ResolutionStrategy;
  46. typedef struct
  47. {
  48.     int                    rasterXSize;
  49.     int                    rasterYSize;
  50. } RasterSize;
  51. typedef struct
  52. {
  53.     GDALColorInterp        colorInterpretation;
  54.     GDALDataType           dataType;
  55.     GDALColorTableH        colorTable;
  56.     int                    bHasNoData;
  57.     double                 noDataValue;
  58. } BandProperty;
  59. /************************************************************************/
  60. /*                               Usage()                                */
  61. /************************************************************************/
  62. static void Usage()
  63. {
  64.     fprintf(stdout, "%s", 
  65.             "Usage: gdalbuildvrt [-tileindex field_name] [-resolution {highest,lowest,average}]n"
  66.             "                     output.vrt {[inputfile]* | -input_file_list my_liste.txt}n"
  67.             "n"
  68.             "eg.n"
  69.             "  % gdalbuildvrt doq_index.vrt doq/*.tifn"
  70.             "  % gdalbuildvrt -input_file_list my_liste.txt doq_index.vrtn"
  71.             "n"
  72.             "NOTES:n"
  73.             "  o The default tile index field is 'location' unless otherwise specified by -tileindex.n"
  74.             "  o In case the resolution of all input files is not the same, the -resolution flag.n"
  75.             "    enable the user to control the way the output resolution is computed. average is the default.n"
  76.             "  o Input files may be any valid GDAL dataset or a GDAL raster tile index.n"
  77.             "  o For a GDAL raster tile index, all entries will be added to the VRT.n"
  78.             "  o If one GDAL dataset is made of several subdatasets, they will be added to the VRT "
  79.             "    rather than the dataset itself.n"
  80.             "  o Only datasets of same projection and band characteristics may be added to the VRT.n");
  81.     exit( 1 );
  82. }
  83. void build_vrt(const char* pszOutputFilename,
  84.                int* pnInputFiles, char*** pppszInputFilenames,
  85.                ResolutionStrategy resolutionStrategy)
  86. {
  87.     char* projectionRef = NULL;
  88.     RasterSize* rasterSizes;
  89.     double* adfGeoTransforms;
  90.     int nBands = 0;
  91.     BandProperty* bandProperties = NULL;
  92.     int index = 0;
  93.     double minX = 0, minY = 0, maxX = 0, maxY = 0;
  94.     int i,j;
  95.     int* isFileOk;
  96.     double we_res = 0;
  97.     double ns_res = 0;
  98.     VRTDataset* ds;
  99.     int rasterXSize;
  100.     int rasterYSize;
  101.     
  102.     char** ppszInputFilenames = *pppszInputFilenames;
  103.     int nInputFiles = *pnInputFiles;
  104.     
  105.     rasterSizes = (RasterSize*)CPLMalloc(nInputFiles*sizeof(RasterSize));
  106.     adfGeoTransforms = (double*)CPLMalloc(nInputFiles*6*sizeof(double));
  107.     isFileOk = (int*)CPLMalloc(nInputFiles*sizeof(int));
  108.     for(i=0;i<nInputFiles;i++)
  109.     {
  110.         const char* dsFileName = ppszInputFilenames[i];
  111.         GDALTermProgress( 1.0 * (i+1) / nInputFiles, NULL, NULL);
  112.         GDALDatasetH hDS = GDALOpen(ppszInputFilenames[i], GA_ReadOnly );
  113.         isFileOk[i] = 0;
  114.         if (hDS)
  115.         {
  116.             char** papszMetadata = GDALGetMetadata( hDS, "SUBDATASETS" );
  117.             if( CSLCount(papszMetadata) > 0 )
  118.             {
  119.                 rasterSizes = (RasterSize*)CPLRealloc(rasterSizes,
  120.                                (nInputFiles+CSLCount(papszMetadata))*sizeof(RasterSize));
  121.                 adfGeoTransforms = (double*)CPLRealloc(adfGeoTransforms,
  122.                                     (nInputFiles+CSLCount(papszMetadata))*6*sizeof(double));
  123.                 isFileOk = (int*)CPLRealloc(isFileOk,
  124.                             (nInputFiles+CSLCount(papszMetadata))*sizeof(int));
  125.                 ppszInputFilenames = (char**)CPLRealloc(ppszInputFilenames,
  126.                                         sizeof(char*) * (nInputFiles+CSLCount(papszMetadata)));
  127.                 int count = 1;
  128.                 char subdatasetNameKey[256];
  129.                 sprintf(subdatasetNameKey, "SUBDATASET_%d_NAME", count);
  130.                 while(*papszMetadata != NULL)
  131.                 {
  132.                     if (EQUALN(*papszMetadata, subdatasetNameKey, strlen(subdatasetNameKey)))
  133.                     {
  134.                         ppszInputFilenames[nInputFiles++] = CPLStrdup(*papszMetadata+strlen(subdatasetNameKey)+1);
  135.                         count++;
  136.                         sprintf(subdatasetNameKey, "SUBDATASET_%d_NAME", count);
  137.                     }
  138.                     papszMetadata++;
  139.                 }
  140.                 GDALClose(hDS);
  141.                 continue;
  142.             }
  143.             const char* proj = GDALGetProjectionRef(hDS);
  144.             GDALGetGeoTransform(hDS, adfGeoTransforms + index * 6);
  145.             if (adfGeoTransforms[index * 6 + GEOTRSFRM_ROTATION_PARAM1] != 0 ||
  146.                 adfGeoTransforms[index * 6 + GEOTRSFRM_ROTATION_PARAM2] != 0)
  147.             {
  148.                 fprintf( stderr, "gdalbuildvrt does not support rotated geo transforms. Skipping %sn",
  149.                              dsFileName);
  150.                 GDALClose(hDS);
  151.                 continue;
  152.             }
  153.             if (adfGeoTransforms[index * 6 + GEOTRSFRM_NS_RES] >= 0)
  154.             {
  155.                 fprintf( stderr, "gdalbuildvrt does not support positive NS resolution. Skipping %sn",
  156.                              dsFileName);
  157.                 GDALClose(hDS);
  158.                 continue;
  159.             }
  160.             rasterSizes[index].rasterXSize = GDALGetRasterXSize(hDS);
  161.             rasterSizes[index].rasterYSize = GDALGetRasterYSize(hDS);
  162.             double product_minX = adfGeoTransforms[index * 6 + GEOTRSFRM_TOPLEFT_X];
  163.             double product_maxY = adfGeoTransforms[index * 6 + GEOTRSFRM_TOPLEFT_Y];
  164.             double product_maxX = product_minX + rasterSizes[index].rasterXSize * adfGeoTransforms[index * 6 + GEOTRSFRM_WE_RES];
  165.             double product_minY = product_maxY + rasterSizes[index].rasterYSize * adfGeoTransforms[index * 6 + GEOTRSFRM_NS_RES];
  166.             
  167.             if (index == 0)
  168.             {
  169.                 if (proj)
  170.                     projectionRef = CPLStrdup(proj);
  171.                 minX = product_minX;
  172.                 minY = product_minY;
  173.                 maxX = product_maxX;
  174.                 maxY = product_maxY;
  175.                 nBands = GDALGetRasterCount(hDS);
  176.                 bandProperties = (BandProperty*)CPLMalloc(nBands*sizeof(BandProperty));
  177.                 for(j=0;j<nBands;j++)
  178.                 {
  179.                     GDALRasterBandH hRasterBand = GDALGetRasterBand( hDS, j+1 );
  180.                     bandProperties[j].colorInterpretation = GDALGetRasterColorInterpretation(hRasterBand);
  181.                     bandProperties[j].dataType = GDALGetRasterDataType(hRasterBand);
  182.                     if (bandProperties[j].colorInterpretation == GCI_PaletteIndex)
  183.                     {
  184.                         bandProperties[j].colorTable = GDALGetRasterColorTable( hRasterBand );
  185.                         if (bandProperties[j].colorTable)
  186.                         {
  187.                             bandProperties[j].colorTable = GDALCloneColorTable(bandProperties[j].colorTable);
  188.                         }
  189.                     }
  190.                     else
  191.                         bandProperties[j].colorTable = 0;
  192.                     bandProperties[j].noDataValue = GDALGetRasterNoDataValue(hRasterBand, &bandProperties[j].bHasNoData);
  193.                 }
  194.             }
  195.             else
  196.             {
  197.                 if (proj != NULL && projectionRef == NULL ||
  198.                     proj == NULL && projectionRef != NULL ||
  199.                     (proj != NULL && projectionRef != NULL && EQUAL(proj, projectionRef) == FALSE))
  200.                 {
  201.                     fprintf( stderr, "gdalbuildvrt does not support heterogenous projection. Skipping %sn",
  202.                              dsFileName);
  203.                     GDALClose(hDS);
  204.                     continue;
  205.                 }
  206.                 int _nBands = GDALGetRasterCount(hDS);
  207.                 if (nBands != _nBands)
  208.                 {
  209.                     fprintf( stderr, "gdalbuildvrt does not support heterogenous band numbers. Skipping %sn",
  210.                              dsFileName);
  211.                     GDALClose(hDS);
  212.                     continue;
  213.                 }
  214.                 for(j=0;j<nBands;j++)
  215.                 {
  216.                     GDALRasterBandH hRasterBand = GDALGetRasterBand( hDS, j+1 );
  217.                     if (bandProperties[j].colorInterpretation != GDALGetRasterColorInterpretation(hRasterBand) ||
  218.                         bandProperties[j].dataType != GDALGetRasterDataType(hRasterBand))
  219.                     {
  220.                         fprintf( stderr, "gdalbuildvrt does not support heterogenous band characteristics. Skipping %sn",
  221.                              dsFileName);
  222.                         GDALClose(hDS);
  223.                     }
  224.                     if (bandProperties[j].colorTable)
  225.                     {
  226.                         GDALColorTableH colorTable = GDALGetRasterColorTable( hRasterBand );
  227.                         if (colorTable == NULL ||
  228.                             GDALGetColorEntryCount(colorTable) != GDALGetColorEntryCount(bandProperties[j].colorTable))
  229.                         {
  230.                             fprintf( stderr, "gdalbuildvrt does not support heterogenous band characteristics. Skipping %sn",
  231.                              dsFileName);
  232.                             GDALClose(hDS);
  233.                             break;
  234.                         }
  235.                         /* We should check that the palette are the same too ! */
  236.                     }
  237.                 }
  238.                 if (j != nBands)
  239.                     continue;
  240.                 if (product_minX < minX) minX = product_minX;
  241.                 if (product_minY < minY) minY = product_minY;
  242.                 if (product_maxX > maxX) maxX = product_maxX;
  243.                 if (product_maxY > maxY) maxY = product_maxY;
  244.             }
  245.             if (resolutionStrategy == AVERAGE_RESOLUTION)
  246.             {
  247.                 we_res += adfGeoTransforms[index * 6 + GEOTRSFRM_WE_RES];
  248.                 ns_res += adfGeoTransforms[index * 6 + GEOTRSFRM_NS_RES];
  249.             }
  250.             else
  251.             {
  252.                 if (index == 0)
  253.                 {
  254.                     we_res = adfGeoTransforms[index * 6 + GEOTRSFRM_WE_RES];
  255.                     ns_res = adfGeoTransforms[index * 6 + GEOTRSFRM_NS_RES];
  256.                 }
  257.                 else if (resolutionStrategy == HIGHEST_RESOLUTION)
  258.                 {
  259.                     we_res = MIN(we_res, adfGeoTransforms[index * 6 + GEOTRSFRM_WE_RES]);
  260.                     ns_res = MIN(ns_res, adfGeoTransforms[index * 6 + GEOTRSFRM_NS_RES]);
  261.                 }
  262.                 else
  263.                 {
  264.                     we_res = MAX(we_res, adfGeoTransforms[index * 6 + GEOTRSFRM_WE_RES]);
  265.                     ns_res = MAX(ns_res, adfGeoTransforms[index * 6 + GEOTRSFRM_NS_RES]);
  266.                 }
  267.             }
  268.             isFileOk[i] = 1;
  269.             index++;
  270.             GDALClose(hDS);
  271.         }
  272.         else
  273.         {
  274.             fprintf( stderr, "Warning : can't open %s. Skipping itn", dsFileName);
  275.         }
  276.     }
  277.     
  278.     *pppszInputFilenames = ppszInputFilenames;
  279.     *pnInputFiles = nInputFiles;
  280.     
  281.     if (index == 0)
  282.         goto end;
  283.     
  284.     if (resolutionStrategy == AVERAGE_RESOLUTION)
  285.     {
  286.         we_res /= index;
  287.         ns_res /= index;
  288.     }
  289.     
  290.     rasterXSize = (int)(0.5 + (maxX - minX) / we_res);
  291.     rasterYSize = (int)(0.5 + (maxY - minY) / -ns_res);
  292.     
  293.     ds = new VRTDataset(rasterXSize, rasterYSize);
  294.     ds->SetDescription(pszOutputFilename);
  295.     
  296.     if (projectionRef)
  297.     {
  298.         ds->SetProjection(projectionRef);
  299.     }
  300.     double adfGeoTransform[6];
  301.     adfGeoTransform[GEOTRSFRM_TOPLEFT_X] = minX;
  302.     adfGeoTransform[GEOTRSFRM_WE_RES] = we_res;
  303.     adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] = 0;
  304.     adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = maxY;
  305.     adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] = 0;
  306.     adfGeoTransform[GEOTRSFRM_NS_RES] = ns_res;
  307.     ds->SetGeoTransform(adfGeoTransform);
  308.     
  309.     for(j=0;j<nBands;j++)
  310.     {
  311.         ds->AddBand(bandProperties[j].dataType, NULL);
  312.         ds->GetRasterBand(j+1)->SetColorInterpretation(bandProperties[j].colorInterpretation);
  313.         if (bandProperties[j].colorInterpretation == GCI_PaletteIndex)
  314.         {
  315.             ds->GetRasterBand(j+1)->SetColorTable((GDALColorTable*)bandProperties[j].colorTable);
  316.         }
  317.         if (bandProperties[j].bHasNoData)
  318.             ds->GetRasterBand(j+1)->SetNoDataValue(bandProperties[j].noDataValue);
  319.     }
  320.     for(i=0;i<nInputFiles;i++)
  321.     {
  322.         if (isFileOk[i] == 0)
  323.             continue;
  324.         const char* dsFileName = ppszInputFilenames[i];
  325.         GDALDatasetH hDS = GDALOpen(dsFileName, GA_ReadOnly ); 
  326.         int xoffset = (int)
  327.                 (0.5 + (adfGeoTransforms[i * 6 + GEOTRSFRM_TOPLEFT_X] - minX) / we_res);
  328.         int yoffset = (int)
  329.                 (0.5 + (maxY - adfGeoTransforms[i * 6 + GEOTRSFRM_TOPLEFT_Y]) / -ns_res);
  330.         int dest_width = (int)
  331.                 (0.5 + rasterSizes[i].rasterXSize * adfGeoTransforms[i * 6 + GEOTRSFRM_WE_RES] / we_res);
  332.         int dest_height = (int)
  333.                 (0.5 + rasterSizes[i].rasterYSize * adfGeoTransforms[i * 6 + GEOTRSFRM_NS_RES] / ns_res);
  334.         for(j=0;j<nBands;j++)
  335.         {
  336.             VRTSourcedRasterBand *poBand = (VRTSourcedRasterBand*)ds->GetRasterBand( j + 1 );
  337.             /* Place the raster band at the right position in the VRT */
  338.             poBand->AddSimpleSource((GDALRasterBand*)GDALGetRasterBand(hDS, j + 1),
  339.                                     0, 0, rasterSizes[i].rasterXSize, rasterSizes[i].rasterYSize,
  340.                                     xoffset, yoffset,
  341.                                     dest_width, dest_height);
  342.         }
  343.         GDALClose(hDS);
  344.     }
  345.     delete ds;
  346. end:
  347.     CPLFree(rasterSizes);
  348.     CPLFree(adfGeoTransforms);
  349.     for(j=0;j<nBands;j++)
  350.     {
  351.         GDALDestroyColorTable(bandProperties[j].colorTable);
  352.     }
  353.     CPLFree(bandProperties);
  354.     CPLFree(projectionRef);
  355.     CPLFree(isFileOk);
  356. }
  357. static void add_file_to_list(const char* filename, const char* tile_index,
  358.                              int* pnInputFiles, char*** pppszInputFilenames)
  359. {
  360.     SHPHandle   hSHP;
  361.     DBFHandle   hDBF;
  362.     int j, ti_field;
  363.     
  364.     int nInputFiles = *pnInputFiles;
  365.     char** ppszInputFilenames = *pppszInputFilenames;
  366.     
  367.     if (EQUAL(CPLGetExtension(filename), "SHP"))
  368.     {
  369.         /* Handle GDALTIndex Shapefile as a special case */
  370.         hSHP = SHPOpen( filename, "r" );
  371.         if( hSHP == NULL )
  372.         {
  373.             fprintf( stderr, "Unable to open shapefile `%s'.n", 
  374.                     filename );
  375.             exit(2);
  376.         }
  377.     
  378.         hDBF = DBFOpen( filename, "r" );
  379.         if( hDBF == NULL )
  380.         {
  381.             fprintf( stderr, "Unable to open DBF file `%s'.n", 
  382.                         filename );
  383.             exit(2);
  384.         }
  385.     
  386.         for( ti_field = 0; ti_field < DBFGetFieldCount(hDBF); ti_field++ )
  387.         {
  388.             char field_name[16];
  389.     
  390.             DBFGetFieldInfo( hDBF, ti_field, field_name, NULL, NULL );
  391.             if (strcmp(field_name, "LOCATION") == 0 && strcmp("LOCATION", tile_index) != 0 )
  392.             {
  393.                 fprintf( stderr, "This shapefile seems to be a tile index of "
  394.                                 "OGR features and not GDAL products.n");
  395.             }
  396.             if( strcmp(field_name, tile_index) == 0 )
  397.                 break;
  398.         }
  399.     
  400.         if( ti_field == DBFGetFieldCount(hDBF) )
  401.         {
  402.             fprintf( stderr, "Unable to find field `%s' in DBF file `%s'.n", 
  403.                     tile_index, filename );
  404.             return;
  405.         }
  406.     
  407.         /* Load in memory existing file names in SHP */
  408.         int nTileIndexFiles = DBFGetRecordCount(hDBF);
  409.         if (nTileIndexFiles == 0)
  410.         {
  411.             fprintf( stderr, "Tile index %s is empty. Skipping it.n", filename);
  412.             return;
  413.         }
  414.         
  415.         ppszInputFilenames = (char**)CPLRealloc(ppszInputFilenames, sizeof(char*) * (nInputFiles+nTileIndexFiles));
  416.         for(j=0;j<nTileIndexFiles;j++)
  417.         {
  418.             ppszInputFilenames[nInputFiles++] = CPLStrdup(DBFReadStringAttribute( hDBF, j, ti_field ));
  419.         }
  420.         DBFClose( hDBF );
  421.         SHPClose( hSHP );
  422.     }
  423.     else
  424.     {
  425.         ppszInputFilenames = (char**)CPLRealloc(ppszInputFilenames, sizeof(char*) * (nInputFiles+1));
  426.         ppszInputFilenames[nInputFiles++] = CPLStrdup(filename);
  427.     }
  428.     *pnInputFiles = nInputFiles;
  429.     *pppszInputFilenames = ppszInputFilenames;
  430. }
  431. /************************************************************************/
  432. /*                                main()                                */
  433. /************************************************************************/
  434. int main( int nArgc, char ** papszArgv )
  435. {
  436.     const char *tile_index = "location";
  437.     const char *resolution = "average";
  438.     int nInputFiles = 0;
  439.     char ** ppszInputFilenames = NULL;
  440.     const char * pszOutputFilename = NULL;
  441.     int i;
  442.     GDALAllRegister();
  443.     nArgc = GDALGeneralCmdLineProcessor( nArgc, &papszArgv, 0 );
  444.     if( nArgc < 1 )
  445.         exit( -nArgc );
  446. /* -------------------------------------------------------------------- */
  447. /*      Parse commandline.                                              */
  448. /* -------------------------------------------------------------------- */
  449.     for( int iArg = 1; iArg < nArgc; iArg++ )
  450.     {
  451.         if( strcmp(papszArgv[iArg],"-tileindex") == 0 )
  452.         {
  453.             tile_index = papszArgv[++iArg];
  454.         }
  455.         else if( strcmp(papszArgv[iArg],"-resolution") == 0 )
  456.         {
  457.             resolution = papszArgv[++iArg];
  458.         }
  459.         else if( strcmp(papszArgv[iArg],"-input_file_list") == 0 )
  460.         {
  461.             const char* input_file_list = papszArgv[++iArg];
  462.             FILE* f = VSIFOpen(input_file_list, "r");
  463.             if (f)
  464.             {
  465.                 while(1)
  466.                 {
  467.                     const char* filename = CPLReadLine(f);
  468.                     if (filename == NULL)
  469.                         break;
  470.                     add_file_to_list(filename, tile_index, &nInputFiles, &ppszInputFilenames);
  471.                 }
  472.                 VSIFClose(f);
  473.             }
  474.         }
  475.         else if( pszOutputFilename == NULL )
  476.             pszOutputFilename = papszArgv[iArg];
  477.         else
  478.         {
  479.             add_file_to_list(papszArgv[iArg], tile_index, &nInputFiles, &ppszInputFilenames);
  480.         }
  481.     }
  482.     if( pszOutputFilename == NULL || nInputFiles == 0 )
  483.         Usage();
  484.     
  485.     build_vrt(pszOutputFilename, &nInputFiles, &ppszInputFilenames,
  486.               EQUAL(resolution, "highest") ? HIGHEST_RESOLUTION :
  487.               EQUAL(resolution, "lowest") ? LOWEST_RESOLUTION : AVERAGE_RESOLUTION);
  488.     
  489.     for(i=0;i<nInputFiles;i++)
  490.     {
  491.         CPLFree(ppszInputFilenames[i]);
  492.     }
  493.     CPLFree(ppszInputFilenames);
  494.     CSLDestroy( papszArgv );
  495.     GDALDestroyDriverManager();
  496.     return 0;
  497. }