LCOV - code coverage report
Current view: top level - apps - gdal_create.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 198 253 78.3 %
Date: 2025-07-09 17:50:03 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL Utilities
       4             :  * Purpose:  GDAL Raster creation utility
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2020, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_string.h"
      14             : #include "gdal_version.h"
      15             : #include "gdal_priv.h"
      16             : #include "gdal.h"
      17             : #include "commonutils.h"
      18             : #include "gdalargumentparser.h"
      19             : #include "ogr_spatialref.h"
      20             : 
      21             : #include <cstdlib>
      22             : #include <memory>
      23             : #include <vector>
      24             : 
      25             : /**
      26             :  * @brief Makes sure the GDAL library is properly cleaned up before exiting.
      27             :  * @param nCode exit code
      28             :  * @todo Move to API
      29             :  */
      30           3 : static void GDALExit(int nCode)
      31             : {
      32           3 :     GDALDestroy();
      33           3 :     exit(nCode);
      34             : }
      35             : 
      36             : /************************************************************************/
      37             : /*                     GDALCreateOptions                               */
      38             : /************************************************************************/
      39             : 
      40             : struct GDALCreateOptions
      41             : {
      42             :     int nBandCount = -1;
      43             :     int nPixels = 0;
      44             :     bool bPixelsSet{false};
      45             :     int nLines = 0;
      46             :     GDALDataType eDT = GDT_Unknown;
      47             :     double dfULX = 0;
      48             :     double dfULY = 0;
      49             :     double dfLRX = 0;
      50             :     double dfLRY = 0;
      51             :     int nULCounter{0};
      52             :     bool bGeoTransform = false;
      53             :     std::string osOutputSRS;
      54             :     CPLStringList aosMetadata;
      55             :     std::vector<double> adfBurnValues;
      56             :     bool bQuiet = false;
      57             :     bool bSetNoData = false;
      58             :     std::string osNoData;
      59             :     std::string osOutputFilename;
      60             :     std::string osInputFilename;
      61             :     std::string osFormat;
      62             :     CPLStringList aosCreateOptions;
      63             : };
      64             : 
      65             : /************************************************************************/
      66             : /*                   GDALCreateAppOptionsGetParser()                   */
      67             : /************************************************************************/
      68             : 
      69             : static std::unique_ptr<GDALArgumentParser>
      70          10 : GDALCreateAppOptionsGetParser(GDALCreateOptions *psOptions)
      71             : {
      72             :     auto argParser = std::make_unique<GDALArgumentParser>(
      73          10 :         "gdal_create", /* bForBinary */ true);
      74             : 
      75          10 :     argParser->add_description(
      76          10 :         _("Create a raster file (without source dataset)."));
      77             : 
      78          10 :     argParser->add_epilog(_(
      79             :         "For more details, consult the full documentation for the gdal_create "
      80          10 :         "utility: http://gdal.org/gdal_create.html"));
      81             : 
      82          10 :     argParser->add_output_type_argument(psOptions->eDT);
      83             : 
      84          10 :     argParser->add_output_format_argument(psOptions->osFormat);
      85             : 
      86          10 :     argParser->add_argument("-outsize")
      87          20 :         .metavar("<xsize> <ysize>")
      88          10 :         .nargs(2)
      89          10 :         .scan<'i', int>()
      90             :         .action(
      91          24 :             [psOptions](const std::string &s)
      92             :             {
      93          12 :                 if (!psOptions->bPixelsSet)
      94             :                 {
      95           6 :                     psOptions->nPixels = atoi(s.c_str());
      96           6 :                     psOptions->bPixelsSet = true;
      97             :                 }
      98             :                 else
      99             :                 {
     100           6 :                     psOptions->nLines = atoi(s.c_str());
     101             :                 }
     102          10 :             })
     103          10 :         .help(_("Set the size of the output file."));
     104             : 
     105          10 :     argParser->add_argument("-bands")
     106          20 :         .metavar("<count>")
     107          10 :         .store_into(psOptions->nBandCount)
     108          10 :         .help(_("Set the number of bands in the output file."));
     109             : 
     110          20 :     argParser->add_argument("-burn").metavar("<value>").append().help(
     111             :         _("A fixed value to burn into a band. A list of "
     112          10 :           "-burn options can be supplied, one per band being written to."));
     113             : 
     114          10 :     argParser->add_argument("-a_srs")
     115          20 :         .metavar("<srs_def>")
     116          10 :         .store_into(psOptions->osOutputSRS)
     117          10 :         .help(_("Override the projection for the output file. "));
     118             : 
     119          10 :     argParser->add_argument("-a_ullr")
     120          20 :         .metavar("<ulx> <uly> <lrx> <lry>")
     121          10 :         .scan<'g', double>()
     122          10 :         .nargs(4)
     123             :         .action(
     124          27 :             [psOptions](const std::string &s)
     125             :             {
     126          12 :                 switch (psOptions->nULCounter++)
     127             :                 {
     128           3 :                     case 0:
     129           3 :                         psOptions->bGeoTransform = true;
     130           3 :                         psOptions->dfULX = CPLAtofM(s.c_str());
     131           3 :                         break;
     132           3 :                     case 1:
     133           3 :                         psOptions->dfULY = CPLAtofM(s.c_str());
     134           3 :                         break;
     135           3 :                     case 2:
     136           3 :                         psOptions->dfLRX = CPLAtofM(s.c_str());
     137           3 :                         break;
     138           3 :                     case 3:
     139           3 :                         psOptions->dfLRY = CPLAtof(s.c_str());
     140           3 :                         break;
     141             :                 }
     142          10 :             })
     143          10 :         .help(_("Assign the georeferenced bounds of the output file. "));
     144             : 
     145          10 :     argParser->add_argument("-a_nodata")
     146          20 :         .metavar("<value>")
     147          10 :         .scan<'g', double>()
     148             :         .action(
     149           4 :             [psOptions](const std::string &s)
     150             :             {
     151           4 :                 psOptions->bSetNoData = true;
     152           4 :                 psOptions->osNoData = s;
     153          10 :             })
     154          10 :         .help(_("Assign a specified nodata value to output bands."));
     155             : 
     156          10 :     argParser->add_metadata_item_options_argument(psOptions->aosMetadata);
     157             : 
     158          10 :     argParser->add_creation_options_argument(psOptions->aosCreateOptions);
     159             : 
     160          10 :     argParser->add_quiet_argument(&psOptions->bQuiet);
     161             : 
     162          10 :     argParser->add_argument("-if")
     163          20 :         .metavar("<input_dataset>")
     164          10 :         .store_into(psOptions->osInputFilename)
     165             :         .help(_("Name of GDAL input dataset that serves as a template for "
     166             :                 "default values of options -outsize, -bands, -ot, -a_srs, "
     167          10 :                 "-a_ullr and -a_nodata."));
     168             : 
     169          10 :     argParser->add_argument("out_dataset")
     170          20 :         .metavar("<out_dataset>")
     171          10 :         .store_into(psOptions->osOutputFilename)
     172          10 :         .help(_("Name of the output dataset to create."));
     173             : 
     174          10 :     return argParser;
     175             : }
     176             : 
     177             : /************************************************************************/
     178             : /*                                main()                                */
     179             : /************************************************************************/
     180             : 
     181          11 : MAIN_START(argc, argv)
     182             : 
     183             : {
     184             :     /* Check strict compilation and runtime library version as we use C++ API */
     185          11 :     if (!GDAL_CHECK_VERSION(argv[0]))
     186           0 :         exit(1);
     187             : 
     188          11 :     EarlySetConfigOptions(argc, argv);
     189             : 
     190             :     /* -------------------------------------------------------------------- */
     191             :     /*      Register standard GDAL drivers, and process generic GDAL        */
     192             :     /*      command options.                                                */
     193             :     /* -------------------------------------------------------------------- */
     194          11 :     GDALAllRegister();
     195             : 
     196          11 :     argc = GDALGeneralCmdLineProcessor(argc, &argv, 0);
     197          11 :     if (argc < 1)
     198           1 :         GDALExit(-argc);
     199             : 
     200          10 :     if (argc < 2)
     201             :     {
     202             :         try
     203             :         {
     204           0 :             GDALCreateOptions sOptions;
     205           0 :             auto argParser = GDALCreateAppOptionsGetParser(&sOptions);
     206           0 :             fprintf(stderr, "%s\n", argParser->usage().c_str());
     207             :         }
     208           0 :         catch (const std::exception &err)
     209             :         {
     210           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
     211           0 :                      err.what());
     212             :         }
     213           0 :         CSLDestroy(argv);
     214           0 :         GDALExit(1);
     215             :     }
     216             : 
     217          18 :     GDALCreateOptions sOptions;
     218             : 
     219          18 :     CPLStringList aosArgv;
     220         111 :     for (int iArg = 1; iArg < argc; iArg++)
     221             :     {
     222         101 :         if (iArg + 1 < argc && EQUAL(argv[iArg], "-burn"))
     223             :         {
     224           4 :             ++iArg;
     225             :             while (true)
     226             :             {
     227           5 :                 if (strchr(argv[iArg], ' '))
     228             :                 {
     229             :                     const CPLStringList aosTokens(
     230           2 :                         CSLTokenizeString(argv[iArg]));
     231           3 :                     for (int i = 0; i < aosTokens.size(); i++)
     232             :                     {
     233           2 :                         char *endptr = nullptr;
     234           2 :                         sOptions.adfBurnValues.push_back(
     235           2 :                             CPLStrtodM(aosTokens[i], &endptr));
     236           2 :                         if (endptr != aosTokens[i] + strlen(aosTokens[i]))
     237             :                         {
     238           0 :                             fprintf(stderr, "Invalid value for -burn\n");
     239           0 :                             CSLDestroy(argv);
     240           0 :                             GDALExit(1);
     241             :                         }
     242             :                     }
     243             :                 }
     244             :                 else
     245             :                 {
     246           4 :                     char *endptr = nullptr;
     247           4 :                     sOptions.adfBurnValues.push_back(
     248           4 :                         CPLStrtodM(argv[iArg], &endptr));
     249           4 :                     if (endptr != argv[iArg] + strlen(argv[iArg]))
     250             :                     {
     251           0 :                         fprintf(stderr, "Invalid value for -burn\n");
     252           0 :                         CSLDestroy(argv);
     253           0 :                         GDALExit(1);
     254             :                     }
     255             :                 }
     256          10 :                 if (iArg + 1 < argc &&
     257           5 :                     CPLGetValueType(argv[iArg + 1]) != CPL_VALUE_STRING)
     258             :                 {
     259           1 :                     ++iArg;
     260             :                 }
     261             :                 else
     262             :                 {
     263           4 :                     break;
     264             :                 }
     265           5 :             }
     266             :         }
     267             :         else
     268             :         {
     269          97 :             aosArgv.AddString(argv[iArg]);
     270             :         }
     271             :     }
     272          10 :     CSLDestroy(argv);
     273             : 
     274             :     try
     275             :     {
     276             : 
     277          20 :         auto argParser = GDALCreateAppOptionsGetParser(&sOptions);
     278          10 :         argParser->parse_args_without_binary_name(aosArgv.List());
     279             :     }
     280           0 :     catch (const std::exception &error)
     281             :     {
     282           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", error.what());
     283           0 :         GDALExit(1);
     284             :     }
     285             : 
     286          10 :     GDALGeoTransform gt;
     287          10 :     if (sOptions.bGeoTransform && sOptions.nPixels > 0 && sOptions.nLines > 0)
     288             :     {
     289           3 :         gt[0] = sOptions.dfULX;
     290           3 :         gt[1] = (sOptions.dfLRX - sOptions.dfULX) / sOptions.nPixels;
     291           3 :         gt[2] = 0;
     292           3 :         gt[3] = sOptions.dfULY;
     293           3 :         gt[4] = 0;
     294           3 :         gt[5] = (sOptions.dfLRY - sOptions.dfULY) / sOptions.nLines;
     295             :     }
     296             : 
     297           0 :     std::unique_ptr<GDALDataset> poInputDS;
     298          10 :     if (!sOptions.osInputFilename.empty())
     299             :     {
     300           4 :         poInputDS.reset(
     301             :             GDALDataset::Open(sOptions.osInputFilename.c_str(),
     302             :                               GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
     303           4 :         if (poInputDS == nullptr)
     304             :         {
     305           1 :             GDALExit(1);
     306             :         }
     307           3 :         if (sOptions.nPixels == 0)
     308             :         {
     309           2 :             sOptions.nPixels = poInputDS->GetRasterXSize();
     310           2 :             sOptions.nLines = poInputDS->GetRasterYSize();
     311             :         }
     312           3 :         if (sOptions.nBandCount < 0)
     313             :         {
     314           2 :             sOptions.nBandCount = poInputDS->GetRasterCount();
     315             :         }
     316           3 :         if (sOptions.eDT == GDT_Unknown && poInputDS->GetRasterCount() > 0)
     317             :         {
     318           3 :             sOptions.eDT = poInputDS->GetRasterBand(1)->GetRasterDataType();
     319             :         }
     320           3 :         if (sOptions.osOutputSRS.empty())
     321             :         {
     322           3 :             sOptions.osOutputSRS = poInputDS->GetProjectionRef();
     323             :         }
     324           3 :         if (!(sOptions.bGeoTransform && sOptions.nPixels > 0 &&
     325           0 :               sOptions.nLines > 0))
     326             :         {
     327           3 :             if (poInputDS->GetGeoTransform(gt) == CE_None)
     328             :             {
     329           2 :                 sOptions.bGeoTransform = true;
     330             :             }
     331             :         }
     332           3 :         if (!sOptions.bSetNoData && poInputDS->GetRasterCount() > 0)
     333             :         {
     334           2 :             if (sOptions.eDT == GDT_Int64)
     335             :             {
     336             :                 int noData;
     337             :                 const auto nNoDataValue =
     338           0 :                     poInputDS->GetRasterBand(1)->GetNoDataValueAsInt64(&noData);
     339           0 :                 sOptions.bSetNoData = noData;
     340           0 :                 if (sOptions.bSetNoData)
     341             :                     sOptions.osNoData = CPLSPrintf(
     342           0 :                         CPL_FRMT_GIB, static_cast<GIntBig>(nNoDataValue));
     343             :             }
     344           2 :             else if (sOptions.eDT == GDT_UInt64)
     345             :             {
     346             :                 int noData;
     347             :                 const auto nNoDataValue =
     348           0 :                     poInputDS->GetRasterBand(1)->GetNoDataValueAsUInt64(
     349           0 :                         &noData);
     350           0 :                 sOptions.bSetNoData = noData;
     351           0 :                 if (sOptions.bSetNoData)
     352             :                     sOptions.osNoData = CPLSPrintf(
     353           0 :                         CPL_FRMT_GUIB, static_cast<GUIntBig>(nNoDataValue));
     354             :             }
     355             :             else
     356             :             {
     357             :                 int noData;
     358             :                 const double dfNoDataValue =
     359           2 :                     poInputDS->GetRasterBand(1)->GetNoDataValue(&noData);
     360           2 :                 sOptions.bSetNoData = noData;
     361           2 :                 if (sOptions.bSetNoData)
     362           0 :                     sOptions.osNoData = CPLSPrintf("%.18g", dfNoDataValue);
     363             :             }
     364             :         }
     365             :     }
     366             : 
     367          18 :     GDALDriverH hDriver = GDALGetDriverByName(
     368           9 :         sOptions.osFormat.empty()
     369          16 :             ? GetOutputDriverForRaster(sOptions.osOutputFilename.c_str())
     370           7 :                   .c_str()
     371           2 :             : sOptions.osFormat.c_str());
     372             : 
     373           9 :     if (hDriver == nullptr)
     374             :     {
     375           0 :         fprintf(stderr, "Output driver not found.\n");
     376           0 :         GDALExit(1);
     377             :     }
     378             :     const bool bHasCreate =
     379           9 :         GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, nullptr) != nullptr;
     380          11 :     if (!bHasCreate &&
     381           2 :         GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATECOPY, nullptr) == nullptr)
     382             :     {
     383           1 :         fprintf(stderr, "This driver has no creation capabilities.\n");
     384           1 :         GDALExit(1);
     385             :     }
     386           8 :     GDALDriverH hTmpDriver = GDALGetDriverByName("MEM");
     387           8 :     if (!bHasCreate && hTmpDriver == nullptr)
     388             :     {
     389           0 :         fprintf(stderr, "MEM driver not available.\n");
     390           0 :         GDALExit(1);
     391             :     }
     392             : 
     393           8 :     if (sOptions.nPixels != 0 && sOptions.eDT == GDT_Unknown)
     394             :     {
     395           1 :         sOptions.eDT = GDT_Byte;
     396             :     }
     397           8 :     if (sOptions.nBandCount < 0)
     398             :     {
     399           2 :         sOptions.nBandCount = sOptions.eDT == GDT_Unknown ? 0 : 1;
     400             :     }
     401          15 :     GDALDatasetH hDS = GDALCreate(
     402             :         bHasCreate ? hDriver : hTmpDriver, sOptions.osOutputFilename.c_str(),
     403             :         sOptions.nPixels, sOptions.nLines, sOptions.nBandCount, sOptions.eDT,
     404           7 :         bHasCreate ? sOptions.aosCreateOptions.List() : nullptr);
     405             : 
     406           8 :     if (hDS == nullptr)
     407             :     {
     408           0 :         GDALExit(1);
     409             :     }
     410             : 
     411          13 :     if (!sOptions.osOutputSRS.empty() &&
     412           5 :         !EQUAL(sOptions.osOutputSRS.c_str(), "NONE"))
     413             :     {
     414          10 :         OGRSpatialReference oSRS;
     415           5 :         oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     416             : 
     417           5 :         if (oSRS.SetFromUserInput(sOptions.osOutputSRS.c_str()) != OGRERR_NONE)
     418             :         {
     419           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     420             :                      "Failed to process SRS definition: %s",
     421             :                      sOptions.osOutputSRS.c_str());
     422           0 :             GDALExit(1);
     423             :         }
     424             : 
     425           5 :         char *pszSRS = nullptr;
     426           5 :         oSRS.exportToWkt(&pszSRS);
     427             : 
     428           5 :         if (GDALSetProjection(hDS, pszSRS) != CE_None)
     429             :         {
     430           0 :             CPLFree(pszSRS);
     431           0 :             GDALClose(hDS);
     432           0 :             GDALExit(1);
     433             :         }
     434           5 :         CPLFree(pszSRS);
     435             :     }
     436           8 :     if (sOptions.bGeoTransform)
     437             :     {
     438           5 :         if (sOptions.nPixels == 0)
     439             :         {
     440           0 :             fprintf(stderr,
     441             :                     "-outsize must be specified when -a_ullr is used.\n");
     442           0 :             GDALClose(hDS);
     443           0 :             GDALExit(1);
     444             :         }
     445           5 :         if (GDALDataset::FromHandle(hDS)->SetGeoTransform(gt) != CE_None)
     446             :         {
     447           0 :             GDALClose(hDS);
     448           0 :             GDALExit(1);
     449             :         }
     450             :     }
     451           3 :     else if (poInputDS && poInputDS->GetGCPCount() > 0)
     452             :     {
     453           2 :         GDALDataset::FromHandle(hDS)->SetGCPs(poInputDS->GetGCPCount(),
     454           1 :                                               poInputDS->GetGCPs(),
     455           1 :                                               poInputDS->GetGCPSpatialRef());
     456             :     }
     457             : 
     458           8 :     if (!sOptions.aosMetadata.empty())
     459             :     {
     460           3 :         GDALSetMetadata(hDS, sOptions.aosMetadata.List(), nullptr);
     461             :     }
     462           8 :     const int nBands = GDALGetRasterCount(hDS);
     463           8 :     if (sOptions.bSetNoData)
     464             :     {
     465          15 :         for (int i = 0; i < nBands; i++)
     466             :         {
     467          11 :             auto hBand = GDALGetRasterBand(hDS, i + 1);
     468          11 :             if (sOptions.eDT == GDT_Int64)
     469             :             {
     470           0 :                 GDALSetRasterNoDataValueAsInt64(
     471           0 :                     hBand, static_cast<int64_t>(std::strtoll(
     472             :                                sOptions.osNoData.c_str(), nullptr, 10)));
     473             :             }
     474          11 :             else if (sOptions.eDT == GDT_UInt64)
     475             :             {
     476           0 :                 GDALSetRasterNoDataValueAsUInt64(
     477           0 :                     hBand, static_cast<uint64_t>(std::strtoull(
     478             :                                sOptions.osNoData.c_str(), nullptr, 10)));
     479             :             }
     480             :             else
     481             :             {
     482          11 :                 GDALSetRasterNoDataValue(hBand,
     483             :                                          CPLAtofM(sOptions.osNoData.c_str()));
     484             :             }
     485             :         }
     486             :     }
     487           8 :     if (!sOptions.adfBurnValues.empty())
     488             :     {
     489          12 :         for (int i = 0; i < nBands; i++)
     490             :         {
     491           9 :             GDALFillRaster(GDALGetRasterBand(hDS, i + 1),
     492           9 :                            i < static_cast<int>(sOptions.adfBurnValues.size())
     493           6 :                                ? sOptions.adfBurnValues[i]
     494           3 :                                : sOptions.adfBurnValues.back(),
     495             :                            0);
     496             :         }
     497             :     }
     498             : 
     499           8 :     bool bHasGotErr = false;
     500           8 :     if (!bHasCreate)
     501             :     {
     502           2 :         GDALDatasetH hOutDS = GDALCreateCopy(
     503             :             hDriver, sOptions.osOutputFilename.c_str(), hDS, false,
     504           1 :             sOptions.aosCreateOptions.List(),
     505           1 :             sOptions.bQuiet ? GDALDummyProgress : GDALTermProgress, nullptr);
     506           1 :         if (hOutDS == nullptr)
     507             :         {
     508           0 :             GDALClose(hDS);
     509           0 :             GDALExit(1);
     510             :         }
     511           1 :         if (GDALClose(hOutDS) != CE_None)
     512             :         {
     513           0 :             bHasGotErr = true;
     514             :         }
     515             :     }
     516             : 
     517           8 :     const bool bWasFailureBefore = (CPLGetLastErrorType() == CE_Failure);
     518           8 :     if (GDALClose(hDS) != CE_None)
     519           0 :         bHasGotErr = true;
     520           8 :     if (!bWasFailureBefore && CPLGetLastErrorType() == CE_Failure)
     521             :     {
     522           0 :         bHasGotErr = true;
     523             :     }
     524             : 
     525           8 :     return bHasGotErr ? 1 : 0;
     526             : }
     527             : 
     528           0 : MAIN_END

Generated by: LCOV version 1.14