LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_create.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 336 367 91.6 %
Date: 2026-06-25 20:36:43 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  gdal "raster create" subcommand
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_raster_create.h"
      14             : 
      15             : #include "cpl_conv.h"
      16             : #include "gdal_priv.h"
      17             : #include "gdal_utils.h"
      18             : #include "ogr_spatialref.h"
      19             : 
      20             : //! @cond Doxygen_Suppress
      21             : 
      22             : #ifndef _
      23             : #define _(x) (x)
      24             : #endif
      25             : 
      26             : /************************************************************************/
      27             : /*        GDALRasterCreateAlgorithm::GDALRasterCreateAlgorithm()        */
      28             : /************************************************************************/
      29             : 
      30         193 : GDALRasterCreateAlgorithm::GDALRasterCreateAlgorithm(
      31         193 :     bool standaloneStep) noexcept
      32             :     : GDALRasterPipelineStepAlgorithm(
      33             :           NAME, DESCRIPTION, HELP_URL,
      34         579 :           ConstructorOptions()
      35         193 :               .SetStandaloneStep(standaloneStep)
      36         193 :               .SetAddDefaultArguments(false)
      37         193 :               .SetAutoOpenInputDatasets(true)
      38         386 :               .SetInputDatasetHelpMsg("Template raster dataset")
      39         386 :               .SetInputDatasetAlias("like")
      40         386 :               .SetInputDatasetMetaVar("TEMPLATE-DATASET")
      41         193 :               .SetInputDatasetRequired(false)
      42         193 :               .SetInputDatasetPositional(false)
      43         579 :               .SetInputDatasetMaxCount(1))
      44             : {
      45         193 :     AddRasterInputArgs(false, false);
      46         193 :     if (standaloneStep)
      47             :     {
      48         153 :         AddProgressArg();
      49         153 :         AddRasterOutputArgs(false);
      50             :     }
      51             : 
      52         180 :     auto checkResSizeInput = [this](const std::vector<std::string> &values,
      53             :                                     const std::string &arg_name,
      54           2 :                                     bool requiresInt)
      55             :     {
      56         537 :         for (const auto &s : values)
      57             :         {
      58         359 :             char *endptr = nullptr;
      59         359 :             const double val = CPLStrtod(s.c_str(), &endptr);
      60         359 :             bool ok = false;
      61         359 :             if (endptr == s.c_str() + s.size())
      62             :             {
      63         295 :                 if (val >= 0 && val <= INT_MAX &&
      64         293 :                     (!requiresInt || static_cast<int>(val) == val))
      65             :                 {
      66         293 :                     ok = true;
      67             :                 }
      68             :             }
      69          64 :             else if (endptr &&
      70          64 :                      ((endptr[0] == ' ' && endptr[1] == '%' &&
      71           0 :                        endptr + 2 == s.c_str() + s.size()) ||
      72         128 :                       (endptr[0] == '%' && endptr + 1 == s.c_str() + s.size())))
      73             :             {
      74          64 :                 if (val >= 0)
      75             :                 {
      76          64 :                     ok = true;
      77             :                 }
      78             :             }
      79         359 :             if (!ok)
      80             :             {
      81           2 :                 ReportError(CE_Failure, CPLE_IllegalArg,
      82             :                             "Invalid %s value: %s'", arg_name.c_str(),
      83             :                             s.c_str());
      84           2 :                 return false;
      85             :             }
      86             :         }
      87         178 :         return true;
      88         193 :     };
      89             : 
      90             :     AddArg("resolution", 0, _("Target resolution (in destination CRS units)"),
      91         386 :            &m_resolution_str)
      92         193 :         .SetMinCount(2)
      93         193 :         .SetMaxCount(2)
      94         193 :         .SetMinValueExcluded(0)
      95         193 :         .SetRepeatedArgAllowed(false)
      96         193 :         .SetDisplayHintAboutRepetition(false)
      97         386 :         .SetMetaVar("<xres[%]>,<yres[%]>")
      98         386 :         .SetMutualExclusionGroup("resolution-size")
      99             :         .AddValidationAction(
     100          68 :             [this, checkResSizeInput]()
     101             :             {
     102          34 :                 return checkResSizeInput(m_resolution_str, "resolution", false);
     103         193 :             });
     104             : 
     105             :     // The same logic was applied in gdalalg_raster_resize.cpp, so we replicate it here for consistency.
     106             :     AddArg("size", 0,
     107             :            _("Target size in pixels (or percentage if using '%' suffix)"),
     108         386 :            &m_size_str)
     109         193 :         .SetMinCount(2)
     110         193 :         .SetMaxCount(2)
     111         193 :         .SetMinValueIncluded(0)
     112         193 :         .SetRepeatedArgAllowed(false)
     113         193 :         .SetDisplayHintAboutRepetition(false)
     114         386 :         .SetMetaVar("<width[%]>,<height[%]>")
     115         386 :         .SetMutualExclusionGroup("resolution-size")
     116             :         .AddValidationAction(
     117         292 :             [this, checkResSizeInput]()
     118         339 :             { return checkResSizeInput(m_size_str, "size", true); });
     119             : 
     120         386 :     AddArg("band-count", 0, _("Number of bands"), &m_bandCount)
     121         193 :         .SetDefault(m_bandCount)
     122         193 :         .SetMinValueIncluded(0);
     123         193 :     AddOutputDataTypeArg(&m_type).SetDefault(m_type);
     124             : 
     125         193 :     AddNodataArg(&m_nodata, /* noneAllowed = */ true);
     126             : 
     127         193 :     AddArg("burn", 0, _("Burn value"), &m_burnValues);
     128         386 :     AddArg("crs", 0, _("Set CRS"), &m_crs)
     129         386 :         .AddHiddenAlias("a_srs")
     130         193 :         .SetIsCRSArg(/*noneAllowed=*/true);
     131         193 :     AddBBOXArg(&m_bbox);
     132             : 
     133             :     {
     134         386 :         auto &arg = AddArg("metadata", 0, _("Add metadata item"), &m_metadata)
     135         386 :                         .SetMetaVar("<KEY>=<VALUE>")
     136         193 :                         .SetPackedValuesAllowed(false);
     137          56 :         arg.AddValidationAction([this, &arg]()
     138         249 :                                 { return ParseAndValidateKeyValue(arg); });
     139         193 :         arg.AddHiddenAlias("mo");
     140             :     }
     141             : 
     142         193 :     const auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
     143         193 :     CPLAssertNotNull(inputArg);
     144             : 
     145             :     AddArg("copy-metadata", 0, _("Copy metadata from input dataset"),
     146         386 :            &m_copyMetadata)
     147         193 :         .AddDirectDependency(*inputArg);
     148             :     AddArg("copy-overviews", 0,
     149         386 :            _("Create same overview levels as input dataset"), &m_copyOverviews)
     150         193 :         .AddDirectDependency(*inputArg);
     151         193 : }
     152             : 
     153             : /************************************************************************/
     154             : /*                 GDALRasterCreateAlgorithm::RunImpl()                 */
     155             : /************************************************************************/
     156             : 
     157          99 : bool GDALRasterCreateAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
     158             :                                         void *pProgressData)
     159             : {
     160          99 :     GDALPipelineStepRunContext stepCtxt;
     161          99 :     stepCtxt.m_pfnProgress = pfnProgress;
     162          99 :     stepCtxt.m_pProgressData = pProgressData;
     163          99 :     return RunPreStepPipelineValidations() && RunStep(stepCtxt);
     164             : }
     165             : 
     166             : /************************************************************************/
     167             : /*                 GDALRasterCreateAlgorithm::RunStep()                 */
     168             : /************************************************************************/
     169             : 
     170         102 : bool GDALRasterCreateAlgorithm::RunStep(GDALPipelineStepRunContext &)
     171             : {
     172         102 :     CPLAssert(!m_outputDataset.GetDatasetRef());
     173             : 
     174         102 :     if (m_standaloneStep)
     175             :     {
     176          99 :         if (m_format.empty())
     177             :         {
     178             :             const auto aosFormats =
     179             :                 CPLStringList(GDALGetOutputDriversForDatasetName(
     180          43 :                     m_outputDataset.GetName().c_str(), GDAL_OF_RASTER,
     181             :                     /* bSingleMatch = */ true,
     182          43 :                     /* bWarn = */ true));
     183          43 :             if (aosFormats.size() != 1)
     184             :             {
     185           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     186             :                             "Cannot guess driver for %s",
     187           1 :                             m_outputDataset.GetName().c_str());
     188           1 :                 return false;
     189             :             }
     190          42 :             m_format = aosFormats[0];
     191             :         }
     192             :     }
     193             :     else
     194             :     {
     195           3 :         m_format = "MEM";
     196             :     }
     197             : 
     198         202 :     OGRSpatialReference oSRS;
     199             : 
     200         101 :     GDALGeoTransform gt;
     201         101 :     bool bGTValid = false;
     202             : 
     203         202 :     CPLStringList aosCreationOptions(m_creationOptions);
     204             : 
     205         101 :     GDALDataset *poSrcDS = m_inputDataset.empty()
     206         101 :                                ? nullptr
     207          36 :                                : m_inputDataset.front().GetDatasetRef();
     208             : 
     209         101 :     constexpr double EPSILON = 1e-5;
     210             : 
     211             :     // Process size_str to fill m_size
     212         228 :     for (size_t i = 0; i < m_size_str.size(); i++)
     213             :     {
     214         130 :         const auto &s = m_size_str[i];
     215         130 :         char *endptr = nullptr;
     216         130 :         const double val = CPLStrtod(s.c_str(), &endptr);
     217         130 :         if (endptr &&
     218         130 :             ((endptr[0] == ' ' && endptr[1] == '%' &&
     219           0 :               endptr + 2 == s.c_str() + s.size()) ||
     220         260 :              (endptr[0] == '%' && endptr + 1 == s.c_str() + s.size())))
     221             :         {
     222             :             // Percentage
     223          15 :             if (!poSrcDS)
     224             :             {
     225           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     226             :                             "Cannot use percentage size without input dataset");
     227           3 :                 return false;
     228             :             }
     229          14 :             const int refSize = (i == 0) ? poSrcDS->GetRasterXSize()
     230           7 :                                          : poSrcDS->GetRasterYSize();
     231          14 :             const double dfSize = std::ceil((refSize * val / 100.0) - EPSILON);
     232             :             ;
     233          14 :             if (dfSize > INT_MAX)
     234             :             {
     235           2 :                 ReportError(CE_Failure, CPLE_AppDefined,
     236             :                             "Computed size is too large");
     237           2 :                 return false;
     238             :             }
     239          12 :             m_size.push_back(static_cast<int>(dfSize));
     240             :         }
     241             :         else
     242             :         {
     243         115 :             m_size.push_back(static_cast<int>(val));
     244             :         }
     245             :     }
     246             : 
     247         196 :     std::vector<bool> abResIsPercentage(m_resolution_str.size(), false);
     248             : 
     249             :     // Process resolution_str to fill m_resolution
     250         126 :     for (size_t i = 0; i < m_resolution_str.size(); i++)
     251             :     {
     252          30 :         const auto &s = m_resolution_str[i];
     253          30 :         char *endptr = nullptr;
     254          30 :         const double val = CPLStrtod(s.c_str(), &endptr);
     255             : 
     256          30 :         if (endptr &&
     257          30 :             ((endptr[0] == ' ' && endptr[1] == '%' &&
     258           0 :               endptr + 2 == s.c_str() + s.size()) ||
     259          60 :              (endptr[0] == '%' && endptr + 1 == s.c_str() + s.size())))
     260             :         {
     261             :             // Percentage
     262          14 :             if (!poSrcDS)
     263             :             {
     264           2 :                 ReportError(
     265             :                     CE_Failure, CPLE_AppDefined,
     266             :                     "Cannot use percentage resolution without input dataset");
     267           2 :                 return false;
     268             :             }
     269          12 :             if (poSrcDS->GetGeoTransform(gt) == CE_None)
     270             :             {
     271             :                 const double refRes =
     272          12 :                     (i == 0) ? std::abs(gt.xscale) : std::abs(gt.yscale);
     273          12 :                 const double dfRes = refRes * (val / 100.0);
     274          12 :                 m_resolution.push_back(dfRes);
     275             :             }
     276             :             else
     277             :             {
     278           0 :                 ReportError(CE_Failure, CPLE_AppDefined,
     279             :                             "Cannot get geotransform from input dataset");
     280           0 :                 return false;
     281             :             }
     282          12 :             abResIsPercentage[i] = true;
     283             :         }
     284             :         else
     285             :         {
     286          16 :             m_resolution.push_back(val);
     287          16 :             abResIsPercentage[i] = false;
     288             :         }
     289             :     }
     290             : 
     291          96 :     if (poSrcDS)
     292             :     {
     293          34 :         if (m_size.empty())
     294             :         {
     295          75 :             m_size = std::vector<int>{poSrcDS->GetRasterXSize(),
     296          50 :                                       poSrcDS->GetRasterYSize()};
     297             :         }
     298             : 
     299          34 :         bGTValid = poSrcDS->GetGeoTransform(gt) == CE_None;
     300             : 
     301             :         // If one of the size is 0, compute it from the other size
     302          34 :         if (m_size[0] == 0 && m_size[1] > 0)
     303             :         {
     304           1 :             if (bGTValid)
     305             :             {
     306             :                 const double ratio =
     307           1 :                     static_cast<double>(poSrcDS->GetRasterXSize()) /
     308           1 :                     static_cast<double>(poSrcDS->GetRasterYSize());
     309             :                 const double dfWidth =
     310           1 :                     std::ceil(static_cast<double>(m_size[1]) * ratio - EPSILON);
     311           1 :                 if (dfWidth > INT_MAX)
     312             :                 {
     313           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
     314             :                                 "Computed width is too large");
     315           0 :                     return false;
     316             :                 }
     317           1 :                 m_size[0] = static_cast<int>(dfWidth);
     318             :             }
     319             :             else
     320             :             {
     321           0 :                 ReportError(CE_Failure, CPLE_AppDefined,
     322             :                             "Cannot get geotransform from input dataset");
     323           0 :                 return false;
     324             :             }
     325             :         }
     326          33 :         else if (m_size[1] == 0 && m_size[0] > 0)
     327             :         {
     328           1 :             if (bGTValid)
     329             :             {
     330             :                 const double ratio =
     331           1 :                     static_cast<double>(poSrcDS->GetRasterYSize()) /
     332           1 :                     static_cast<double>(poSrcDS->GetRasterXSize());
     333             :                 const double dfHeight =
     334           1 :                     std::ceil(static_cast<double>(m_size[0]) * ratio - EPSILON);
     335           1 :                 if (dfHeight > INT_MAX)
     336             :                 {
     337           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
     338             :                                 "Computed height is too large");
     339           0 :                     return false;
     340             :                 }
     341           1 :                 m_size[1] = static_cast<int>(dfHeight);
     342             :             }
     343             :             else
     344             :             {
     345           0 :                 m_size[1] = poSrcDS->GetRasterYSize();
     346             :             }
     347             :         }
     348             : 
     349          34 :         if (!GetArg("band-count")->IsExplicitlySet())
     350             :         {
     351          33 :             m_bandCount = poSrcDS->GetRasterCount();
     352             :         }
     353             : 
     354          34 :         if (!GetArg("datatype")->IsExplicitlySet())
     355             :         {
     356          33 :             if (m_bandCount > 0)
     357             :             {
     358             :                 m_type = GDALGetDataTypeName(
     359          33 :                     poSrcDS->GetRasterBand(1)->GetRasterDataType());
     360             :             }
     361             :         }
     362             : 
     363          34 :         if (m_crs.empty())
     364             :         {
     365          32 :             if (const auto poSRS = poSrcDS->GetSpatialRef())
     366          32 :                 oSRS = *poSRS;
     367             :         }
     368             : 
     369          34 :         if (m_nodata.empty() && m_bandCount > 0)
     370             :         {
     371          33 :             int bNoData = false;
     372             :             const double dfNoData =
     373          33 :                 poSrcDS->GetRasterBand(1)->GetNoDataValue(&bNoData);
     374          33 :             if (bNoData)
     375          24 :                 m_nodata = CPLSPrintf("%.17g", dfNoData);
     376             :         }
     377             : 
     378             :         // Replicate tiling of input datasets for a few popular output formats,
     379             :         // when compatible, and when the user hasn't specified creation options
     380             :         // affecting tiling.
     381          34 :         int nBlockXSize = 0, nBlockYSize = 0;
     382          34 :         if (m_bandCount > 0)
     383          34 :             poSrcDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
     384             : 
     385          34 :         if (EQUAL(m_format.c_str(), "GTIFF") &&
     386          17 :             aosCreationOptions.FetchNameValue("TILED") == nullptr &&
     387          16 :             aosCreationOptions.FetchNameValue("BLOCKXSIZE") == nullptr &&
     388          67 :             aosCreationOptions.FetchNameValue("BLOCKYSIZE") == nullptr &&
     389          16 :             m_bandCount > 0)
     390             :         {
     391          16 :             if (nBlockXSize != poSrcDS->GetRasterXSize() &&
     392          16 :                 (nBlockXSize % 16) == 0 && (nBlockYSize % 16) == 0)
     393             :             {
     394           1 :                 aosCreationOptions.SetNameValue("TILED", "YES");
     395             :                 aosCreationOptions.SetNameValue("BLOCKXSIZE",
     396           1 :                                                 CPLSPrintf("%d", nBlockXSize));
     397             :                 aosCreationOptions.SetNameValue("BLOCKYSIZE",
     398           1 :                                                 CPLSPrintf("%d", nBlockYSize));
     399             :             }
     400             :         }
     401          18 :         else if (EQUAL(m_format.c_str(), "COG") &&
     402          19 :                  aosCreationOptions.FetchNameValue("BLOCKSIZE") == nullptr &&
     403           1 :                  m_bandCount > 0)
     404             :         {
     405           1 :             if (nBlockXSize != poSrcDS->GetRasterXSize() &&
     406           2 :                 nBlockXSize == nBlockYSize && nBlockXSize >= 128 &&
     407           1 :                 (nBlockXSize % 16) == 0)
     408             :             {
     409             :                 aosCreationOptions.SetNameValue("BLOCKSIZE",
     410           1 :                                                 CPLSPrintf("%d", nBlockXSize));
     411             :             }
     412             :         }
     413          17 :         else if (EQUAL(m_format.c_str(), "GPKG") &&
     414           3 :                  aosCreationOptions.FetchNameValue("BLOCKSIZE") == nullptr &&
     415           2 :                  aosCreationOptions.FetchNameValue("BLOCKXSIZE") == nullptr &&
     416          22 :                  aosCreationOptions.FetchNameValue("BLOCKYSIZE") == nullptr &&
     417           2 :                  m_bandCount > 0)
     418             :         {
     419           2 :             if (nBlockXSize != poSrcDS->GetRasterXSize() &&
     420           2 :                 nBlockXSize >= 256 && nBlockXSize <= 4096 &&
     421           4 :                 nBlockYSize >= 256 && nBlockYSize <= 4096)
     422             :             {
     423             :                 aosCreationOptions.SetNameValue("BLOCKXSIZE",
     424           1 :                                                 CPLSPrintf("%d", nBlockXSize));
     425             :                 aosCreationOptions.SetNameValue("BLOCKYSIZE",
     426           1 :                                                 CPLSPrintf("%d", nBlockYSize));
     427             :             }
     428             :         }
     429             : 
     430             :         // If resolution was explicitly set, then we need to recompute size
     431          34 :         if (!m_resolution.empty() && bGTValid)
     432             :         {
     433             :             // Set resolution from the other axis if 0
     434           7 :             if (m_resolution[0] == 0)
     435             :             {
     436           1 :                 if (abResIsPercentage[1])
     437             :                 {
     438           2 :                     m_resolution[0] = (std::abs(gt.xscale) / m_resolution[1]) /
     439           1 :                                       std::abs(gt.yscale);
     440             :                 }
     441             :                 else
     442             :                 {
     443           0 :                     m_resolution[0] = m_resolution[1];
     444             :                 }
     445             :             }
     446             : 
     447           7 :             if (m_resolution[1] == 0)
     448             :             {
     449           1 :                 if (abResIsPercentage[0])
     450             :                 {
     451           2 :                     m_resolution[1] = (std::abs(gt.yscale) / m_resolution[0]) /
     452           1 :                                       std::abs(gt.xscale);
     453             :                 }
     454             :                 else
     455             :                 {
     456           0 :                     m_resolution[1] = m_resolution[0];
     457             :                 }
     458             :             }
     459             : 
     460           7 :             const double dfXResRatio = std::abs(gt.xscale) / m_resolution[0];
     461           7 :             const double dfYResRatio = std::abs(gt.yscale) / m_resolution[1];
     462             :             const double dfWidth =
     463           7 :                 std::ceil(poSrcDS->GetRasterXSize() * dfXResRatio - EPSILON);
     464             :             const double dfHeight =
     465           7 :                 std::ceil(poSrcDS->GetRasterYSize() * dfYResRatio - EPSILON);
     466           7 :             if (dfWidth > INT_MAX || dfHeight > INT_MAX)
     467             :             {
     468           0 :                 ReportError(CE_Failure, CPLE_AppDefined,
     469             :                             "Computed size is too large");
     470           0 :                 return false;
     471             :             }
     472           7 :             m_size = {static_cast<int>(dfWidth), static_cast<int>(dfHeight)};
     473             :         }
     474             :     }
     475             : 
     476             :     // If size is empty, try resolution from bbox
     477         104 :     if (m_size.empty() && m_bbox.size() == 4 && m_resolution.size() == 2 &&
     478         104 :         (m_bbox[3] - m_bbox[1] != 0) && (m_bbox[2] - m_bbox[0] != 0))
     479             :     {
     480             :         const double dfWidth =
     481           6 :             std::ceil((m_bbox[2] - m_bbox[0]) / m_resolution[0] - EPSILON);
     482             :         const double dfHeight =
     483           6 :             std::ceil((m_bbox[3] - m_bbox[1]) / m_resolution[1] - EPSILON);
     484           6 :         if (dfWidth > INT_MAX || dfHeight > INT_MAX)
     485             :         {
     486           3 :             ReportError(CE_Failure, CPLE_AppDefined,
     487             :                         "Computed size is too large");
     488           3 :             return false;
     489             :         }
     490           3 :         m_size = {static_cast<int>(dfWidth), static_cast<int>(dfHeight)};
     491             :     }
     492             : 
     493          93 :     if (m_size.empty())
     494             :     {
     495           2 :         if (!m_resolution.empty() && m_bbox.empty())
     496             :         {
     497           1 :             ReportError(
     498             :                 CE_Failure, CPLE_IllegalArg,
     499             :                 "Cannot use resolution without 'bbox' or 'like' dataset");
     500             :         }
     501             :         else
     502             :         {
     503           1 :             ReportError(CE_Failure, CPLE_IllegalArg,
     504             :                         "Argument 'size' or 'resolution' or 'like' dataset "
     505             :                         "should be specified");
     506             :         }
     507           2 :         return false;
     508             :     }
     509             : 
     510             :     // Guess the size from bbox if only one of the two is specified
     511         182 :     if (m_size.size() == 2 && (m_size[0] == 0 || m_size[1] == 0) &&
     512           5 :         !(m_size[0] == 0 && m_size[1] == 0) && m_bbox.size() == 4 &&
     513         182 :         (m_bbox[3] - m_bbox[1] != 0) && (m_bbox[2] - m_bbox[0] != 0))
     514             :     {
     515           4 :         const double ratio = (m_bbox[2] - m_bbox[0]) / (m_bbox[3] - m_bbox[1]);
     516           4 :         if (m_size[0] == 0)
     517             :         {
     518           2 :             double dfWidth = std::ceil(m_size[1] * ratio - EPSILON);
     519           2 :             if (dfWidth > INT_MAX)
     520             :             {
     521           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     522             :                             "Too large computed width");
     523           1 :                 return false;
     524             :             }
     525           1 :             m_size[0] = static_cast<int>(dfWidth);
     526             :         }
     527           2 :         else if (m_size[1] == 0)
     528             :         {
     529           2 :             double dfHeight = std::ceil(m_size[0] / ratio - EPSILON);
     530           2 :             if (dfHeight > INT_MAX)
     531             :             {
     532           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     533             :                             "Too large computed height");
     534           1 :                 return false;
     535             :             }
     536           1 :             m_size[1] = static_cast<int>(dfHeight);
     537             :         }
     538             :     }
     539             : 
     540         133 :     if (!m_burnValues.empty() && m_burnValues.size() != 1 &&
     541          44 :         static_cast<int>(m_burnValues.size()) != m_bandCount)
     542             :     {
     543           2 :         if (m_bandCount == 1)
     544             :         {
     545           1 :             ReportError(CE_Failure, CPLE_IllegalArg,
     546             :                         "One value should be provided for argument "
     547             :                         "'burn', given there is one band");
     548             :         }
     549             :         else
     550             :         {
     551           1 :             ReportError(CE_Failure, CPLE_IllegalArg,
     552             :                         "One or %d values should be provided for argument "
     553             :                         "'burn', given there are %d bands",
     554             :                         m_bandCount, m_bandCount);
     555             :         }
     556           2 :         return false;
     557             :     }
     558             : 
     559          87 :     auto poDriver = GetGDALDriverManager()->GetDriverByName(m_format.c_str());
     560          87 :     if (!poDriver)
     561             :     {
     562             :         // shouldn't happen given checks done in GDALAlgorithm
     563           0 :         ReportError(CE_Failure, CPLE_AppDefined, "Cannot find driver %s",
     564             :                     m_format.c_str());
     565           0 :         return false;
     566             :     }
     567             : 
     568          87 :     if (m_appendRaster)
     569             :     {
     570           2 :         if (poDriver->GetMetadataItem(GDAL_DCAP_CREATE_SUBDATASETS) == nullptr)
     571             :         {
     572           1 :             ReportError(CE_Failure, CPLE_NotSupported,
     573             :                         "-append option not supported for driver %s",
     574           1 :                         poDriver->GetDescription());
     575           1 :             return false;
     576             :         }
     577           1 :         aosCreationOptions.SetNameValue("APPEND_SUBDATASET", "YES");
     578             :     }
     579             : 
     580             :     auto poRetDS = std::unique_ptr<GDALDataset>(poDriver->Create(
     581         258 :         m_outputDataset.GetName().c_str(), m_size[0], m_size[1], m_bandCount,
     582         172 :         GDALGetDataTypeByName(m_type.c_str()), aosCreationOptions.List()));
     583          86 :     if (!poRetDS)
     584             :     {
     585           1 :         return false;
     586             :     }
     587             : 
     588          85 :     if (!m_crs.empty() && m_crs != "none" && m_crs != "null")
     589             :     {
     590          36 :         oSRS.SetFromUserInput(m_crs.c_str());
     591          36 :         oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     592             :     }
     593             : 
     594          85 :     if (!oSRS.IsEmpty())
     595             :     {
     596          68 :         if (poRetDS->SetSpatialRef(&oSRS) != CE_None)
     597             :         {
     598           1 :             ReportError(CE_Failure, CPLE_AppDefined, "Setting CRS failed");
     599           1 :             return false;
     600             :         }
     601             :     }
     602             : 
     603          84 :     if (!m_bbox.empty())
     604             :     {
     605          42 :         if (poRetDS->GetRasterXSize() == 0 || poRetDS->GetRasterYSize() == 0)
     606             :         {
     607           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     608             :                         "Cannot set extent because one of dataset height or "
     609             :                         "width is null");
     610           1 :             return false;
     611             :         }
     612          41 :         bGTValid = true;
     613          41 :         gt.xorig = m_bbox[0];
     614          41 :         gt.xscale = (m_bbox[2] - m_bbox[0]) / poRetDS->GetRasterXSize();
     615          41 :         gt.xrot = 0;
     616          41 :         gt.yorig = m_bbox[3];
     617          41 :         gt.yrot = 0;
     618          41 :         gt.yscale = -(m_bbox[3] - m_bbox[1]) / poRetDS->GetRasterYSize();
     619             :     }
     620          83 :     if (bGTValid)
     621             :     {
     622          74 :         if (poRetDS->SetGeoTransform(gt) != CE_None)
     623             :         {
     624           1 :             ReportError(CE_Failure, CPLE_AppDefined, "Setting extent failed");
     625           1 :             return false;
     626             :         }
     627             :     }
     628             : 
     629          82 :     if (!m_nodata.empty() && !EQUAL(m_nodata.c_str(), "none"))
     630             :     {
     631         158 :         for (int i = 0; i < poRetDS->GetRasterCount(); ++i)
     632             :         {
     633         105 :             bool bCannotBeExactlyRepresented = false;
     634         105 :             if (poRetDS->GetRasterBand(i + 1)->SetNoDataValueAsString(
     635         105 :                     m_nodata.c_str(), &bCannotBeExactlyRepresented) != CE_None)
     636             :             {
     637           1 :                 if (bCannotBeExactlyRepresented)
     638             :                 {
     639           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
     640             :                                 "Setting nodata value failed as it cannot be "
     641             :                                 "represented on its data type");
     642             :                 }
     643             :                 else
     644             :                 {
     645           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
     646             :                                 "Setting nodata value failed");
     647             :                 }
     648           1 :                 return false;
     649             :             }
     650             :         }
     651             :     }
     652             : 
     653          81 :     if (m_copyMetadata)
     654             :     {
     655             : 
     656             :         // This should never happen because of the dependency set
     657           1 :         CPLAssertNotNull(poSrcDS);
     658             : 
     659             :         {
     660           1 :             const CPLStringList aosDomains(poSrcDS->GetMetadataDomainList());
     661           4 :             for (const char *domain : aosDomains)
     662             :             {
     663           3 :                 if (!EQUAL(domain, GDAL_MDD_IMAGE_STRUCTURE))
     664             :                 {
     665           4 :                     if (poRetDS->SetMetadata(poSrcDS->GetMetadata(domain),
     666           4 :                                              domain) != CE_None)
     667             :                     {
     668           0 :                         ReportError(CE_Failure, CPLE_AppDefined,
     669             :                                     "Cannot copy '%s' metadata domain", domain);
     670           0 :                         return false;
     671             :                     }
     672             :                 }
     673             :             }
     674             :         }
     675           3 :         for (int i = 0; i < m_bandCount; ++i)
     676             :         {
     677             :             const CPLStringList aosDomains(
     678           2 :                 poSrcDS->GetRasterBand(i + 1)->GetMetadataDomainList());
     679           3 :             for (const char *domain : aosDomains)
     680             :             {
     681           1 :                 if (!EQUAL(domain, GDAL_MDD_IMAGE_STRUCTURE))
     682             :                 {
     683           2 :                     if (poRetDS->GetRasterBand(i + 1)->SetMetadata(
     684           1 :                             poSrcDS->GetRasterBand(i + 1)->GetMetadata(domain),
     685           2 :                             domain) != CE_None)
     686             :                     {
     687           0 :                         ReportError(
     688             :                             CE_Failure, CPLE_AppDefined,
     689             :                             "Cannot copy '%s' metadata domain for band %d",
     690             :                             domain, i + 1);
     691           0 :                         return false;
     692             :                     }
     693             :                 }
     694             :             }
     695             :         }
     696             :     }
     697             : 
     698         162 :     const CPLStringList aosMD(m_metadata);
     699         109 :     for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
     700             :     {
     701          28 :         if (poRetDS->SetMetadataItem(key, value) != CE_None)
     702             :         {
     703           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     704             :                         "SetMetadataItem('%s', '%s') failed", key, value);
     705           0 :             return false;
     706             :         }
     707             :     }
     708             : 
     709          81 :     if (m_copyOverviews && m_bandCount > 0)
     710             :     {
     711             :         // This should never happen because of the dependency set
     712           2 :         CPLAssertNotNull(poSrcDS);
     713             : 
     714           3 :         if (poSrcDS->GetRasterXSize() != poRetDS->GetRasterXSize() ||
     715           1 :             poSrcDS->GetRasterYSize() != poRetDS->GetRasterYSize())
     716             :         {
     717           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     718             :                         "Argument 'copy-overviews' can only be set when the "
     719             :                         "input and output datasets have the same dimension");
     720           1 :             return false;
     721             :         }
     722             :         const int nOverviewCount =
     723           1 :             poSrcDS->GetRasterBand(1)->GetOverviewCount();
     724           1 :         std::vector<int> anLevels;
     725           2 :         for (int i = 0; i < nOverviewCount; ++i)
     726             :         {
     727           1 :             const auto poOvrBand = poSrcDS->GetRasterBand(1)->GetOverview(i);
     728           1 :             const int nOvrFactor = GDALComputeOvFactor(
     729             :                 poOvrBand->GetXSize(), poSrcDS->GetRasterXSize(),
     730           1 :                 poOvrBand->GetYSize(), poSrcDS->GetRasterYSize());
     731           1 :             anLevels.push_back(nOvrFactor);
     732             :         }
     733           2 :         if (poRetDS->BuildOverviews(
     734           1 :                 "NONE", nOverviewCount, anLevels.data(),
     735             :                 /* nListBands = */ 0, /* panBandList = */ nullptr,
     736             :                 /* pfnProgress = */ nullptr, /* pProgressData = */ nullptr,
     737           1 :                 /* options = */ nullptr) != CE_None)
     738             :         {
     739           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     740             :                         "Creating overview(s) failed");
     741           0 :             return false;
     742             :         }
     743             :     }
     744             : 
     745          80 :     if (!m_burnValues.empty())
     746             :     {
     747         138 :         for (int i = 0; i < m_bandCount; ++i)
     748             :         {
     749          90 :             const int burnValueIdx = m_burnValues.size() == 1 ? 0 : i;
     750          90 :             const auto poDstBand = poRetDS->GetRasterBand(i + 1);
     751             :             // cppcheck-suppress negativeContainerIndex
     752          90 :             if (poDstBand->Fill(m_burnValues[burnValueIdx]) != CE_None)
     753             :             {
     754           0 :                 ReportError(CE_Failure, CPLE_AppDefined,
     755             :                             "Setting burn value failed");
     756           0 :                 return false;
     757             :             }
     758             :         }
     759          48 :         if (poRetDS->FlushCache(false) != CE_None)
     760             :         {
     761           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     762             :                         "Setting burn value failed");
     763           0 :             return false;
     764             :         }
     765             :     }
     766             : 
     767          80 :     m_outputDataset.Set(std::move(poRetDS));
     768             : 
     769          80 :     return true;
     770             : }
     771             : 
     772             : GDALRasterCreateAlgorithmStandalone::~GDALRasterCreateAlgorithmStandalone() =
     773             :     default;
     774             : 
     775             : //! @endcond

Generated by: LCOV version 1.14