LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_create.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 178 193 92.2 %
Date: 2026-03-26 23:25:44 Functions: 4 4 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         134 : GDALRasterCreateAlgorithm::GDALRasterCreateAlgorithm(
      31         134 :     bool standaloneStep) noexcept
      32             :     : GDALRasterPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
      33         402 :                                       ConstructorOptions()
      34         134 :                                           .SetStandaloneStep(standaloneStep)
      35         134 :                                           .SetAddDefaultArguments(false)
      36         134 :                                           .SetAutoOpenInputDatasets(true)
      37         268 :                                           .SetInputDatasetAlias("like")
      38         134 :                                           .SetInputDatasetRequired(false)
      39         134 :                                           .SetInputDatasetPositional(false)
      40         402 :                                           .SetInputDatasetMaxCount(1))
      41             : {
      42         134 :     AddRasterInputArgs(false, false);
      43         134 :     if (standaloneStep)
      44             :     {
      45          96 :         AddProgressArg();
      46          96 :         AddRasterOutputArgs(false);
      47             :     }
      48             : 
      49         268 :     AddArg("size", 0, _("Output size in pixels"), &m_size)
      50         134 :         .SetMinCount(2)
      51         134 :         .SetMaxCount(2)
      52         134 :         .SetMinValueIncluded(0)
      53         134 :         .SetRepeatedArgAllowed(false)
      54         134 :         .SetDisplayHintAboutRepetition(false)
      55         134 :         .SetMetaVar("<width>,<height>");
      56         268 :     AddArg("band-count", 0, _("Number of bands"), &m_bandCount)
      57         134 :         .SetDefault(m_bandCount)
      58         134 :         .SetMinValueIncluded(0);
      59         134 :     AddOutputDataTypeArg(&m_type).SetDefault(m_type);
      60             : 
      61         134 :     AddNodataArg(&m_nodata, /* noneAllowed = */ true);
      62             : 
      63         134 :     AddArg("burn", 0, _("Burn value"), &m_burnValues);
      64         268 :     AddArg("crs", 0, _("Set CRS"), &m_crs)
      65         268 :         .AddHiddenAlias("a_srs")
      66         134 :         .SetIsCRSArg(/*noneAllowed=*/true);
      67         134 :     AddBBOXArg(&m_bbox);
      68             : 
      69             :     {
      70         268 :         auto &arg = AddArg("metadata", 0, _("Add metadata item"), &m_metadata)
      71         268 :                         .SetMetaVar("<KEY>=<VALUE>")
      72         134 :                         .SetPackedValuesAllowed(false);
      73          24 :         arg.AddValidationAction([this, &arg]()
      74         158 :                                 { return ParseAndValidateKeyValue(arg); });
      75         134 :         arg.AddHiddenAlias("mo");
      76             :     }
      77             :     AddArg("copy-metadata", 0, _("Copy metadata from input dataset"),
      78         134 :            &m_copyMetadata);
      79             :     AddArg("copy-overviews", 0,
      80         134 :            _("Create same overview levels as input dataset"), &m_copyOverviews);
      81         134 : }
      82             : 
      83             : /************************************************************************/
      84             : /*                 GDALRasterCreateAlgorithm::RunImpl()                 */
      85             : /************************************************************************/
      86             : 
      87          48 : bool GDALRasterCreateAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
      88             :                                         void *pProgressData)
      89             : {
      90          48 :     GDALPipelineStepRunContext stepCtxt;
      91          48 :     stepCtxt.m_pfnProgress = pfnProgress;
      92          48 :     stepCtxt.m_pProgressData = pProgressData;
      93          48 :     return RunPreStepPipelineValidations() && RunStep(stepCtxt);
      94             : }
      95             : 
      96             : /************************************************************************/
      97             : /*                 GDALRasterCreateAlgorithm::RunStep()                 */
      98             : /************************************************************************/
      99             : 
     100          50 : bool GDALRasterCreateAlgorithm::RunStep(GDALPipelineStepRunContext &)
     101             : {
     102          50 :     CPLAssert(!m_outputDataset.GetDatasetRef());
     103             : 
     104          50 :     if (m_standaloneStep)
     105             :     {
     106          48 :         if (m_format.empty())
     107             :         {
     108             :             const auto aosFormats =
     109             :                 CPLStringList(GDALGetOutputDriversForDatasetName(
     110          13 :                     m_outputDataset.GetName().c_str(), GDAL_OF_RASTER,
     111             :                     /* bSingleMatch = */ true,
     112          13 :                     /* bWarn = */ true));
     113          13 :             if (aosFormats.size() != 1)
     114             :             {
     115           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     116             :                             "Cannot guess driver for %s",
     117           1 :                             m_outputDataset.GetName().c_str());
     118           1 :                 return false;
     119             :             }
     120          12 :             m_format = aosFormats[0];
     121             :         }
     122             :     }
     123             :     else
     124             :     {
     125           2 :         m_format = "MEM";
     126             :     }
     127             : 
     128          98 :     OGRSpatialReference oSRS;
     129             : 
     130          49 :     GDALGeoTransform gt;
     131          49 :     bool bGTValid = false;
     132             : 
     133          49 :     GDALDataset *poSrcDS = m_inputDataset.empty()
     134          49 :                                ? nullptr
     135          13 :                                : m_inputDataset.front().GetDatasetRef();
     136          49 :     if (poSrcDS)
     137             :     {
     138          13 :         if (m_size.empty())
     139             :         {
     140          33 :             m_size = std::vector<int>{poSrcDS->GetRasterXSize(),
     141          22 :                                       poSrcDS->GetRasterYSize()};
     142             :         }
     143             : 
     144          13 :         if (!GetArg("band-count")->IsExplicitlySet())
     145             :         {
     146          12 :             m_bandCount = poSrcDS->GetRasterCount();
     147             :         }
     148             : 
     149          13 :         if (!GetArg("datatype")->IsExplicitlySet())
     150             :         {
     151          12 :             if (m_bandCount > 0)
     152             :             {
     153             :                 m_type = GDALGetDataTypeName(
     154          12 :                     poSrcDS->GetRasterBand(1)->GetRasterDataType());
     155             :             }
     156             :         }
     157             : 
     158          13 :         if (m_crs.empty())
     159             :         {
     160          11 :             if (const auto poSRS = poSrcDS->GetSpatialRef())
     161          11 :                 oSRS = *poSRS;
     162             :         }
     163             : 
     164          13 :         if (m_bbox.empty())
     165             :         {
     166          12 :             bGTValid = poSrcDS->GetGeoTransform(gt) == CE_None;
     167             :         }
     168             : 
     169          13 :         if (m_nodata.empty() && m_bandCount > 0)
     170             :         {
     171          12 :             int bNoData = false;
     172             :             const double dfNoData =
     173          12 :                 poSrcDS->GetRasterBand(1)->GetNoDataValue(&bNoData);
     174          12 :             if (bNoData)
     175          10 :                 m_nodata = CPLSPrintf("%.17g", dfNoData);
     176             :         }
     177             :     }
     178             : 
     179          49 :     if (m_size.empty())
     180             :     {
     181           1 :         ReportError(CE_Failure, CPLE_IllegalArg,
     182             :                     "Argument 'size' should be specified, or 'like' dataset "
     183             :                     "should be specified");
     184           1 :         return false;
     185             :     }
     186             : 
     187          62 :     if (!m_burnValues.empty() && m_burnValues.size() != 1 &&
     188          14 :         static_cast<int>(m_burnValues.size()) != m_bandCount)
     189             :     {
     190           2 :         if (m_bandCount == 1)
     191             :         {
     192           1 :             ReportError(CE_Failure, CPLE_IllegalArg,
     193             :                         "One value should be provided for argument "
     194             :                         "'burn', given there is one band");
     195             :         }
     196             :         else
     197             :         {
     198           1 :             ReportError(CE_Failure, CPLE_IllegalArg,
     199             :                         "One or %d values should be provided for argument "
     200             :                         "'burn', given there are %d bands",
     201             :                         m_bandCount, m_bandCount);
     202             :         }
     203           2 :         return false;
     204             :     }
     205             : 
     206          46 :     auto poDriver = GetGDALDriverManager()->GetDriverByName(m_format.c_str());
     207          46 :     if (!poDriver)
     208             :     {
     209             :         // shouldn't happen given checks done in GDALAlgorithm
     210           0 :         ReportError(CE_Failure, CPLE_AppDefined, "Cannot find driver %s",
     211             :                     m_format.c_str());
     212           0 :         return false;
     213             :     }
     214             : 
     215          46 :     if (m_appendRaster)
     216             :     {
     217           2 :         if (poDriver->GetMetadataItem(GDAL_DCAP_CREATE_SUBDATASETS) == nullptr)
     218             :         {
     219           1 :             ReportError(CE_Failure, CPLE_NotSupported,
     220             :                         "-append option not supported for driver %s",
     221           1 :                         poDriver->GetDescription());
     222           1 :             return false;
     223             :         }
     224           1 :         m_creationOptions.push_back("APPEND_SUBDATASET=YES");
     225             :     }
     226             : 
     227             :     auto poRetDS = std::unique_ptr<GDALDataset>(poDriver->Create(
     228         135 :         m_outputDataset.GetName().c_str(), m_size[0], m_size[1], m_bandCount,
     229             :         GDALGetDataTypeByName(m_type.c_str()),
     230          90 :         CPLStringList(m_creationOptions).List()));
     231          45 :     if (!poRetDS)
     232             :     {
     233           1 :         return false;
     234             :     }
     235             : 
     236          44 :     if (!m_crs.empty() && m_crs != "none" && m_crs != "null")
     237             :     {
     238          14 :         oSRS.SetFromUserInput(m_crs.c_str());
     239          14 :         oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     240             :     }
     241             : 
     242          44 :     if (!oSRS.IsEmpty())
     243             :     {
     244          25 :         if (poRetDS->SetSpatialRef(&oSRS) != CE_None)
     245             :         {
     246           1 :             ReportError(CE_Failure, CPLE_AppDefined, "Setting CRS failed");
     247           1 :             return false;
     248             :         }
     249             :     }
     250             : 
     251          43 :     if (!m_bbox.empty())
     252             :     {
     253          20 :         if (poRetDS->GetRasterXSize() == 0 || poRetDS->GetRasterYSize() == 0)
     254             :         {
     255           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     256             :                         "Cannot set extent because one of dataset height or "
     257             :                         "width is null");
     258           1 :             return false;
     259             :         }
     260          19 :         bGTValid = true;
     261          19 :         gt.xorig = m_bbox[0];
     262          19 :         gt.xscale = (m_bbox[2] - m_bbox[0]) / poRetDS->GetRasterXSize();
     263          19 :         gt.xrot = 0;
     264          19 :         gt.yorig = m_bbox[3];
     265          19 :         gt.yrot = 0;
     266          19 :         gt.yscale = -(m_bbox[3] - m_bbox[1]) / poRetDS->GetRasterYSize();
     267             :     }
     268          42 :     if (bGTValid)
     269             :     {
     270          31 :         if (poRetDS->SetGeoTransform(gt) != CE_None)
     271             :         {
     272           1 :             ReportError(CE_Failure, CPLE_AppDefined, "Setting extent failed");
     273           1 :             return false;
     274             :         }
     275             :     }
     276             : 
     277          41 :     if (!m_nodata.empty() && !EQUAL(m_nodata.c_str(), "none"))
     278             :     {
     279          68 :         for (int i = 0; i < poRetDS->GetRasterCount(); ++i)
     280             :         {
     281          45 :             bool bCannotBeExactlyRepresented = false;
     282          45 :             if (poRetDS->GetRasterBand(i + 1)->SetNoDataValueAsString(
     283          45 :                     m_nodata.c_str(), &bCannotBeExactlyRepresented) != CE_None)
     284             :             {
     285           1 :                 if (bCannotBeExactlyRepresented)
     286             :                 {
     287           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
     288             :                                 "Setting nodata value failed as it cannot be "
     289             :                                 "represented on its data type");
     290             :                 }
     291             :                 else
     292             :                 {
     293           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
     294             :                                 "Setting nodata value failed");
     295             :                 }
     296           1 :                 return false;
     297             :             }
     298             :         }
     299             :     }
     300             : 
     301          40 :     if (m_copyMetadata)
     302             :     {
     303           2 :         if (!poSrcDS)
     304             :         {
     305           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     306             :                         "Argument 'copy-metadata' can only be set when an "
     307             :                         "input dataset is set");
     308           1 :             return false;
     309             :         }
     310             :         {
     311           1 :             const CPLStringList aosDomains(poSrcDS->GetMetadataDomainList());
     312           4 :             for (const char *domain : aosDomains)
     313             :             {
     314           3 :                 if (!EQUAL(domain, "IMAGE_STRUCTURE"))
     315             :                 {
     316           4 :                     if (poRetDS->SetMetadata(poSrcDS->GetMetadata(domain),
     317           4 :                                              domain) != CE_None)
     318             :                     {
     319           0 :                         ReportError(CE_Failure, CPLE_AppDefined,
     320             :                                     "Cannot copy '%s' metadata domain", domain);
     321           0 :                         return false;
     322             :                     }
     323             :                 }
     324             :             }
     325             :         }
     326           3 :         for (int i = 0; i < m_bandCount; ++i)
     327             :         {
     328             :             const CPLStringList aosDomains(
     329           2 :                 poSrcDS->GetRasterBand(i + 1)->GetMetadataDomainList());
     330           3 :             for (const char *domain : aosDomains)
     331             :             {
     332           1 :                 if (!EQUAL(domain, "IMAGE_STRUCTURE"))
     333             :                 {
     334           2 :                     if (poRetDS->GetRasterBand(i + 1)->SetMetadata(
     335           1 :                             poSrcDS->GetRasterBand(i + 1)->GetMetadata(domain),
     336           2 :                             domain) != CE_None)
     337             :                     {
     338           0 :                         ReportError(
     339             :                             CE_Failure, CPLE_AppDefined,
     340             :                             "Cannot copy '%s' metadata domain for band %d",
     341             :                             domain, i + 1);
     342           0 :                         return false;
     343             :                     }
     344             :                 }
     345             :             }
     346             :         }
     347             :     }
     348             : 
     349          78 :     const CPLStringList aosMD(m_metadata);
     350          51 :     for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
     351             :     {
     352          12 :         if (poRetDS->SetMetadataItem(key, value) != CE_None)
     353             :         {
     354           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     355             :                         "SetMetadataItem('%s', '%s') failed", key, value);
     356           0 :             return false;
     357             :         }
     358             :     }
     359             : 
     360          39 :     if (m_copyOverviews && m_bandCount > 0)
     361             :     {
     362           3 :         if (!poSrcDS)
     363             :         {
     364           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     365             :                         "Argument 'copy-overviews' can only be set when an "
     366             :                         "input dataset is set");
     367           2 :             return false;
     368             :         }
     369           3 :         if (poSrcDS->GetRasterXSize() != poRetDS->GetRasterXSize() ||
     370           1 :             poSrcDS->GetRasterYSize() != poRetDS->GetRasterYSize())
     371             :         {
     372           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     373             :                         "Argument 'copy-overviews' can only be set when the "
     374             :                         "input and output datasets have the same dimension");
     375           1 :             return false;
     376             :         }
     377             :         const int nOverviewCount =
     378           1 :             poSrcDS->GetRasterBand(1)->GetOverviewCount();
     379           1 :         std::vector<int> anLevels;
     380           2 :         for (int i = 0; i < nOverviewCount; ++i)
     381             :         {
     382           1 :             const auto poOvrBand = poSrcDS->GetRasterBand(1)->GetOverview(i);
     383           1 :             const int nOvrFactor = GDALComputeOvFactor(
     384             :                 poOvrBand->GetXSize(), poSrcDS->GetRasterXSize(),
     385           1 :                 poOvrBand->GetYSize(), poSrcDS->GetRasterYSize());
     386           1 :             anLevels.push_back(nOvrFactor);
     387             :         }
     388           2 :         if (poRetDS->BuildOverviews(
     389           1 :                 "NONE", nOverviewCount, anLevels.data(),
     390             :                 /* nListBands = */ 0, /* panBandList = */ nullptr,
     391             :                 /* pfnProgress = */ nullptr, /* pProgressData = */ nullptr,
     392           1 :                 /* options = */ nullptr) != CE_None)
     393             :         {
     394           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     395             :                         "Creating overview(s) failed");
     396           0 :             return false;
     397             :         }
     398             :     }
     399             : 
     400          37 :     if (!m_burnValues.empty())
     401             :     {
     402          38 :         for (int i = 0; i < m_bandCount; ++i)
     403             :         {
     404          25 :             const int burnValueIdx = m_burnValues.size() == 1 ? 0 : i;
     405          25 :             const auto poDstBand = poRetDS->GetRasterBand(i + 1);
     406             :             // cppcheck-suppress negativeContainerIndex
     407          25 :             if (poDstBand->Fill(m_burnValues[burnValueIdx]) != CE_None)
     408             :             {
     409           0 :                 ReportError(CE_Failure, CPLE_AppDefined,
     410             :                             "Setting burn value failed");
     411           0 :                 return false;
     412             :             }
     413             :         }
     414          13 :         if (poRetDS->FlushCache(false) != CE_None)
     415             :         {
     416           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     417             :                         "Setting burn value failed");
     418           0 :             return false;
     419             :         }
     420             :     }
     421             : 
     422          37 :     m_outputDataset.Set(std::move(poRetDS));
     423             : 
     424          37 :     return true;
     425             : }
     426             : 
     427             : GDALRasterCreateAlgorithmStandalone::~GDALRasterCreateAlgorithmStandalone() =
     428             :     default;
     429             : 
     430             : //! @endcond

Generated by: LCOV version 1.14