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

Generated by: LCOV version 1.14