LCOV - code coverage report
Current view: top level - apps - gdal_create.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 199 255 78.0 %
Date: 2024-11-21 22:18:42 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           2 : static void GDALExit(int nCode)
      31             : {
      32           2 :     GDALDestroy();
      33           2 :     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          11 : GDALCreateAppOptionsGetParser(GDALCreateOptions *psOptions)
      71             : {
      72             :     auto argParser = std::make_unique<GDALArgumentParser>(
      73          11 :         "gdal_create", /* bForBinary */ true);
      74             : 
      75          11 :     argParser->add_description(
      76          11 :         _("Create a raster file (without source dataset)."));
      77             : 
      78          11 :     argParser->add_epilog(_(
      79             :         "For more details, consult the full documentation for the gdal_create "
      80          11 :         "utility: http://gdal.org/gdal_create.html"));
      81             : 
      82          11 :     argParser->add_output_type_argument(psOptions->eDT);
      83             : 
      84          11 :     argParser->add_output_format_argument(psOptions->osFormat);
      85             : 
      86          11 :     argParser->add_argument("-outsize")
      87          22 :         .metavar("<xsize> <ysize>")
      88          11 :         .nargs(2)
      89          11 :         .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          11 :             })
     103          11 :         .help(_("Set the size of the output file."));
     104             : 
     105          11 :     argParser->add_argument("-bands")
     106          22 :         .metavar("<count>")
     107          11 :         .store_into(psOptions->nBandCount)
     108          11 :         .help(_("Set the number of bands in the output file."));
     109             : 
     110          22 :     argParser->add_argument("-burn").metavar("<value>").append().help(
     111             :         _("A fixed value to burn into a band. A list of "
     112          11 :           "-burn options can be supplied, one per band being written to."));
     113             : 
     114          11 :     argParser->add_argument("-a_srs")
     115          22 :         .metavar("<srs_def>")
     116          11 :         .store_into(psOptions->osOutputSRS)
     117          11 :         .help(_("Override the projection for the output file. "));
     118             : 
     119          11 :     argParser->add_argument("-a_ullr")
     120          22 :         .metavar("<ulx> <uly> <lrx> <lry>")
     121          11 :         .scan<'g', double>()
     122          11 :         .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          11 :             })
     143          11 :         .help(_("Assign the georeferenced bounds of the output file. "));
     144             : 
     145          11 :     argParser->add_argument("-a_nodata")
     146          22 :         .metavar("<value>")
     147          11 :         .scan<'g', double>()
     148             :         .action(
     149           4 :             [psOptions](const std::string &s)
     150             :             {
     151           4 :                 psOptions->bSetNoData = true;
     152           4 :                 psOptions->osNoData = s;
     153          11 :             })
     154          11 :         .help(_("Assign a specified nodata value to output bands."));
     155             : 
     156          11 :     argParser->add_metadata_item_options_argument(psOptions->aosMetadata);
     157             : 
     158          11 :     argParser->add_creation_options_argument(psOptions->aosCreateOptions);
     159             : 
     160          11 :     argParser->add_quiet_argument(&psOptions->bQuiet);
     161             : 
     162          11 :     argParser->add_argument("-if")
     163          22 :         .metavar("<input_dataset>")
     164          11 :         .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          11 :                 "-a_ullr and -a_nodata."));
     168             : 
     169          11 :     argParser->add_argument("out_dataset")
     170          22 :         .metavar("<out_dataset>")
     171          11 :         .store_into(psOptions->osOutputFilename)
     172          11 :         .help(_("Name of the output dataset to create."));
     173             : 
     174          11 :     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           0 :         GDALExit(-argc);
     199             : 
     200          11 :     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          19 :     GDALCreateOptions sOptions;
     218             : 
     219          19 :     CPLStringList aosArgv;
     220         113 :     for (int iArg = 1; iArg < argc; iArg++)
     221             :     {
     222         102 :         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          98 :             aosArgv.AddString(argv[iArg]);
     270             :         }
     271             :     }
     272          11 :     CSLDestroy(argv);
     273             : 
     274             :     try
     275             :     {
     276             : 
     277          21 :         auto argParser = GDALCreateAppOptionsGetParser(&sOptions);
     278          11 :         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 :     double adfGeoTransform[6] = {0, 1, 0, 0, 0, 1};
     287          10 :     if (sOptions.bGeoTransform && sOptions.nPixels > 0 && sOptions.nLines > 0)
     288             :     {
     289           3 :         adfGeoTransform[0] = sOptions.dfULX;
     290           3 :         adfGeoTransform[1] =
     291           3 :             (sOptions.dfLRX - sOptions.dfULX) / sOptions.nPixels;
     292           3 :         adfGeoTransform[2] = 0;
     293           3 :         adfGeoTransform[3] = sOptions.dfULY;
     294           3 :         adfGeoTransform[4] = 0;
     295           3 :         adfGeoTransform[5] =
     296           3 :             (sOptions.dfLRY - sOptions.dfULY) / sOptions.nLines;
     297             :     }
     298             : 
     299           0 :     std::unique_ptr<GDALDataset> poInputDS;
     300          10 :     if (!sOptions.osInputFilename.empty())
     301             :     {
     302           4 :         poInputDS.reset(
     303             :             GDALDataset::Open(sOptions.osInputFilename.c_str(),
     304             :                               GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
     305           4 :         if (poInputDS == nullptr)
     306             :         {
     307           1 :             GDALExit(1);
     308             :         }
     309           3 :         if (sOptions.nPixels == 0)
     310             :         {
     311           2 :             sOptions.nPixels = poInputDS->GetRasterXSize();
     312           2 :             sOptions.nLines = poInputDS->GetRasterYSize();
     313             :         }
     314           3 :         if (sOptions.nBandCount < 0)
     315             :         {
     316           2 :             sOptions.nBandCount = poInputDS->GetRasterCount();
     317             :         }
     318           3 :         if (sOptions.eDT == GDT_Unknown && poInputDS->GetRasterCount() > 0)
     319             :         {
     320           3 :             sOptions.eDT = poInputDS->GetRasterBand(1)->GetRasterDataType();
     321             :         }
     322           3 :         if (sOptions.osOutputSRS.empty())
     323             :         {
     324           3 :             sOptions.osOutputSRS = poInputDS->GetProjectionRef();
     325             :         }
     326           3 :         if (!(sOptions.bGeoTransform && sOptions.nPixels > 0 &&
     327           0 :               sOptions.nLines > 0))
     328             :         {
     329           3 :             if (poInputDS->GetGeoTransform(adfGeoTransform) == CE_None)
     330             :             {
     331           2 :                 sOptions.bGeoTransform = true;
     332             :             }
     333             :         }
     334           3 :         if (!sOptions.bSetNoData && poInputDS->GetRasterCount() > 0)
     335             :         {
     336           2 :             if (sOptions.eDT == GDT_Int64)
     337             :             {
     338             :                 int noData;
     339             :                 const auto nNoDataValue =
     340           0 :                     poInputDS->GetRasterBand(1)->GetNoDataValueAsInt64(&noData);
     341           0 :                 sOptions.bSetNoData = noData;
     342           0 :                 if (sOptions.bSetNoData)
     343             :                     sOptions.osNoData = CPLSPrintf(
     344           0 :                         CPL_FRMT_GIB, static_cast<GIntBig>(nNoDataValue));
     345             :             }
     346           2 :             else if (sOptions.eDT == GDT_UInt64)
     347             :             {
     348             :                 int noData;
     349             :                 const auto nNoDataValue =
     350           0 :                     poInputDS->GetRasterBand(1)->GetNoDataValueAsUInt64(
     351           0 :                         &noData);
     352           0 :                 sOptions.bSetNoData = noData;
     353           0 :                 if (sOptions.bSetNoData)
     354             :                     sOptions.osNoData = CPLSPrintf(
     355           0 :                         CPL_FRMT_GUIB, static_cast<GUIntBig>(nNoDataValue));
     356             :             }
     357             :             else
     358             :             {
     359             :                 int noData;
     360             :                 const double dfNoDataValue =
     361           2 :                     poInputDS->GetRasterBand(1)->GetNoDataValue(&noData);
     362           2 :                 sOptions.bSetNoData = noData;
     363           2 :                 if (sOptions.bSetNoData)
     364           0 :                     sOptions.osNoData = CPLSPrintf("%.18g", dfNoDataValue);
     365             :             }
     366             :         }
     367             :     }
     368             : 
     369          18 :     GDALDriverH hDriver = GDALGetDriverByName(
     370           9 :         sOptions.osFormat.empty()
     371          16 :             ? GetOutputDriverForRaster(sOptions.osOutputFilename.c_str())
     372           7 :                   .c_str()
     373           2 :             : sOptions.osFormat.c_str());
     374             : 
     375           9 :     if (hDriver == nullptr)
     376             :     {
     377           0 :         fprintf(stderr, "Output driver not found.\n");
     378           0 :         GDALExit(1);
     379             :     }
     380             :     const bool bHasCreate =
     381           9 :         GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, nullptr) != nullptr;
     382          11 :     if (!bHasCreate &&
     383           2 :         GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATECOPY, nullptr) == nullptr)
     384             :     {
     385           1 :         fprintf(stderr, "This driver has no creation capabilities.\n");
     386           1 :         GDALExit(1);
     387             :     }
     388           8 :     GDALDriverH hTmpDriver = GDALGetDriverByName("MEM");
     389           8 :     if (!bHasCreate && hTmpDriver == nullptr)
     390             :     {
     391           0 :         fprintf(stderr, "MEM driver not available.\n");
     392           0 :         GDALExit(1);
     393             :     }
     394             : 
     395           8 :     if (sOptions.nPixels != 0 && sOptions.eDT == GDT_Unknown)
     396             :     {
     397           1 :         sOptions.eDT = GDT_Byte;
     398             :     }
     399           8 :     if (sOptions.nBandCount < 0)
     400             :     {
     401           2 :         sOptions.nBandCount = sOptions.eDT == GDT_Unknown ? 0 : 1;
     402             :     }
     403          15 :     GDALDatasetH hDS = GDALCreate(
     404             :         bHasCreate ? hDriver : hTmpDriver, sOptions.osOutputFilename.c_str(),
     405             :         sOptions.nPixels, sOptions.nLines, sOptions.nBandCount, sOptions.eDT,
     406           7 :         bHasCreate ? sOptions.aosCreateOptions.List() : nullptr);
     407             : 
     408           8 :     if (hDS == nullptr)
     409             :     {
     410           0 :         GDALExit(1);
     411             :     }
     412             : 
     413          13 :     if (!sOptions.osOutputSRS.empty() &&
     414           5 :         !EQUAL(sOptions.osOutputSRS.c_str(), "NONE"))
     415             :     {
     416          10 :         OGRSpatialReference oSRS;
     417           5 :         oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     418             : 
     419           5 :         if (oSRS.SetFromUserInput(sOptions.osOutputSRS.c_str()) != OGRERR_NONE)
     420             :         {
     421           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     422             :                      "Failed to process SRS definition: %s",
     423             :                      sOptions.osOutputSRS.c_str());
     424           0 :             GDALExit(1);
     425             :         }
     426             : 
     427           5 :         char *pszSRS = nullptr;
     428           5 :         oSRS.exportToWkt(&pszSRS);
     429             : 
     430           5 :         if (GDALSetProjection(hDS, pszSRS) != CE_None)
     431             :         {
     432           0 :             CPLFree(pszSRS);
     433           0 :             GDALClose(hDS);
     434           0 :             GDALExit(1);
     435             :         }
     436           5 :         CPLFree(pszSRS);
     437             :     }
     438           8 :     if (sOptions.bGeoTransform)
     439             :     {
     440           5 :         if (sOptions.nPixels == 0)
     441             :         {
     442           0 :             fprintf(stderr,
     443             :                     "-outsize must be specified when -a_ullr is used.\n");
     444           0 :             GDALClose(hDS);
     445           0 :             GDALExit(1);
     446             :         }
     447           5 :         if (GDALSetGeoTransform(hDS, adfGeoTransform) != CE_None)
     448             :         {
     449           0 :             GDALClose(hDS);
     450           0 :             GDALExit(1);
     451             :         }
     452             :     }
     453           3 :     else if (poInputDS && poInputDS->GetGCPCount() > 0)
     454             :     {
     455           2 :         GDALDataset::FromHandle(hDS)->SetGCPs(poInputDS->GetGCPCount(),
     456           1 :                                               poInputDS->GetGCPs(),
     457           1 :                                               poInputDS->GetGCPSpatialRef());
     458             :     }
     459             : 
     460           8 :     if (!sOptions.aosMetadata.empty())
     461             :     {
     462           3 :         GDALSetMetadata(hDS, sOptions.aosMetadata.List(), nullptr);
     463             :     }
     464           8 :     const int nBands = GDALGetRasterCount(hDS);
     465           8 :     if (sOptions.bSetNoData)
     466             :     {
     467          15 :         for (int i = 0; i < nBands; i++)
     468             :         {
     469          11 :             auto hBand = GDALGetRasterBand(hDS, i + 1);
     470          11 :             if (sOptions.eDT == GDT_Int64)
     471             :             {
     472           0 :                 GDALSetRasterNoDataValueAsInt64(
     473           0 :                     hBand, static_cast<int64_t>(std::strtoll(
     474             :                                sOptions.osNoData.c_str(), nullptr, 10)));
     475             :             }
     476          11 :             else if (sOptions.eDT == GDT_UInt64)
     477             :             {
     478           0 :                 GDALSetRasterNoDataValueAsUInt64(
     479           0 :                     hBand, static_cast<uint64_t>(std::strtoull(
     480             :                                sOptions.osNoData.c_str(), nullptr, 10)));
     481             :             }
     482             :             else
     483             :             {
     484          11 :                 GDALSetRasterNoDataValue(hBand,
     485             :                                          CPLAtofM(sOptions.osNoData.c_str()));
     486             :             }
     487             :         }
     488             :     }
     489           8 :     if (!sOptions.adfBurnValues.empty())
     490             :     {
     491          12 :         for (int i = 0; i < nBands; i++)
     492             :         {
     493           9 :             GDALFillRaster(GDALGetRasterBand(hDS, i + 1),
     494           9 :                            i < static_cast<int>(sOptions.adfBurnValues.size())
     495           6 :                                ? sOptions.adfBurnValues[i]
     496           3 :                                : sOptions.adfBurnValues.back(),
     497             :                            0);
     498             :         }
     499             :     }
     500             : 
     501           8 :     bool bHasGotErr = false;
     502           8 :     if (!bHasCreate)
     503             :     {
     504           2 :         GDALDatasetH hOutDS = GDALCreateCopy(
     505             :             hDriver, sOptions.osOutputFilename.c_str(), hDS, false,
     506           1 :             sOptions.aosCreateOptions.List(),
     507           1 :             sOptions.bQuiet ? GDALDummyProgress : GDALTermProgress, nullptr);
     508           1 :         if (hOutDS == nullptr)
     509             :         {
     510           0 :             GDALClose(hDS);
     511           0 :             GDALExit(1);
     512             :         }
     513           1 :         if (GDALClose(hOutDS) != CE_None)
     514             :         {
     515           0 :             bHasGotErr = true;
     516             :         }
     517             :     }
     518             : 
     519           8 :     const bool bWasFailureBefore = (CPLGetLastErrorType() == CE_Failure);
     520           8 :     if (GDALClose(hDS) != CE_None)
     521           0 :         bHasGotErr = true;
     522           8 :     if (!bWasFailureBefore && CPLGetLastErrorType() == CE_Failure)
     523             :     {
     524           0 :         bHasGotErr = true;
     525             :     }
     526             : 
     527           8 :     return bHasGotErr ? 1 : 0;
     528             : }
     529             : 
     530           0 : MAIN_END

Generated by: LCOV version 1.14